# Authentication Source: https://docs.atomscale.ai/api-reference/authentication Secure your API requests with API keys All API requests require authentication. The Atomscale API uses API key authentication via the `X-API-KEY` header. ## Obtaining an API Key 1. Log in to the [Atomscale Dashboard](https://app.atomscale.ai) 2. Click your name at the bottom of the sidebar, then select **Manage account** > **Access** 3. Enter a **Label (optional)** to identify the key, and choose an **Expiration** (see [Key Expiration](#key-expiration)) 4. Click **Generate API Key** 5. Copy and securely store the key. It is only shown once. Treat your API keys like passwords. Never commit them to version control or share them publicly. ## Using Your API Key Include your API key in the `X-API-KEY` header of every request: ```bash theme={null} X-API-KEY: YOUR_API_KEY ``` ## Code Examples ```python SDK theme={null} from atomscale import Client # The SDK handles authentication automatically client = Client(api_key="YOUR_API_KEY") ``` ```python Python theme={null} import requests headers = { "X-API-KEY": "YOUR_API_KEY" } response = requests.get( "https://api.atomscale.ai/data_entries/", headers=headers ) ``` ```bash cURL theme={null} curl https://api.atomscale.ai/data_entries/ \ -H "X-API-KEY: YOUR_API_KEY" ``` ## Environment Variables For security, store your API key in an environment variable rather than hardcoding it. The SDK reads `AS_API_KEY` automatically when you create a `Client()` without an `api_key` argument (it also accepts `ATOMSCALE_API_KEY` as a fallback): ```python Python theme={null} import os api_key = os.environ.get("AS_API_KEY") ``` ```bash Shell theme={null} export AS_API_KEY="your_api_key_here" ``` ## Key Permissions API keys do not have separate scopes. A request made with your key has the same permissions as your user account. ## Key Expiration * When you generate a key, you can set it to expire after 7, 30, 60, or 90 days, or never. The default is 90 days. * A banner at the top of the dashboard warns you when a key will expire within 14 days. * A key that goes unused for 90 days is revoked automatically, whatever expiration you chose. * Generating a new key immediately expires your previous key. Update any applications that use the old key. * Requests made with an expired or revoked key return `401 Unauthorized`. ## Revoking Keys If a key is compromised or no longer needed: 1. Open the **Access** page (click your name at the bottom of the sidebar, then select **Manage account** > **Access**) 2. Find the key and click **Revoke**, then click **Revoke** again to confirm 3. The key is immediately invalidated Revoking a key cannot be undone. Any applications using that key will stop working immediately. # Associate Data with Sample Source: https://docs.atomscale.ai/api-reference/data-catalogue/associate-sample POST /data_entries/physical_sample Attach data entries to a physical sample Associates one or more data entries with a physical sample. To detach the data entries from their current sample, pass `null` for `physical_sample_id`. UUIDs of the data entries to associate UUID of the physical sample to associate with the data entries. Pass `null` to detach the data entries from their current sample. ## Response Returns an empty response on success. ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/data_entries/physical_sample" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"], "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/data_entries/physical_sample", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"], "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab" } ) ``` ```json 200 theme={null} null ``` # Delete Data Entries Source: https://docs.atomscale.ai/api-reference/data-catalogue/delete DELETE /data_entries/ Delete one or more data entries from the catalogue Asynchronously soft-deletes data items and their derivative results from database tables, raw buckets, and processed buckets. Deleting a data entry removes all associated raw data, processed data, and analysis results. Array of data entry UUIDs to delete (passed as repeated query parameters) If `true`, permanently deletes the data. If `false`, performs a soft delete. ## Response Returns an array of UUIDs that were successfully deleted. ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/data_entries/?data_ids=d290f1ee-6c54-4b01-90e6-d701748f0851&data_ids=a1b2c3d4-5678-90ab-cdef-1234567890ab" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/data_entries/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={ "data_ids": [ "d290f1ee-6c54-4b01-90e6-d701748f0851", "a1b2c3d4-5678-90ab-cdef-1234567890ab" ] } ) deleted_ids = response.json() ``` ```json 200 theme={null} [ "d290f1ee-6c54-4b01-90e6-d701748f0851", "a1b2c3d4-5678-90ab-cdef-1234567890ab" ] ``` # Export Data Source: https://docs.atomscale.ai/api-reference/data-catalogue/export GET /data_entries/export/ Export one or more data entries Exports the specified data entries and returns a string you can use to retrieve the export (an export URL or token). UUIDs of the data entries to export ## Response Returns a string containing the export URL or token. ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/data_entries/export/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"]} ) export = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/data_entries/export/?data_ids=d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} "https://storage.atomscale.ai/exports/d290f1ee.zip?token=..." ``` # Get Processed Data Source: https://docs.atomscale.ai/api-reference/data-catalogue/get-processed GET /data_entries/processed_data/{data_id} Retrieve processed data for a data entry Returns processed data for a data entry. You must specify how you want the data returned using the `return_as` parameter. The UUID of the data entry How to return the data: `bytes` (raw binary), `url-download` (pre-signed download URL), or `url-embed` (pre-signed embeddable URL) Filter type: `video` to retrieve processed video, or `none` Set to `true` when retrieving processed data for a streaming session Optional suffix to append to the processed data file path ## Response When `return_as` is `url-download` or `url-embed`: Pre-signed URL for accessing the processed data file Filename of the processed data File format/extension Additional metadata about the processed data Returns `null` if no processed data is available yet. ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") client.download( data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851", dest_dir="processed/", data_type="processed", ) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/data_entries/processed_data/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"return_as": "url-download"} ) processed = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/data_entries/processed_data/d290f1ee-6c54-4b01-90e6-d701748f0851?return_as=url-download" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "url": "https://storage.atomscale.ai/processed/d290f1ee.json?token=...", "file_name": "rheed_results.json", "file_format": "json", "metadata": {} } ``` # Get Raw Data Source: https://docs.atomscale.ai/api-reference/data-catalogue/get-raw GET /data_entries/raw_data/{data_id} Retrieve raw data for a specific data entry Returns the raw, unprocessed data associated with a data entry. You must specify how you want the data returned using the `return_as` parameter. The UUID of the data entry How to return the data: `bytes` (raw binary), `url-download` (pre-signed download URL), or `url-embed` (pre-signed embeddable URL) ## Response When `return_as` is `url-download` or `url-embed`: Pre-signed URL for accessing the raw data file Original filename File format/extension When `return_as` is `bytes`, the response body contains the raw file binary data. ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") client.download( data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851", dest_dir="raw/", data_type="raw", ) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/data_entries/raw_data/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"return_as": "url-download"} ) raw_data = response.json() # Download the actual file file_response = requests.get(raw_data["url"]) ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/data_entries/raw_data/d290f1ee-6c54-4b01-90e6-d701748f0851?return_as=url-download" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "url": "https://storage.atomscale.ai/raw/d290f1ee.mp4?token=...", "file_name": "rheed_video_2024_01_15.mp4", "file_format": "mp4" } ``` # List Data Entries Source: https://docs.atomscale.ai/api-reference/data-catalogue/list GET /data_entries/ Retrieve data catalogue entries with filtering Returns data catalogue items for your organization with advanced filtering options. Filter by specific data entry UUIDs Filter by physical sample UUIDs Filter by characterization source type. Supported values: `rheed`, `rheed_image`, `rheed_xscan`, `xps`, `sem`, `tool_state`, `optical`, `raman`, `photoluminescence`, `recipe`, `ellipsometry`, `sims`, `oes`, `xrd`, `xrr`. The legacy RHEED names `rheed_stationary`, `rheed_rotating`, and `rheed_video` are still accepted and treated as `rheed`. Filter by pipeline status: `success`, `pending`, `error`, `running`, `quarantined`, `stream_active`, `stream_paused`, `stream_interrupted`, `stream_finalizing`, `stream_error`, `all` Search keywords to filter results Only return entries uploaded at or after this datetime (ISO 8601) Only return entries uploaded at or before this datetime (ISO 8601) Only return entries last updated at or after this datetime (ISO 8601) Only return entries last updated at or before this datetime (ISO 8601) Only return entries with a growth length of at least this many seconds Only return entries with a growth length of at most this many seconds If set, filter to tagged (`true`) or untagged (`false`) data Include data from the entire organization (not just your own) Maximum number of results to return (max 10000) Number of results to skip for pagination ## Response Returns an array of data catalogue objects. Key fields shown below (the full response includes additional metadata fields). Data entry UUID Original filename of the uploaded data Characterization source type (e.g., `rheed`, `xps`, `sem`) Processing pipeline status ISO 8601 timestamp of upload Associated physical sample UUID Name of the associated physical sample Tags associated with this data entry Projects this data entry belongs to ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.search(data_type="rheed", status="success") ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/data_entries/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"data_type": "rheed", "limit": 50} ) data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/data_entries/?data_type=rheed&limit=50" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "raw_name": "rheed_video_2024_01_15.mp4", "char_source_type": "rheed", "pipeline_status": "success", "upload_datetime": "2024-01-15T10:30:00Z", "last_accessed_datetime": "2024-01-15T14:00:00Z", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "physical_sample_name": "GaAs-001", "tags": [], "projects": [] } ] ``` # Get Processing Status Source: https://docs.atomscale.ai/api-reference/data-catalogue/status GET /pipelines/status/resource/{data_id} Poll the processing pipeline status for a data entry Returns the current processing pipeline status for a data entry. Use this to poll after an upload and track a data entry through processing. The UUID of the data entry ## Response Returns the workflow status object, or `null` if no processing workflow exists for the data entry. ID of the processing workflow Overall workflow status. One of: `PENDING`, `SUCCESS`, `ERROR`, `MAX_RECOVERY_ATTEMPTS_EXCEEDED`, `CANCELLED`, `ENQUEUED` Name of the current processing step, or `null` if not applicable Unix timestamp (seconds) when the current step started, or `null` if not applicable ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/pipelines/status/resource/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) status = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/pipelines/status/resource/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "workflow_id": "wf_9f8e7d6c", "workflow_status": "PENDING", "step_name": "extract_frames", "step_started_at": 1705315800 } ``` # Update Data Entry Source: https://docs.atomscale.ai/api-reference/data-catalogue/update PATCH /data_entries/{data_id} Update the metadata of a data catalogue entry Updates the metadata of an existing data catalogue entry. All body fields are optional, so you can update only the fields you need. The UUID of the data entry Characterization source type. Supported values: `rheed`, `rheed_image`, `rheed_xscan`, `xps`, `sem`, `tool_state`, `optical`, `raman`, `photoluminescence`, `recipe`, `ellipsometry`, `sims`, `oes`, `xrd`, `xrr`. The legacy RHEED names `rheed_stationary`, `rheed_rotating`, and `rheed_video` are still accepted and treated as `rheed`. Processing pipeline status. One of: `success`, `pending`, `error`, `running`, `quarantined`, `stream_active`, `stream_paused`, `stream_interrupted`, `stream_finalizing`, `stream_error` Original filename of the uploaded data ## Response Returns the full data catalogue object. Key fields are shown below (the full response includes additional metadata fields). Data entry UUID Original filename of the uploaded data Characterization source type (e.g., `rheed`, `xps`, `sem`) Processing pipeline status Associated physical sample UUID ISO 8601 timestamp of upload ISO 8601 timestamp of the last update Size of the raw file in gigabytes ID of the user who owns the entry ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/data_entries/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "char_source_type": "rheed", "pipeline_status": "success", "raw_name": "rheed_video_2024_01_15.mp4" }' ``` ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/data_entries/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "char_source_type": "rheed", "pipeline_status": "success", "raw_name": "rheed_video_2024_01_15.mp4" } ) entry = response.json() ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "raw_name": "rheed_video_2024_01_15.mp4", "char_source_type": "rheed", "pipeline_status": "success", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "upload_datetime": "2024-01-15T10:30:00Z", "last_updated": "2024-01-15T14:00:00Z", "raw_size_gb": 1.2, "user_id": "user_abc123", "version": "0.269.1" } ``` # API Reference Source: https://docs.atomscale.ai/api-reference/index Programmatic access to the Atomscale platform The Atomscale API provides programmatic access to your materials science data, analysis results, and platform features. Use it to integrate Atomscale into your existing workflows, automate data uploads, or build custom applications. ## Base URL All API requests should be made to: ``` https://api.atomscale.ai ``` ## Request Format The API accepts JSON-encoded request bodies and returns JSON responses. Include the `Content-Type` header for requests with a body: ```bash theme={null} Content-Type: application/json ``` ## Response Format All responses return JSON with consistent structure. Successful responses include the requested data directly, while errors follow a standard format: ```json theme={null} { "detail": "Error message describing what went wrong" } ``` ## HTTP Status Codes | Code | Description | | - | - | | 200 | Success | | 201 | Resource created | | 400 | Bad request - check your parameters | | 401 | Unauthorized - invalid or missing credentials | | 403 | Forbidden - insufficient permissions | | 404 | Resource not found | | 422 | Validation error - check field requirements | | 500 | Server error - contact support | ## Rate Limiting API requests are rate-limited to ensure platform stability. If you receive a `429` status code, wait before retrying. Contact support if you need higher limits for production workloads. ## Versioning The API is versioned through the URL path. The current version is `v1`, which is the default when no version is specified. Breaking changes will be introduced in new versions with advance notice. Learn how to authenticate your API requests # Create Synthesis Instrument Source: https://docs.atomscale.ai/api-reference/instruments/create POST /instruments/synthesis/ Register a new synthesis instrument Creates a new synthesis instrument record. Synthesis instruments represent the growth tools used to produce samples, such as MBE and sputter systems. Name of the instrument Label for the instrument Synthesis source type. One of `mbe`, `pvd`, `sputter`, `cvd`, `ald`, `pld`. Serial identifier of the instrument ## Response Synthesis source identifier Name of the instrument ID of the source make (manufacturer and model combination) Synthesis source type. One of `mbe`, `pvd`, `sputter`, `cvd`, `ald`, `pld`. Additional metadata about the instrument. Year the instrument was manufactured Serial identifier of the instrument Number of inputs on the instrument ID of the user who owns the instrument ISO 8601 timestamp of last update ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/instruments/synthesis/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "MBE Chamber A", "label": "MBE-A", "type": "mbe" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/instruments/synthesis/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "MBE Chamber A", "label": "MBE-A", "type": "mbe" } ) instrument = response.json() ``` ```json 200 theme={null} { "synth_source_id": 42, "source_name": "MBE Chamber A", "source_makes_id": 3, "synth_source_type": "mbe", "source_metadata": { "manufacture_year": null, "serial_identifier": null, "inputs": null }, "user_id": "user_abc123", "last_updated": "2024-01-15T11:00:00Z" } ``` # Delete Synthesis Instruments Source: https://docs.atomscale.ai/api-reference/instruments/delete DELETE /instruments/synthesis/ Delete one or more synthesis instruments Permanently removes synthesis instrument records. Array of synthesis instrument IDs to delete (passed as repeated query parameters) ## Response Returns an empty response on success. ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/instruments/synthesis/?synthesis_instrument_ids=7&synthesis_instrument_ids=8" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/instruments/synthesis/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={ "synthesis_instrument_ids": [7, 8] } ) ``` ```json 200 theme={null} null ``` # Get Synthesis Instrument Source: https://docs.atomscale.ai/api-reference/instruments/get GET /instruments/synthesis/{synthesis_instrument_id} Retrieve a single synthesis instrument by ID Returns a single synthesis instrument by its ID. ID of the synthesis instrument to retrieve ## Response Synthesis source identifier Name of the instrument ID of the source make (manufacturer and model combination) Synthesis source type. One of `mbe`, `pvd`, `sputter`, `cvd`, `ald`, `pld`. Additional metadata about the instrument. Year the instrument was manufactured Serial identifier of the instrument Number of inputs on the instrument ID of the user who owns the instrument ISO 8601 timestamp of last update ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/instruments/synthesis/7", headers={"X-API-KEY": "YOUR_API_KEY"} ) instrument = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/instruments/synthesis/7" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "synth_source_id": 42, "source_name": "MBE Chamber A", "source_makes_id": 3, "synth_source_type": "mbe", "source_metadata": { "manufacture_year": 2021, "serial_identifier": "GEN10-00421", "inputs": 8 }, "user_id": "user_abc123", "last_updated": "2024-01-15T10:00:00Z" } ``` # List Synthesis Instruments Source: https://docs.atomscale.ai/api-reference/instruments/list GET /instruments/synthesis/ Retrieve synthesis instruments in your organization Returns the synthesis instruments registered in your organization. Synthesis instruments represent the growth tools used to produce samples, such as MBE and sputter systems. ## Response Returns an array of synthesis instrument objects. Synthesis instrument ID Name or label of the instrument Manufacturer of the instrument Model of the instrument ID of the source make (manufacturer and model combination) Synthesis source type. One of `mbe`, `pvd`, `sputter`, `cvd`, `ald`, `pld`. Synthesis source identifier Additional metadata about the instrument. Year the instrument was manufactured Serial identifier of the instrument Number of inputs on the instrument Content of the linked detail note ISO 8601 timestamp of when the detail note was last updated ID of the user who owns the instrument Display name of the instrument owner ISO 8601 timestamp of last update ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/instruments/synthesis/", headers={"X-API-KEY": "YOUR_API_KEY"} ) instruments = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/instruments/synthesis/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": 7, "source_name": "MBE Chamber A", "source_manufacturer": "Veeco", "source_model": "GEN10", "source_makes_id": 3, "synth_source_type": "mbe", "synth_source_id": 42, "source_metadata": { "manufacture_year": 2021, "serial_identifier": "GEN10-00421", "inputs": 8 }, "detail_note_content": null, "detail_note_last_updated": null, "user_id": "user_abc123", "user_name": "Jane Smith", "last_updated": "2024-01-15T10:00:00Z" } ] ``` # Update Synthesis Instrument Source: https://docs.atomscale.ai/api-reference/instruments/update PATCH /instruments/synthesis/{synthesis_instrument_id} Update an existing synthesis instrument Updates an existing synthesis instrument. Include only the fields you want to change. ID of the synthesis instrument to update Name of the instrument Label for the instrument Synthesis source type. One of `mbe`, `pvd`, `sputter`, `cvd`, `ald`, `pld`. Serial identifier of the instrument ## Response Synthesis source identifier Name of the instrument ID of the source make (manufacturer and model combination) Synthesis source type. One of `mbe`, `pvd`, `sputter`, `cvd`, `ald`, `pld`. Additional metadata about the instrument. Year the instrument was manufactured Serial identifier of the instrument Number of inputs on the instrument ID of the user who owns the instrument ISO 8601 timestamp of last update ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/instruments/synthesis/7" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "MBE Chamber B" }' ``` ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/instruments/synthesis/7", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "MBE Chamber B" } ) instrument = response.json() ``` ```json 200 theme={null} { "synth_source_id": 42, "source_name": "MBE Chamber B", "source_makes_id": 3, "synth_source_type": "mbe", "source_metadata": { "manufacture_year": 2021, "serial_identifier": "GEN10-00421", "inputs": 8 }, "user_id": "user_abc123", "last_updated": "2024-01-15T12:00:00Z" } ``` # Get OES Data Source: https://docs.atomscale.ai/api-reference/oes/get GET /oes/{data_id} Retrieve optical emission spectroscopy data and analysis Returns the most recent processed optical emission spectroscopy (OES) result for a data entry, including the measured spectrum, reference and dark spectra, and any detected emission peaks. The data entry UUID for the OES measurement ## Response Data entry UUID OES results identifier Wavelength values for the spectrum Unit for the `wavelengths` values (e.g., `nm`) Measured sample spectrum intensities Reference spectrum intensities (if captured) Dark spectrum intensities used for background correction (if captured) Background-corrected spectrum intensities The type of spectrum represented by this result Detected emission peaks in the spectrum Element constraint mode applied during peak assignment: `auto`, `include`, or `exclude` Elements constrained during peak assignment (when `constraint_mode` is `include` or `exclude`) Additional metadata about the measurement ISO 8601 timestamp of last update ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") oes = results[0] # OESResult print(oes.wavelengths) print(oes.detected_peaks) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/oes/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) oes_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/oes/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "id": "oes_001", "wavelengths": [400.0, 400.5, 401.0, 401.5], "wavelength_unit": "nm", "sample": [1200, 1350, 4100, 1280], "reference": [1100, 1150, 1180, 1120], "dark": [50, 52, 51, 49], "scope_corrected": [1150, 1298, 4049, 1231], "spectrum_type": "sample", "detected_peaks": [ {"peak_id": "p1", "wavelength_nm": 401.0, "element": "Ga"} ], "constraint_mode": "auto", "constrained_elements": null, "spectral_metadata": {}, "last_updated": "2024-01-15T10:35:00Z" } ``` # Get All OES Results Source: https://docs.atomscale.ai/api-reference/oes/get-all GET /oes/{data_id}/all Retrieve all OES spectra for a data entry Returns every processed OES result associated with a data entry. Use this for multi-spectrum captures (for example, NetCDF files that hold several spectra), where `GET /oes/{data_id}` returns only the latest result. The data entry UUID for the OES measurement Maximum number of results to return (1–1000) Number of results to skip for pagination ## Response Returns an array of OES result objects, ordered oldest to newest. Each object has the same shape as the [Get OES Data](/api-reference/oes/get) response. ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/oes/d290f1ee-6c54-4b01-90e6-d701748f0851/all", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"limit": 100, "offset": 0} ) oes_results = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/oes/d290f1ee-6c54-4b01-90e6-d701748f0851/all?limit=100" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "id": "oes_001", "wavelengths": [400.0, 400.5, 401.0, 401.5], "wavelength_unit": "nm", "sample": [1200, 1350, 4100, 1280], "reference": [1100, 1150, 1180, 1120], "dark": [50, 52, 51, 49], "scope_corrected": [1150, 1298, 4049, 1231], "spectrum_type": "sample", "detected_peaks": [ {"peak_id": "p1", "wavelength_nm": 401.0, "element": "Ga"} ], "constraint_mode": "auto", "constrained_elements": null, "spectral_metadata": {}, "last_updated": "2024-01-15T10:35:00Z" } ] ``` # Update OES Peak Assignment Source: https://docs.atomscale.ai/api-reference/oes/update-peaks PATCH /oes/{data_id}/peaks Manually reassign an OES emission peak to a candidate element Applies a manual peak assignment override to the latest OES result. Each detected peak carries a set of candidate element assignments; this endpoint selects one of those candidates for a given peak and persists the override so it survives reprocessing. The data entry UUID for the OES measurement The `peak_id` of the detected peak to reassign The candidate assignment to apply to the peak. Must be one of the candidate keys available on that peak. ## Response Returns the updated OES result with the new assignment applied. The response has the same shape as the [Get OES Data](/api-reference/oes/get) response. ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/oes/d290f1ee-6c54-4b01-90e6-d701748f0851/peaks", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "peak_id": "p1", "candidate_key": "Ga_417.2" } ) oes_data = response.json() ``` ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/oes/d290f1ee-6c54-4b01-90e6-d701748f0851/peaks" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "peak_id": "p1", "candidate_key": "Ga_417.2" }' ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "id": "oes_001", "wavelengths": [400.0, 400.5, 401.0, 401.5], "wavelength_unit": "nm", "sample": [1200, 1350, 4100, 1280], "scope_corrected": [1150, 1298, 4049, 1231], "spectrum_type": "sample", "detected_peaks": [ {"peak_id": "p1", "wavelength_nm": 401.0, "element": "Ga", "manual": true} ], "constraint_mode": "auto", "constrained_elements": null, "last_updated": "2024-01-15T10:40:00Z" } ``` # Get Ellipsometry Timeseries Source: https://docs.atomscale.ai/api-reference/optical/ellipsometry-timeseries GET /ellipsometry/{data_id}/timeseries/ Retrieve ellipsometry measurement timeseries data Returns processed ellipsometry timeseries data. Data is organized by property name (for example, film thickness or refractive index), where each property has its own arrays of timestamps and values sharing a common time axis. The data entry UUID for the ellipsometry measurement If `true`, returns `null` instead of an error when no timeseries data exists for this entry Return only the last N samples per property (useful for incremental fetching during live monitoring) Only return data points after this elapsed time (seconds). Useful for incremental fetching during live monitoring. If set, only these property columns are returned. Omit to return all properties. If `true`, skip the data and return a metadata-only response listing the available properties ## Response Mapping of property names to their timeseries data. Each key is a property name (e.g., `thickness`, `refractive_index`). Array of time values relative to the start of the measurement (seconds) Array of Unix timestamps in milliseconds Array of measurement values Measurement unit for the values (e.g., `nm`) Convenience mapping of property name to its unit All property names recorded for this data entry, regardless of any `property_names` filter applied to the response Maximum `relative_time_seconds` value across all properties ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") ellipsometry = results[0] # EllipsometryResult print(ellipsometry.timeseries_data) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/ellipsometry/d290f1ee-6c54-4b01-90e6-d701748f0851/timeseries/", headers={"X-API-KEY": "YOUR_API_KEY"} ) ellipsometry_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/ellipsometry/d290f1ee-6c54-4b01-90e6-d701748f0851/timeseries/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "properties": { "thickness": { "relative_time_seconds": [0, 10, 20, 30, 40], "unix_timestamp_ms": [1705312200000, 1705312210000, 1705312220000, 1705312230000, 1705312240000], "values": [0, 4.9, 10.2, 15.0, 20.1], "units": "nm" }, "refractive_index": { "relative_time_seconds": [0, 10, 20, 30, 40], "unix_timestamp_ms": [1705312200000, 1705312210000, 1705312220000, 1705312230000, 1705312240000], "values": [2.01, 2.02, 2.02, 2.03, 2.02], "units": null } }, "property_units": { "thickness": "nm", "refractive_index": null }, "available_property_names": ["thickness", "refractive_index"], "series_max_time": 40.0 } ``` # Get Optical Video Frames Source: https://docs.atomscale.ai/api-reference/optical/frames GET /optical/frame/video_single_frames/{data_id} Retrieve individual frames from optical video recordings Returns saved frames from an optical video recording, including frame image UUIDs and file metadata. The data entry UUID for the optical video ## Response UUID of the video Saved video frames UUID of the frame image File metadata for the processed frame Image file type (e.g., `png`) Frame width in pixels Frame height in pixels ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") optical = results[0] # OpticalResult print(optical.snapshot_image_data) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/optical/frame/video_single_frames/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) frames_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/optical/frame/video_single_frames/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "video_uuid": "d290f1ee-6c54-4b01-90e6-d701748f0851", "frames": [ { "image_uuid": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "processed_file_metadata": { "file_type": "png", "width": 1920, "height": 1080 } } ] } ``` # Get Optical Timeseries Source: https://docs.atomscale.ai/api-reference/optical/timeseries GET /optical/timeseries/{data_id}/ Retrieve optical monitoring timeseries data Returns processed timeseries data from optical image analysis during growth. Each data point corresponds to a video frame and may include computed morphology metrics such as perimeter, circularity, and edge roughness. The data entry UUID for the optical measurement If `true`, returns `null` instead of an error when no timeseries data exists for this entry If set, only these property columns are returned. Omit to return all properties. If `true`, skip the data and return a metadata-only response listing the available properties ## Response Array of per-frame measurement data points Time relative to the start of the recording (seconds) Unix timestamp in milliseconds Frame index in the video Detected perimeter in pixels (if available) Circularity metric (if available) Edge roughness metric (if available) Hausdorff distance in pixels (if available) ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") optical = results[0] # OpticalResult print(optical.timeseries_data) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/optical/timeseries/d290f1ee-6c54-4b01-90e6-d701748f0851/", headers={"X-API-KEY": "YOUR_API_KEY"} ) optical_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/optical/timeseries/d290f1ee-6c54-4b01-90e6-d701748f0851/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "series": [ { "relative_time_seconds": 0.0, "unix_timestamp_ms": 1705312200000, "frame_number": 0, "perimeter_px": 245.3, "circularity": 0.87, "edge_roughness": 0.12, "hausdorff_px": 3.5 }, { "relative_time_seconds": 1.0, "unix_timestamp_ms": 1705312201000, "frame_number": 30, "perimeter_px": 248.1, "circularity": 0.85, "edge_roughness": 0.14, "hausdorff_px": 3.8 } ] } ``` # Create Project Source: https://docs.atomscale.ai/api-reference/projects/create POST /projects/ Create a new project in your workspace Creates a new project for organizing samples and data. Projects serve as the top-level container for related experiments. Project name (must be non-empty) Notes or description for the project ## Response Returns the UUID of the newly created project. ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/projects/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "InGaN LED Development", "notes": "Blue LED optimization project for Q1 2024" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/projects/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "InGaN LED Development", "notes": "Blue LED optimization project for Q1 2024" } ) project_id = response.json() ``` ```json 200 theme={null} "d290f1ee-6c54-4b01-90e6-d701748f0851" ``` # Delete Project Source: https://docs.atomscale.ai/api-reference/projects/delete DELETE /projects/{project_id} Delete a project Permanently removes a project. Deleting a project cannot be undone. UUID of the project to delete ## Response Returns the UUID of the deleted project. ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) project_id = response.json() ``` ```json 200 theme={null} "d290f1ee-6c54-4b01-90e6-d701748f0851" ``` # Get Project Samples Source: https://docs.atomscale.ai/api-reference/projects/get-samples GET /projects/{project_id}/physical_samples List all physical samples associated with a project Returns all physical samples linked to a specific project. The UUID of the project ## Response Returns an array of physical sample objects with their ID and name. Physical sample UUID Sample name or label ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") result = client.get_project(project_id="d290f1ee-6c54-4b01-90e6-d701748f0851") for sample in result.samples: print(sample.physical_sample_name) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851/physical_samples", headers={"X-API-KEY": "YOUR_API_KEY"} ) samples = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851/physical_samples" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "name": "GaAs-001" }, { "id": "b2c3d4e5-6789-01bc-defg-2345678901bc", "name": "GaAs-002" } ] ``` # Get Project Last Updated Source: https://docs.atomscale.ai/api-reference/projects/last-updated GET /projects/{project_id}/last_updated Get the most recent update time for a project Returns the timestamp of the most recent update across the project, including its samples and data. UUID of the project ## Response Returns an ISO 8601 timestamp string. ```bash cURL theme={null} curl "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851/last_updated" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851/last_updated", headers={"X-API-KEY": "YOUR_API_KEY"} ) last_updated = response.json() ``` ```json 200 theme={null} "2024-01-15T14:30:00Z" ``` # List Projects Source: https://docs.atomscale.ai/api-reference/projects/list GET /projects/ Retrieve all projects in your organization Returns all projects associated with your organization. ## Response Returns an array of project objects. Unique project identifier (UUID) Project name ISO 8601 timestamp of last update Number of physical samples in the project User ID of the project owner Display name of the project owner Project notes content ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") projects = client.list_projects() ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/projects/", headers={"X-API-KEY": "YOUR_API_KEY"} ) projects = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/projects/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "name": "GaAs MBE Development", "last_updated": "2024-01-15T14:30:00Z", "physical_sample_count": 12, "owner_id": "user_abc123", "owner_name": "Jane Smith", "detail_note": null } ] ``` # Update Project Source: https://docs.atomscale.ai/api-reference/projects/update PATCH /projects/{project_id} Update an existing project Updates an existing project. Only the fields you include in the request body are changed. UUID of the project to update New project name ## Response Returns the UUID of the updated project. ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "InGaN LED Development (2024)" }' ``` ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "InGaN LED Development (2024)" } ) project_id = response.json() ``` ```json 200 theme={null} "d290f1ee-6c54-4b01-90e6-d701748f0851" ``` # Update Project Samples Source: https://docs.atomscale.ai/api-reference/projects/update-samples POST /projects/{project_id}/physical_samples Update the physical samples associated with a project Updates the association between physical samples and a project. The UUID of the project Array of physical sample UUIDs to associate with the project If `true`, replaces all existing sample associations with the provided list. If `false`, adds the provided samples to the existing associations. ## Response Returns an array of the physical sample UUIDs now associated with the project. ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851/physical_samples" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "physical_sample_ids": [ "a1b2c3d4-5678-90ab-cdef-1234567890ab", "b2c3d4e5-6789-01bc-defg-2345678901bc" ], "set": false }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/projects/d290f1ee-6c54-4b01-90e6-d701748f0851/physical_samples", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "physical_sample_ids": [ "a1b2c3d4-5678-90ab-cdef-1234567890ab", "b2c3d4e5-6789-01bc-defg-2345678901bc" ], "set": False } ) result = response.json() ``` ```json 200 theme={null} [ "a1b2c3d4-5678-90ab-cdef-1234567890ab", "b2c3d4e5-6789-01bc-defg-2345678901bc" ] ``` # Compare RHEED Images Source: https://docs.atomscale.ai/api-reference/rheed/compare GET /rheed/images/compare/ Compare two RHEED images to measure changes in spot count, strain, and mask Compares two RHEED images and reports how the diffraction pattern changed between them, including the difference in spot count, the referenced strain, and the mask regions gained or lost. The data entry UUID for the first (initial) RHEED image The data entry UUID for the second (final) RHEED image ## Response Data entry UUID for the first image Data entry UUID for the second image Strain of the second image referenced against the first Change in the number of detected diffraction spots between the two images. Spot count in the first image Spot count in the second image Percent change in spot count from the first image to the second Mask region present in the second image but not the first, encoded using run-length encoding (RLE). Null if no region was gained. Run-length encoded mask data Height of the mask in pixels Width of the mask in pixels Mask region present in the first image but not the second, encoded using run-length encoding (RLE). Null if no region was lost. Run-length encoded mask data Height of the mask in pixels Width of the mask in pixels ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/images/compare/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={ "data_id_1": "d290f1ee-6c54-4b01-90e6-d701748f0851", "data_id_2": "a1b2c3d4-5678-90ab-cdef-1234567890ab" } ) comparison = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/images/compare/?data_id_1=d290f1ee-6c54-4b01-90e6-d701748f0851&data_id_2=a1b2c3d4-5678-90ab-cdef-1234567890ab" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id_1": "d290f1ee-6c54-4b01-90e6-d701748f0851", "data_id_2": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "referenced_strain": 0.42, "spot_count": { "initial": 12, "final": 18, "percent_change": 50.0 }, "mask_gained": { "mask_rle": "0 100 1 50 0 200 ...", "mask_height": 512, "mask_width": 512 }, "mask_lost": null } ``` # End RHEED Stream Source: https://docs.atomscale.ai/api-reference/rheed/end-stream POST /rheed/stream/{data_id}/end End a RHEED streaming session and finalize processing Ends an active RHEED streaming session and triggers final processing of all received frames. The data entry UUID for the streaming session Force the stream to finalize with a success status, even if processing encountered issues ## Response Returns the final pipeline status as a string. Possible values: `success`, `pending`, `error`, `running`, `quarantined`, `stream_finalizing`, `stream_error` ```python SDK theme={null} from atomscale.streaming import RHEEDStreamer streamer = RHEEDStreamer(api_key="YOUR_API_KEY") streamer.finalize("d290f1ee-6c54-4b01-90e6-d701748f0851") ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/end", headers={"X-API-KEY": "YOUR_API_KEY"} ) status = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/end" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} "success" ``` # Get RHEED Fingerprint Source: https://docs.atomscale.ai/api-reference/rheed/fingerprint GET /rheed/images/{data_id}/fingerprint Retrieve the RHEED pattern fingerprint as a serialized graph Returns a computed fingerprint of the RHEED diffraction pattern as a serialized graph object. The fingerprint characterizes the surface structure by identifying nodes (diffraction features) and their spatial relationships. The data entry UUID for the RHEED image ## Response Data entry UUID Processed data entry UUID Serialized fingerprint graph Detected diffraction feature nodes in the pattern Horizontal distances between detected nodes ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") image = results[0] # RHEEDImageResult df = image.get_pattern_dataframe() ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/images/d290f1ee-6c54-4b01-90e6-d701748f0851/fingerprint", headers={"X-API-KEY": "YOUR_API_KEY"} ) fingerprint = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/images/d290f1ee-6c54-4b01-90e6-d701748f0851/fingerprint" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "processed_data_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "fingerprint": { "nodes": [...], "horizontal_distances": [...] } } ``` # Get RHEED Frame Masks Source: https://docs.atomscale.ai/api-reference/rheed/frame-masks GET /rheed/images/{data_id}/frame_masks Retrieve per-frame RLE-encoded masks for a processed RHEED video Returns the binary segmentation mask for each featurized frame of a processed RHEED video, encoded using run-length encoding (RLE) in the same COCO format as the [single-image mask](/api-reference/rheed/mask). `frame_number` is the absolute frame index, keyed identically to the processed video frames and to the `Frame Number` axis of the RHEED timeseries, so a decoded mask overlays the matching frame of the processed video. Coverage is sparse. Masks exist only for featurized frames: every frame for stationary videos, but only the sampled subset for rotating and per-azimuth videos. Frames without a mask are absent from the response rather than returned as null. The data entry UUID of the RHEED video, the same ID used for the video and timeseries First absolute frame number to return, inclusive Last absolute frame number to return, inclusive. Omit to return every featurized frame from `from` onward ## Response Returns an array of mask records. Data entry UUID Processed data entry UUID Absolute frame index within the video Run-length encoded mask data, column-major (Fortran order) Height of the mask in pixels Width of the mask in pixels A `404` means the video has no per-frame mask artifact, either because the item is not RHEED or because it was processed before per-frame masks were persisted. ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") # Decoded arrays keyed by absolute frame number masks = client.get_frame_masks( "d290f1ee-6c54-4b01-90e6-d701748f0851", from_frame=0, to_frame=200, decode=True, ) print(masks[0].shape) # (H, W) uint8, values 0/1 ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/images/d290f1ee-6c54-4b01-90e6-d701748f0851/frame_masks", params={"from": 0, "to": 200}, headers={"X-API-KEY": "YOUR_API_KEY"}, ) rows = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/images/d290f1ee-6c54-4b01-90e6-d701748f0851/frame_masks?from=0&to=200" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "processed_data_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "frame_number": 0, "mask_rle": "PZ`0c0X # Get RHEED Mask Source: https://docs.atomscale.ai/api-reference/rheed/mask GET /rheed/images/{data_id}/mask Retrieve the RLE-encoded mask for a RHEED image Returns the automatically detected mask for a RHEED image, encoded using COCO run-length encoding (RLE). The mask identifies relevant diffraction regions in the RHEED pattern. For the per-frame masks of a processed RHEED video, see [Get RHEED Frame Masks](/api-reference/rheed/frame-masks). The data entry UUID for the RHEED image ## Response Data entry UUID Processed data entry UUID Run-length encoded mask data, column-major (Fortran order). Decode it with `atomscale.results.decode_mask_rle()` or `pycocotools.mask.decode()` Height of the mask in pixels Width of the mask in pixels ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") image = results[0] # RHEEDImageResult mask = image.mask # numpy array ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/images/d290f1ee-6c54-4b01-90e6-d701748f0851/mask", headers={"X-API-KEY": "YOUR_API_KEY"} ) mask = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/images/d290f1ee-6c54-4b01-90e6-d701748f0851/mask" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "processed_data_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "mask_rle": "PZ`0c0X # Get RHEED Overlay Source: https://docs.atomscale.ai/api-reference/rheed/overlay GET /rheed/images/{data_id}/overlay Download the RHEED image with the detected mask overlaid Returns the RHEED image rendered with the detected mask overlaid. The response is a ZIP archive (`application/x-zip-compressed`) containing a single PNG named `{data_id}.png`, rather than a JSON body. The data entry UUID for the RHEED image ## Response A ZIP archive containing the overlay image as a PNG. Write the response content to a file and unzip it to access the image. ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/images/d290f1ee-6c54-4b01-90e6-d701748f0851/overlay", headers={"X-API-KEY": "YOUR_API_KEY"} ) with open("overlay.zip", "wb") as f: f.write(response.content) ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/images/d290f1ee-6c54-4b01-90e6-d701748f0851/overlay" \ -H "X-API-KEY: YOUR_API_KEY" \ --output overlay.zip ``` # Pause RHEED Stream Source: https://docs.atomscale.ai/api-reference/rheed/pause-stream POST /rheed/stream/{data_id}/pause Pause an active RHEED streaming session Pauses an active RHEED streaming session. Resume the session later with the [resume endpoint](/api-reference/rheed/resume-stream). The data entry UUID for the streaming session ## Response Returns the data entry UUID as a string. ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/pause", headers={"X-API-KEY": "YOUR_API_KEY"} ) data_id = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/pause" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} "d290f1ee-6c54-4b01-90e6-d701748f0851" ``` # Get RHEED Processed Metadata Source: https://docs.atomscale.ai/api-reference/rheed/processed-metadata GET /rheed/processed_metadata Retrieve frame rate, total frames, and analysis window bounds for RHEED videos Retrieves processed metadata for one or more RHEED videos, including the frame rate, total frame count, and the frame bounds of the analysis window. The data entry UUIDs to retrieve metadata for ## Response Returns an array of objects, one per requested `data_id`. Data entry UUID Frame rate of the video in frames per second Total number of frames in the video Start frame of the inner analysis window. Null if not set. End frame of the inner analysis window. Null if not set. Start frame of the outer analysis window. Null if not set. End frame of the outer analysis window. Null if not set. ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/processed_metadata", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"]} ) metadata = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/processed_metadata?data_ids=d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "fps": 30.0, "total_frames": 9000, "inner_start": 1200, "inner_end": 7800, "outer_start": 0, "outer_end": 9000 } ] ``` # Resume RHEED Stream Source: https://docs.atomscale.ai/api-reference/rheed/resume-stream POST /rheed/stream/{data_id}/resume Resume a paused RHEED streaming session Resumes a paused RHEED streaming session that was paused with the [pause endpoint](/api-reference/rheed/pause-stream). The data entry UUID for the streaming session ## Response Returns the data entry UUID as a string. ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/resume", headers={"X-API-KEY": "YOUR_API_KEY"} ) data_id = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/resume" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} "d290f1ee-6c54-4b01-90e6-d701748f0851" ``` # Start RHEED Stream Source: https://docs.atomscale.ai/api-reference/rheed/start-stream POST /rheed/stream/ Start a new RHEED streaming session Starts a new RHEED streaming session by creating a streaming data item for live analysis during growth. Name for the streaming session Frame capture rate in frames per second (must be greater than 0, max 120) Project to associate the stream with Physical sample being monitored If set, creates a new physical sample with this name and associates it with the stream Rotational period in seconds (for rotating samples) Rotations per minute (for rotating samples) If `true`, return an object with both `data_id` and `physical_sample_id` instead of the bare data entry UUID. Useful when `create_physical_sample_name` is set and you need the ID of the newly created sample. ## Response By default, returns the UUID of the newly created streaming data entry. When `return_physical_sample=true`, returns an object with `data_id` and `physical_sample_id` instead. ```python SDK theme={null} from atomscale.streaming import RHEEDStreamer streamer = RHEEDStreamer(api_key="YOUR_API_KEY") data_id = streamer.initialize( fps=30.0, rotations_per_min=0.0, chunk_size=60, stream_name="MBE Run 2024-01-15 AM", physical_sample="GaN-001", project_id="d290f1ee-6c54-4b01-90e6-d701748f0851", ) ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/rheed/stream/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "data_item_name": "MBE Run 2024-01-15 AM", "fps_capture_rate": 30, "project_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab" } ) data_id = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/rheed/stream/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "data_item_name": "MBE Run 2024-01-15 AM", "fps_capture_rate": 30, "project_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab" }' ``` ```json 200 theme={null} "d290f1ee-6c54-4b01-90e6-d701748f0851" ``` # Get RHEED Stream Latency Source: https://docs.atomscale.ai/api-reference/rheed/stream-latency GET /rheed/stream/{data_id}/latency Retrieve the real-time latency of a RHEED stream Returns the real-time latency of a RHEED stream in seconds. Returns `null` if the stream is not active or has no results yet. The data entry UUID for the streaming session ## Response The latency in seconds as a number, or `null` if the stream is not active or has no results yet. ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/latency", headers={"X-API-KEY": "YOUR_API_KEY"} ) latency = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/latency" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} 0.85 ``` # Get RHEED Stream Settings Source: https://docs.atomscale.ai/api-reference/rheed/stream-settings GET /rheed/stream/{data_id} Retrieve the configuration for a RHEED streaming session Retrieves the settings that a RHEED streaming session was created with, such as its capture rate and any associated project or physical sample. The data entry UUID for the streaming session ## Response Name of the streaming session Frame capture rate of the recording source in frames per second Project associated with the stream Physical sample associated with the stream Name of the physical sample to create if it does not exist. Only used when `physical_sample_id` is not provided. For rotating samples, the period in frames per rotation For rotating samples, the number of rotations per minute ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) settings = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_item_name": "MBE Run 2024-01-15 AM", "fps_capture_rate": 30.0, "project_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "create_physical_sample_name": null, "rotational_period": null, "rotations_per_min": null } ``` # Get RHEED Stream Total Frames Source: https://docs.atomscale.ai/api-reference/rheed/stream-total-frames POST /rheed/stream/{data_id}/total_frames Retrieve the total number of frames the client attempted to send for a RHEED stream Returns the total number of frames the client attempted to send for a RHEED stream. This endpoint uses `POST` even though it only reads data. The data entry UUID for the streaming session ## Response Returns the total number of frames attempted as an integer. ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/total_frames", headers={"X-API-KEY": "YOUR_API_KEY"} ) total_frames = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/total_frames" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} 9000 ``` # Get RHEED Stream Video URLs Source: https://docs.atomscale.ai/api-reference/rheed/stream-video-urls GET /rheed/stream/{data_id}/video_urls Retrieve presigned URLs for the clustered video of a RHEED stream Retrieves presigned URLs for the clustered video produced from a RHEED stream and its metadata. Returns `null` if no video exists yet. The data entry UUID for the streaming session ## Response Presigned GET URL for the clustered video Presigned HEAD URL for the video metadata ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/video_urls", headers={"X-API-KEY": "YOUR_API_KEY"} ) urls = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/video_urls" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "video_url": "https://storage.atomscale.ai/clustered/d290f1ee-6c54-4b01-90e6-d701748f0851.mp4?...", "metadata_url": "https://storage.atomscale.ai/clustered/d290f1ee-6c54-4b01-90e6-d701748f0851.json?..." } ``` # Warm Up RHEED Stream Worker Source: https://docs.atomscale.ai/api-reference/rheed/stream-warmup POST /rheed/stream/warmup Check whether a RHEED processing worker is ready and trigger a warm-up if not Checks whether a RHEED processing worker is ready. Returns `true` if a worker is ready, or `false` otherwise (in which case a warm-up is triggered). Call this before starting a stream to reduce cold-start latency. ## Response Returns a boolean: `true` if a processing worker is ready, `false` otherwise. ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/rheed/stream/warmup", headers={"X-API-KEY": "YOUR_API_KEY"} ) ready = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/rheed/stream/warmup" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} true ``` # Get RHEED Timeseries Source: https://docs.atomscale.ai/api-reference/rheed/timeseries GET /rheed/timeseries/{data_id}/ Retrieve processed RHEED timeseries data Returns processed RHEED results data for plotting timeseries information, including intensity series organized by azimuthal angle. The data entry UUID for the RHEED recording Return `null` instead of an error if no processed data exists yet Return only the last N data points (useful for incremental fetching during streams) Return only data points from the last N seconds (useful for incremental fetching during streams) If set, only these property columns are returned. Omit to return all properties. If `true`, skip the data and return a metadata-only response listing the available properties ## Response ID of the calibration used for processing Timeseries data organized by azimuthal angle, each containing fingerprint series data Lattice spacing multiple values derived from the diffraction pattern Region definitions used for intensity extraction Maximum timestamp in the timeseries (seconds) ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") video = results[0] # RHEEDVideoResult print(video.timeseries_data) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/timeseries/d290f1ee-6c54-4b01-90e6-d701748f0851/", headers={"X-API-KEY": "YOUR_API_KEY"} ) rheed_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/timeseries/d290f1ee-6c54-4b01-90e6-d701748f0851/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "calibration_id": "cal_001", "series_by_angle": [ { "angle": 0, "series": [...] } ], "lattice_spacing_multiples": {}, "regions": {}, "series_max_time": 1800.5 } ``` # Get RHEED Timeseries Batch Source: https://docs.atomscale.ai/api-reference/rheed/timeseries-batch GET /rheed/timeseries/batch/ Retrieve RHEED timeseries for multiple data entries in a single request Retrieves RHEED timeseries for multiple data entries at once. Each entry in the response mirrors the payload returned by the single-item `/rheed/timeseries` endpoint. The data entry UUIDs to retrieve timeseries for Restrict the results to these property names. If omitted, all available properties are returned. If `true`, return only the list of available property names for each entry rather than the full timeseries values ## Response A map from `data_id` to that entry's timeseries. Each value is a RHEED timeseries payload matching the single-item `/rheed/timeseries` endpoint. ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/rheed/timeseries/batch/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={ "data_ids": [ "d290f1ee-6c54-4b01-90e6-d701748f0851", "a1b2c3d4-5678-90ab-cdef-1234567890ab" ] } ) results = response.json()["results"] ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/rheed/timeseries/batch/?data_ids=d290f1ee-6c54-4b01-90e6-d701748f0851&data_ids=a1b2c3d4-5678-90ab-cdef-1234567890ab" \ -H "X-API-KEY: YOUR_API_KEY" ``` # Bulk Create Sample Annotations Source: https://docs.atomscale.ai/api-reference/samples/annotations-bulk POST /physical_samples/{physical_sample_id}/annotations/bulk Create multiple spatial annotations on a physical sample in one request Creates several spatial annotations on a physical sample in a single request. Send the annotations as an array in the request body. UUID of the physical sample Array of annotations to create. Each item has the same shape as the body of a single annotation create request. Name of the property being annotated Value of the annotated property Coordinate reference frame. One of `sample_relative` or `stage_absolute`. Units for the coordinate values X coordinate of the annotation Y coordinate of the annotation Z coordinate of the annotation Unit for the property value Characterization source type UUID of the data item the annotation is derived from Arbitrary key-value metadata for the annotation ## Response Returns an array of the created spatial annotation objects. Annotation UUID UUID of the physical sample the annotation belongs to Name of the annotated property Value of the annotated property Unit for the property value, or `null` if not set Characterization source type, or `null` if not set Coordinate reference frame. One of `sample_relative` or `stage_absolute`. Units for the coordinate values, or `null` if not set X coordinate of the annotation, or `null` if not set Y coordinate of the annotation, or `null` if not set Z coordinate of the annotation, or `null` if not set UUID of the data item the annotation is derived from, or `null` if not set Arbitrary key-value metadata, or `null` if not set ISO 8601 timestamp of the last update Version string of the annotation record ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/bulk", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "annotations": [ { "property_name": "thickness", "property_value": 25.1, "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 1.5, "coord_y": 2.0 }, { "property_name": "thickness", "property_value": 24.8, "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 3.0, "coord_y": 2.0 } ] } ) annotations = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/bulk" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "annotations": [ { "property_name": "thickness", "property_value": 25.1, "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 1.5, "coord_y": 2.0 }, { "property_name": "thickness", "property_value": 24.8, "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 3.0, "coord_y": 2.0 } ] }' ``` ```json 200 theme={null} [ { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "property_name": "thickness", "property_value": 25.1, "property_unit": null, "char_source_type": null, "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 1.5, "coord_y": 2.0, "coord_z": null, "data_id": null, "annotation_metadata": null, "last_updated": "2024-01-16T09:30:00Z", "version": "0.269.1" } ] ``` # Create Sample Annotation Source: https://docs.atomscale.ai/api-reference/samples/annotations-create POST /physical_samples/{physical_sample_id}/annotations/ Record a spatial annotation on a physical sample Creates a spatial annotation on a physical sample. An annotation captures a property value at a location on the sample. UUID of the physical sample Name of the property being annotated Value of the annotated property Coordinate reference frame. One of `sample_relative` or `stage_absolute`. Units for the coordinate values X coordinate of the annotation Y coordinate of the annotation Z coordinate of the annotation Unit for the property value Characterization source type UUID of the data item the annotation is derived from Arbitrary key-value metadata for the annotation ## Response Annotation UUID UUID of the physical sample the annotation belongs to Name of the annotated property Value of the annotated property Unit for the property value, or `null` if not set Characterization source type, or `null` if not set Coordinate reference frame. One of `sample_relative` or `stage_absolute`. Units for the coordinate values, or `null` if not set X coordinate of the annotation, or `null` if not set Y coordinate of the annotation, or `null` if not set Z coordinate of the annotation, or `null` if not set UUID of the data item the annotation is derived from, or `null` if not set Arbitrary key-value metadata, or `null` if not set ISO 8601 timestamp of the last update Version string of the annotation record ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "property_name": "thickness", "property_value": 25.1, "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 1.5, "coord_y": 2.0 } ) annotation = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "property_name": "thickness", "property_value": 25.1, "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 1.5, "coord_y": 2.0 }' ``` ```json 200 theme={null} { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "property_name": "thickness", "property_value": 25.1, "property_unit": null, "char_source_type": null, "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 1.5, "coord_y": 2.0, "coord_z": null, "data_id": null, "annotation_metadata": null, "last_updated": "2024-01-16T09:30:00Z", "version": "0.269.1" } ``` # Delete Sample Annotations Source: https://docs.atomscale.ai/api-reference/samples/annotations-delete DELETE /physical_samples/{physical_sample_id}/annotations/ Delete spatial annotations from a physical sample Deletes one or more spatial annotations from a physical sample. Pass the annotation UUIDs to remove as the `annotation_ids` query parameter. UUID of the physical sample UUIDs of the annotations to delete ## Response Returns an empty response on success. ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"annotation_ids": ["f47ac10b-58cc-4372-a567-0e02b2c3d479"]} ) ``` ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/?annotation_ids=f47ac10b-58cc-4372-a567-0e02b2c3d479" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} {} ``` # List Sample Annotations Source: https://docs.atomscale.ai/api-reference/samples/annotations-list GET /physical_samples/{physical_sample_id}/annotations/ Retrieve spatial annotations for a physical sample Returns the spatial annotations recorded on a physical sample. Use the query parameters to filter by property, characterization source, or a spatial bounding box. UUID of the physical sample Filter to annotations with this property name Filter to annotations from this characterization source type Lower bound on the x coordinate for spatial filtering Upper bound on the x coordinate for spatial filtering Lower bound on the y coordinate for spatial filtering Upper bound on the y coordinate for spatial filtering ## Response Returns an array of spatial annotation objects. Annotation UUID UUID of the physical sample the annotation belongs to Name of the annotated property Value of the annotated property Unit for the property value, or `null` if not set Characterization source type, or `null` if not set Coordinate reference frame. One of `sample_relative` or `stage_absolute`. Units for the coordinate values, or `null` if not set X coordinate of the annotation, or `null` if not set Y coordinate of the annotation, or `null` if not set Z coordinate of the annotation, or `null` if not set UUID of the data item the annotation is derived from, or `null` if not set Arbitrary key-value metadata, or `null` if not set ISO 8601 timestamp of the last update Version string of the annotation record ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"property_name": "thickness"} ) annotations = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/?property_name=thickness" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "property_name": "thickness", "property_value": 25.1, "property_unit": "nm", "char_source_type": "xrd", "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 1.5, "coord_y": 2.0, "coord_z": null, "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "annotation_metadata": null, "last_updated": "2024-01-16T09:30:00Z", "version": "0.269.1" } ] ``` # Update Sample Annotation Source: https://docs.atomscale.ai/api-reference/samples/annotations-update PATCH /physical_samples/{physical_sample_id}/annotations/{annotation_id} Update fields on an existing spatial annotation Updates a spatial annotation. Only the fields you include in the body are changed. UUID of the physical sample UUID of the annotation to update Name of the property being annotated Value of the annotated property Unit for the property value Coordinate reference frame. One of `sample_relative` or `stage_absolute`. Units for the coordinate values X coordinate of the annotation Y coordinate of the annotation Z coordinate of the annotation Arbitrary key-value metadata for the annotation ## Response Annotation UUID UUID of the physical sample the annotation belongs to Name of the annotated property Value of the annotated property Unit for the property value, or `null` if not set Characterization source type, or `null` if not set Coordinate reference frame. One of `sample_relative` or `stage_absolute`. Units for the coordinate values, or `null` if not set X coordinate of the annotation, or `null` if not set Y coordinate of the annotation, or `null` if not set Z coordinate of the annotation, or `null` if not set UUID of the data item the annotation is derived from, or `null` if not set Arbitrary key-value metadata, or `null` if not set ISO 8601 timestamp of the last update Version string of the annotation record ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/f47ac10b-58cc-4372-a567-0e02b2c3d479", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "property_value": 26.0 } ) annotation = response.json() ``` ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/annotations/f47ac10b-58cc-4372-a567-0e02b2c3d479" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "property_value": 26.0 }' ``` ```json 200 theme={null} { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "property_name": "thickness", "property_value": 26.0, "property_unit": "nm", "char_source_type": "xrd", "coord_ref_frame": "sample_relative", "coord_units": "mm", "coord_x": 1.5, "coord_y": 2.0, "coord_z": null, "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "annotation_metadata": null, "last_updated": "2024-01-16T10:15:00Z", "version": "0.269.1" } ``` # Create Physical Sample Source: https://docs.atomscale.ai/api-reference/samples/create POST /physical_samples/ Register a new physical sample Creates a new physical sample record. Samples represent the substrates and materials that measurements are taken from. Name or label for the sample ID of the growth instrument associated with this sample Numeric sample identifier Free-text notes. When provided, the server creates a detail note and links it to the sample. ## Response UUID of the created sample Sample name ISO 8601 timestamp of creation ISO 8601 timestamp of last update ID of the user who created the sample Growth instrument ID Numeric sample identifier ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/physical_samples/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "GaAs-003" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/physical_samples/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "GaAs-003" } ) sample = response.json() ``` ```json 200 theme={null} { "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "name": "GaAs-003", "created_datetime": "2024-01-15T11:00:00Z", "last_updated": "2024-01-15T11:00:00Z", "version": "0.269.1", "user_id": "user_abc123", "growth_instrument_id": null, "sample_id": null, "detail_notes_id": null, "physical_sample_metadata": {} } ``` # Delete Physical Samples Source: https://docs.atomscale.ai/api-reference/samples/delete DELETE /physical_samples/ Delete one or more physical samples Permanently removes physical sample records. Associated data entries are preserved but become unlinked from the sample. Deleting a sample removes the sample record but preserves associated data entries. The data entries will become unlinked from the sample. Array of physical sample UUIDs to delete (passed as repeated query parameters) ## Response Returns an empty object on success. ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/physical_samples/?physical_sample_ids=a1b2c3d4-5678-90ab-cdef-1234567890ab&physical_sample_ids=b2c3d4e5-6789-01bc-defg-2345678901bc" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/physical_samples/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={ "physical_sample_ids": [ "a1b2c3d4-5678-90ab-cdef-1234567890ab", "b2c3d4e5-6789-01bc-defg-2345678901bc" ] } ) ``` ```json 200 theme={null} {} ``` # List Physical Samples Source: https://docs.atomscale.ai/api-reference/samples/list GET /physical_samples/ Retrieve physical samples in your organization Returns physical samples for your organization with optional filtering. Filter by a specific physical sample UUID Filter to samples that have (or don't have) associated data items ## Response Returns an array of physical sample objects. Physical sample UUID Sample name or label ISO 8601 timestamp of creation ISO 8601 timestamp of last update User ID of the sample owner Display name of the sample owner Number of data entries associated with this sample Growth instrument details Target material information Projects this sample belongs to Sample notes content ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") samples = client.list_physical_samples() ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/physical_samples/", headers={"X-API-KEY": "YOUR_API_KEY"} ) samples = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/physical_samples/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "name": "GaAs-001", "created_datetime": "2024-01-12T09:00:00Z", "last_updated": "2024-01-15T10:00:00Z", "owner_id": "user_abc123", "owner_name": "Jane Smith", "num_data_items": 8, "growth_instrument": null, "target_material": null, "projects": [ { "id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "name": "GaAs MBE Development" } ], "detail_notes": null } ] ``` # Get Physical Sample Timeseries Source: https://docs.atomscale.ai/api-reference/samples/timeseries GET /physical_samples/{physical_sample_id}/timeseries/ Retrieve aggregated timeseries data for a physical sample Returns timeseries data aggregated across the data items associated with a physical sample. Results are organized by property, where each property carries its own arrays of values and times. UUID of the physical sample ## Response UUID of the physical sample the timeseries belong to Array of property timeseries objects, one per property. Name of the property (e.g., `thickness`, `temperature`) Array of measurement values. Individual entries can be `null` where no value was recorded. Array of time values in seconds, aligned with `property_values` UUID of the result that produced this property series ISO 8601 timestamp of the last update to this series UUIDs of the data items that contributed to this series ID of the workflow that generated this series, or `null` if not applicable ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/timeseries/", headers={"X-API-KEY": "YOUR_API_KEY"} ) timeseries = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/timeseries/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "properties": [ { "property_name": "thickness", "property_values": [0, 5.2, 10.1, null, 20.0], "real_time_seconds": [0, 10, 20, 30, 40], "result_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "last_updated": "2024-01-15T10:00:00Z", "constituent_data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"], "generating_dbos_workflow_id": null } ] } ``` # Update Physical Sample Source: https://docs.atomscale.ai/api-reference/samples/update PATCH /physical_samples/{physical_sample_id} Update fields on an existing physical sample Updates a physical sample record. Only the fields you include in the body are changed. UUID of the physical sample to update Name or label for the sample Numeric sample identifier UUID of the detail note to link to this sample Arbitrary key-value metadata for the sample ## Response Physical sample UUID Sample name ISO 8601 timestamp of creation ISO 8601 timestamp of last update Version string of the sample record ID of the user who owns the sample Growth instrument ID, or `null` if not set Numeric sample identifier, or `null` if not set UUID of the linked detail note, or `null` if not set Key-value metadata for the sample ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "GaAs-003-revised" }' ``` ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "GaAs-003-revised" } ) sample = response.json() ``` ```json 200 theme={null} { "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "name": "GaAs-003-revised", "created_datetime": "2024-01-15T11:00:00Z", "last_updated": "2024-01-16T09:30:00Z", "version": "0.269.1", "user_id": "user_abc123", "growth_instrument_id": null, "sample_id": null, "detail_notes_id": null, "physical_sample_metadata": {} } ``` # Update Sample Growth Instrument Source: https://docs.atomscale.ai/api-reference/samples/update-growth-instrument PATCH /physical_samples/{physical_sample_id}/growth_instrument Set or clear the growth instrument for a physical sample Assigns a growth instrument to a physical sample. The `growth_instrument_id` key is required in the body: pass an integer to set the instrument, or `null` to clear it. UUID of the physical sample ID of the growth instrument to associate with the sample. Pass `null` to remove the current instrument. ## Response Physical sample UUID Sample name ISO 8601 timestamp of creation ISO 8601 timestamp of last update Version string of the sample record ID of the user who owns the sample Growth instrument ID, or `null` if not set Numeric sample identifier, or `null` if not set UUID of the linked detail note, or `null` if not set Key-value metadata for the sample ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/growth_instrument" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "growth_instrument_id": 42 }' ``` ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/growth_instrument", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "growth_instrument_id": 42 } ) sample = response.json() ``` ```json 200 theme={null} { "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "name": "GaAs-003", "created_datetime": "2024-01-15T11:00:00Z", "last_updated": "2024-01-16T09:30:00Z", "version": "0.269.1", "user_id": "user_abc123", "growth_instrument_id": 42, "sample_id": null, "detail_notes_id": null, "physical_sample_metadata": {} } ``` # Update Sample Target Material Source: https://docs.atomscale.ai/api-reference/samples/update-target-material PATCH /physical_samples/{physical_sample_id}/target_material Set or clear the target material for a physical sample Assigns a target material to a physical sample. The `target_material_id` key is required in the body: pass an integer to set the material, or `null` to clear it. UUID of the physical sample ID of the target material to associate with the sample. Pass `null` to remove the current target material. ## Response Physical sample UUID Sample name ISO 8601 timestamp of creation ISO 8601 timestamp of last update Version string of the sample record ID of the user who owns the sample Growth instrument ID, or `null` if not set Numeric sample identifier, or `null` if not set UUID of the linked detail note, or `null` if not set Key-value metadata for the sample ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/target_material" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "target_material_id": 7 }' ``` ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/physical_samples/a1b2c3d4-5678-90ab-cdef-1234567890ab/target_material", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "target_material_id": 7 } ) sample = response.json() ``` ```json 200 theme={null} { "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab", "name": "GaAs-003", "created_datetime": "2024-01-15T11:00:00Z", "last_updated": "2024-01-16T09:30:00Z", "version": "0.269.1", "user_id": "user_abc123", "growth_instrument_id": null, "sample_id": null, "detail_notes_id": null, "physical_sample_metadata": {} } ``` # Get SEM Fingerprint Source: https://docs.atomscale.ai/api-reference/sem/fingerprint GET /sem/images/{data_id}/fingerprint Retrieve the SEM image fingerprint for surface morphology analysis Returns a computed fingerprint characterizing the surface morphology from an SEM image. The response segments detected features into three categories (inliers, low outliers, and high outliers) based on the selected feature metric, each with an RLE-encoded mask and statistics. The data entry UUID for the SEM image Feature metric to use for fingerprinting (e.g., `area`) ## Response Data entry UUID Processed data UUID Total number of detected features Features classified as low outliers RLE-encoded mask for this category Run-length encoded mask data Mask height in pixels Mask width in pixels RGBA color for visualization Statistics for this category Number of features in this category Percentage of total features Features classified as inliers (same structure as `low_outliers`) Features classified as high outliers (same structure as `low_outliers`) ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") sem = results[0] ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/sem/images/d290f1ee-6c54-4b01-90e6-d701748f0851/fingerprint", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"feature": "area"} ) fingerprint = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/sem/images/d290f1ee-6c54-4b01-90e6-d701748f0851/fingerprint?feature=area" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "processed_data_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "total_count": 142, "low_outliers": { "mask": { "mask_rle": "0 50 1 10 0 200 ...", "mask_height": 1024, "mask_width": 1024, "color": [255, 0, 0, 0.15] }, "details": { "count": 12, "percentage": 8.45 } }, "inliers": { "mask": { "mask_rle": "0 30 1 20 0 180 ...", "mask_height": 1024, "mask_width": 1024, "color": [0, 255, 0, 0.15] }, "details": { "count": 118, "percentage": 83.1 } }, "high_outliers": { "mask": { "mask_rle": "0 100 1 5 0 300 ...", "mask_height": 1024, "mask_width": 1024, "color": [0, 0, 255, 0.15] }, "details": { "count": 12, "percentage": 8.45 } } } ``` # Get SEM Histograms Source: https://docs.atomscale.ai/api-reference/sem/histograms GET /sem/histograms/{data_id}/ Retrieve feature distribution histograms for an SEM image Returns binned distributions of the morphological features detected in an SEM image (for example, feature area, diameter, and circularity). Each requested feature is returned as its own histogram series. The data entry UUID for the SEM image Which feature histograms to return. Defaults to all supported features. ## Response Data entry UUID One histogram series per requested feature Feature name (e.g., `area`) Unit for the feature values Optional facet label for the series The histogram bins for this feature Lower edge of the bin Upper edge of the bin Number of features falling within the bin ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/sem/histograms/d290f1ee-6c54-4b01-90e6-d701748f0851/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"return_features": ["area", "circularity"]} ) histograms = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/sem/histograms/d290f1ee-6c54-4b01-90e6-d701748f0851/?return_features=area&return_features=circularity" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "series_by_feature": [ { "name": "area", "unit": "nm^2", "facet": null, "histogram_bins": [ {"x0": 0.0, "x1": 100.0, "count": 12}, {"x0": 100.0, "x1": 200.0, "count": 45}, {"x0": 200.0, "x1": 300.0, "count": 30} ] } ] } ``` # Get SEM Mask Source: https://docs.atomscale.ai/api-reference/sem/mask GET /sem/images/{data_id}/mask Retrieve the RLE-encoded segmentation mask for an SEM image Returns the detected segmentation mask for an SEM image, encoded using run-length encoding (RLE). The mask identifies the features segmented from the image during processing. The data entry UUID for the SEM image ## Response Run-length encoded mask data Height of the mask in pixels Width of the mask in pixels RGBA color for visualizing the mask ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/sem/images/d290f1ee-6c54-4b01-90e6-d701748f0851/mask", headers={"X-API-KEY": "YOUR_API_KEY"} ) mask = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/sem/images/d290f1ee-6c54-4b01-90e6-d701748f0851/mask" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "mask_rle": "0 100 1 50 0 200 ...", "mask_height": 1024, "mask_width": 1024, "color": [0, 0, 255, 0.2] } ``` # Get SEM Overlay Source: https://docs.atomscale.ai/api-reference/sem/overlay GET /sem/images/{data_id}/overlay Download the SEM image with the detected mask overlaid Returns the SEM image rendered with the detected segmentation mask overlaid. The response is a ZIP archive (`application/x-zip-compressed`) containing a single PNG named `{data_id}.png`, rather than a JSON body. The data entry UUID for the SEM image ## Response A ZIP archive containing the overlay image as a PNG. Write the response content to a file and unzip it to access the image. ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/sem/images/d290f1ee-6c54-4b01-90e6-d701748f0851/overlay", headers={"X-API-KEY": "YOUR_API_KEY"} ) with open("overlay.zip", "wb") as f: f.write(response.content) ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/sem/images/d290f1ee-6c54-4b01-90e6-d701748f0851/overlay" \ -H "X-API-KEY: YOUR_API_KEY" \ --output overlay.zip ``` # Get Processed SEM Image Source: https://docs.atomscale.ai/api-reference/sem/processed GET /sem/images/{data_id}/processed Retrieve the processed SEM image with the segmentation overlay Returns the processed SEM image (the original image with the detected pattern overlaid). Use the `return_as` parameter to choose whether the image is returned as raw bytes or as a presigned URL. The data entry UUID for the SEM image How to return the image: `bytes` for the raw image bytes, `url-download` for a presigned download URL, or `url-embed` for a presigned URL suitable for embedding ## Response When `return_as` is `url-download` or `url-embed`, returns a JSON object describing the image. When `return_as` is `bytes`, returns the raw image bytes. Presigned URL for the processed image File name of the processed image Image format (e.g., `png`) Metadata associated with the processed image ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/sem/images/d290f1ee-6c54-4b01-90e6-d701748f0851/processed", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"return_as": "url-embed"} ) processed = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/sem/images/d290f1ee-6c54-4b01-90e6-d701748f0851/processed?return_as=url-embed" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "url": "https://processed-bucket.s3.amazonaws.com/...", "file_name": "sem_image_processed.png", "file_format": "png", "metadata": {} } ``` # Get SIMS Data Source: https://docs.atomscale.ai/api-reference/sims/get GET /sims/{data_id} Retrieve secondary ion mass spectrometry depth profiles Returns secondary ion mass spectrometry (SIMS) depth profile data for a data entry, as a list of profiles with one entry per chemical species. Each profile pairs a depth axis with the measured concentration at each depth. The data entry UUID for the SIMS measurement ## Response Returns an array of depth profile objects, one per species, ordered by species name. If no SIMS data exists for the entry, an empty array is returned. Data entry UUID SIMS depth profile identifier Processed data entry UUID Chemical species for this depth profile (e.g., `Si`, `O`) Depth values Unit for the `depths` values (e.g., `nm`) Concentration values corresponding to each depth Unit for the `concentrations` values (e.g., `atoms/cm^3`) Additional metadata for this species profile ISO 8601 timestamp of last update ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") sims = results[0] # SIMSResult print(sims.depth_profiles) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/sims/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) sims_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/sims/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "id": "sims_001", "processed_data_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "species_name": "Si", "depths": [0, 10, 20, 30, 40], "depth_units": "nm", "concentrations": [1.2e18, 3.4e18, 5.1e18, 2.0e18, 8.0e17], "concentration_units": "atoms/cm^3", "species_metadata": {}, "last_updated": "2024-01-15T10:35:00Z" } ] ``` # Get SIMS Species Profile Source: https://docs.atomscale.ai/api-reference/sims/get-species GET /sims/{data_id}/species/{species_name} Retrieve a single SIMS depth profile by species Returns the SIMS depth profile for a single chemical species within a data entry. Returns `null` if no profile exists for the requested species. The data entry UUID for the SIMS measurement The chemical species to retrieve (e.g., `Si`) ## Response Returns a single depth profile object with the same shape as an item in the [Get SIMS Data](/api-reference/sims/get) response, or `null` if the species is not present. ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/sims/d290f1ee-6c54-4b01-90e6-d701748f0851/species/Si", headers={"X-API-KEY": "YOUR_API_KEY"} ) profile = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/sims/d290f1ee-6c54-4b01-90e6-d701748f0851/species/Si" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "id": "sims_001", "processed_data_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "species_name": "Si", "depths": [0, 10, 20, 30, 40], "depth_units": "nm", "concentrations": [1.2e18, 3.4e18, 5.1e18, 2.0e18, 8.0e17], "concentration_units": "atoms/cm^3", "species_metadata": {}, "last_updated": "2024-01-15T10:35:00Z" } ``` # Get Photoluminescence Data Source: https://docs.atomscale.ai/api-reference/spectroscopy/photoluminescence GET /photoluminescence/{data_id} Retrieve photoluminescence spectroscopy data Returns processed photoluminescence (PL) data including energies, intensities, and detected peaks. The data entry UUID for the PL measurement ## Response Data entry UUID PL results identifier Energy values (eV) Intensity values corresponding to the energies Whether normalization has been applied to the intensity values Detected emission peaks in the spectrum ISO 8601 timestamp of last update ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") pl = results[0] # PhotoluminescenceResult print(pl.energies) print(pl.detected_peaks) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/photoluminescence/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) pl_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/photoluminescence/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "id": "pl_001", "energies": [1.31, 1.38, 1.42, 1.46, 1.55], "intensities": [0.08, 0.52, 1.0, 0.45, 0.1], "normalization_applied": true, "detected_peaks": [ {"energy": 1.424, "intensity": 1.0} ], "last_updated": "2024-01-15T10:35:00Z" } ``` # Get Raman Data Source: https://docs.atomscale.ai/api-reference/spectroscopy/raman GET /raman/{data_id} Retrieve Raman spectroscopy data Returns processed Raman spectroscopy data including energies, intensities, and detected peaks. The data entry UUID for the Raman measurement ## Response Data entry UUID Raman results identifier Energy/wavenumber values Intensity values corresponding to the energies Detected peaks in the spectrum ISO 8601 timestamp of last update ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") raman = results[0] # RamanResult print(raman.raman_shift) print(raman.detected_peaks) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/raman/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) raman_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/raman/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "id": "raman_001", "energies": [100, 200, 268, 291, 400, 500], "intensities": [0.1, 0.3, 0.45, 0.82, 0.2, 0.15], "detected_peaks": [ {"position": 291.5, "intensity": 0.82}, {"position": 268.2, "intensity": 0.45} ], "last_updated": "2024-01-15T10:35:00Z" } ``` # Get XPS Data Source: https://docs.atomscale.ai/api-reference/spectroscopy/xps GET /xps/{data_id} Retrieve X-ray photoelectron spectroscopy data and analysis Returns processed XPS data including binding energies, intensities, predicted composition, and detected peaks. The data entry UUID for the XPS measurement ## Response Data entry UUID XPS results identifier Binding energy values (eV) Intensity/count values corresponding to binding energies Predicted elemental composition Uncertainty values for the predicted composition User-specified elements for analysis Elements predicted from the spectrum Predicted chemical formula Detected peaks in the spectrum Formula as a dictionary of element counts ISO 8601 timestamp of last update ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") xps = results[0] # XPSResult print(xps.predicted_composition) print(xps.detected_peaks) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/xps/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) xps_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/xps/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "xps_id": "xps_001", "binding_energies": [0, 10, 20, 30], "intensities": [1000, 1200, 1100, 900], "predicted_composition": {"Ga": 0.485, "As": 0.472, "O": 0.031, "C": 0.012}, "composition_uncertainty": {"Ga": 0.02, "As": 0.02, "O": 0.01, "C": 0.005}, "set_elements": [], "predicted_elements": ["Ga", "As", "O", "C"], "predicted_formula": "GaAs", "detected_peaks": [{"element": "Ga3d", "position": 19.2}], "dict_formula": {"Ga": 1, "As": 1}, "last_updated": "2024-01-15T10:35:00Z" } ``` # Get XRD Data Source: https://docs.atomscale.ai/api-reference/spectroscopy/xrd GET /xrd/{data_id} Retrieve X-ray diffraction data and analysis Returns processed X-ray diffraction (XRD) data, including the diffraction pattern (intensity versus 2θ) and any detected peaks. The data entry UUID for the XRD measurement ## Response Data entry UUID XRD results identifier Diffraction angle (2θ) values Unit for the `two_theta` values (e.g., `degrees`) Intensity/count values corresponding to each `two_theta` value X-ray source wavelength in angstroms Detected peaks in the diffraction pattern Additional metadata about the measurement ISO 8601 timestamp of last update ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") xrd = results[0] # XRDResult print(xrd.two_theta) print(xrd.detected_peaks) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/xrd/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) xrd_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/xrd/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "id": "xrd_001", "two_theta": [20.0, 20.1, 20.2, 20.3, 20.4], "two_theta_unit": "degrees", "intensities": [120, 340, 1580, 410, 150], "wavelength_angstrom": 1.5406, "detected_peaks": [ {"position": 20.2, "intensity": 1580} ], "spectral_metadata": {}, "last_updated": "2024-01-15T10:35:00Z" } ``` # Add Tags to Data Items Source: https://docs.atomscale.ai/api-reference/tags/add-to-data-items POST /tags/data-items/ Apply tags to data items in bulk Applies the given tags to the given data items. Every tag in `tag_ids` is applied to every data item in `data_ids`. Array of data item UUIDs to tag Array of tag UUIDs to apply ## Response Returns an empty response on success. ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/tags/data-items/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"], "tag_ids": ["7c9e6679-7425-40de-944b-e07fc1f90ae7"] } ) ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/tags/data-items/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"], "tag_ids": ["7c9e6679-7425-40de-944b-e07fc1f90ae7"] }' ``` ```json 200 theme={null} {} ``` # Create Tag Source: https://docs.atomscale.ai/api-reference/tags/create POST /tags/ Create a new tag Creates a new tag for your organization. Optionally assign the tag to a category, or provide an explicit id. Tag name ID of the category to assign this tag to Explicit UUID to use for the tag. If omitted, the server generates one. ## Response Returns the created tag. Tag UUID Tag name ID of the organization the tag belongs to ID of the category this tag belongs to, or `null` if uncategorized Name of the tag's category, or `null` if uncategorized Number of data items this tag is applied to ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/tags/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "high-priority", "tag_category_id": "b1a2c3d4-1111-2222-3333-444455556666" } ) tag = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/tags/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "high-priority", "tag_category_id": "b1a2c3d4-1111-2222-3333-444455556666" }' ``` ```json 200 theme={null} { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "high-priority", "organization_id": "org_abc123", "tag_category_id": "b1a2c3d4-1111-2222-3333-444455556666", "category_name": "Review Status", "usage_count": 0 } ``` # Create Tag Category Source: https://docs.atomscale.ai/api-reference/tags/create-category POST /tags/categories/ Create a new tag category Creates a new tag category for your organization. Categories group related tags together. Optionally provide an explicit id. Category name Explicit UUID to use for the category. If omitted, the server generates one. ## Response Returns the created category. Tag category UUID Category name ID of the organization the category belongs to Number of tags that belong to this category ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/tags/categories/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "Review Status" } ) category = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/tags/categories/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Review Status" }' ``` ```json 200 theme={null} { "id": "b1a2c3d4-1111-2222-3333-444455556666", "name": "Review Status", "organization_id": "org_abc123", "tag_count": 0 } ``` # Delete Tags Source: https://docs.atomscale.ai/api-reference/tags/delete DELETE /tags/ Delete one or more tags Permanently removes one or more tags. The tags are also removed from any data items they were applied to. Array of tag UUIDs to delete (passed as repeated query parameters) ## Response Returns an empty response on success. ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/tags/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={ "tag_ids": [ "7c9e6679-7425-40de-944b-e07fc1f90ae7" ] } ) ``` ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/tags/?tag_ids=7c9e6679-7425-40de-944b-e07fc1f90ae7" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} {} ``` # Delete Tag Categories Source: https://docs.atomscale.ai/api-reference/tags/delete-categories DELETE /tags/categories/ Delete one or more tag categories Permanently removes one or more tag categories. Tags that belonged to a deleted category become uncategorized. Array of tag category UUIDs to delete (passed as repeated query parameters) ## Response Returns an empty response on success. ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/tags/categories/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={ "category_ids": [ "b1a2c3d4-1111-2222-3333-444455556666" ] } ) ``` ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/tags/categories/?category_ids=b1a2c3d4-1111-2222-3333-444455556666" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} {} ``` # Get Tags on Data Item Source: https://docs.atomscale.ai/api-reference/tags/get-data-item-tags GET /tags/data-items/{data_id} Retrieve the tags applied to a data item Returns the tags currently applied to a specific data item. UUID of the data item ## Response Returns an array of tag objects applied to the data item. Tag UUID Tag name ID of the category this tag belongs to, or `null` if uncategorized Name of the tag's category, or `null` if uncategorized ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/tags/data-items/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) tags = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/tags/data-items/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "high-priority", "tag_category_id": "b1a2c3d4-1111-2222-3333-444455556666", "category_name": "Review Status" } ] ``` # List Tags Source: https://docs.atomscale.ai/api-reference/tags/list GET /tags/ Retrieve all tags for your organization Returns all tags defined for your organization, including how many data items each tag is applied to. ## Response Returns an array of tag objects. Tag UUID Tag name ID of the organization the tag belongs to ID of the category this tag belongs to, or `null` if uncategorized Name of the tag's category, or `null` if uncategorized Number of data items this tag is applied to ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/tags/", headers={"X-API-KEY": "YOUR_API_KEY"} ) tags = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/tags/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "high-priority", "organization_id": "org_abc123", "tag_category_id": "b1a2c3d4-1111-2222-3333-444455556666", "category_name": "Review Status", "usage_count": 12 } ] ``` # List Tag Categories Source: https://docs.atomscale.ai/api-reference/tags/list-categories GET /tags/categories/ Retrieve all tag categories for your organization Returns all tag categories defined for your organization, including how many tags belong to each category. ## Response Returns an array of tag category objects. Tag category UUID Category name ID of the organization the category belongs to Number of tags that belong to this category ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/tags/categories/", headers={"X-API-KEY": "YOUR_API_KEY"} ) categories = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/tags/categories/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": "b1a2c3d4-1111-2222-3333-444455556666", "name": "Review Status", "organization_id": "org_abc123", "tag_count": 4 } ] ``` # Remove Tags from Data Items Source: https://docs.atomscale.ai/api-reference/tags/remove-from-data-items DELETE /tags/data-items/ Remove tags from data items in bulk Removes the given tags from the given data items. Every tag in `tag_ids` is removed from every data item in `data_ids`. This DELETE request takes a JSON body rather than query parameters. Set the `Content-Type` header to `application/json` and send `data_ids` and `tag_ids` in the body. Array of data item UUIDs to untag Array of tag UUIDs to remove ## Response Returns an empty response on success. ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/tags/data-items/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"], "tag_ids": ["7c9e6679-7425-40de-944b-e07fc1f90ae7"] } ) ``` ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/tags/data-items/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "data_ids": ["d290f1ee-6c54-4b01-90e6-d701748f0851"], "tag_ids": ["7c9e6679-7425-40de-944b-e07fc1f90ae7"] }' ``` ```json 200 theme={null} {} ``` # Update Tags Source: https://docs.atomscale.ai/api-reference/tags/update PATCH /tags/ Update one or more tags Updates one or more tags. You pass a list of tag update objects, so you can update multiple tags in a single request. Each object carries the tag id plus the fields you want to change. The request body is an array of tag update objects. UUID of the tag to update Updated tag name Updated category ID for the tag ## Response Returns an array of the updated tags. Tag UUID Tag name ID of the organization the tag belongs to ID of the category this tag belongs to, or `null` if uncategorized Name of the tag's category, or `null` if uncategorized Number of data items this tag is applied to ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/tags/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json=[ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "critical", "tag_category_id": "b1a2c3d4-1111-2222-3333-444455556666" } ] ) tags = response.json() ``` ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/tags/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '[ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "critical", "tag_category_id": "b1a2c3d4-1111-2222-3333-444455556666" } ]' ``` ```json 200 theme={null} [ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "critical", "organization_id": "org_abc123", "tag_category_id": "b1a2c3d4-1111-2222-3333-444455556666", "category_name": "Review Status", "usage_count": 12 } ] ``` # Update Tag Category Source: https://docs.atomscale.ai/api-reference/tags/update-category PATCH /tags/categories/{category_id} Update a tag category Updates an existing tag category. UUID of the tag category to update Updated category name ## Response Returns the updated category. Tag category UUID Category name ID of the organization the category belongs to Number of tags that belong to this category ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/tags/categories/b1a2c3d4-1111-2222-3333-444455556666", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "name": "Review Stage" } ) category = response.json() ``` ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/tags/categories/b1a2c3d4-1111-2222-3333-444455556666" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Review Stage" }' ``` ```json 200 theme={null} { "id": "b1a2c3d4-1111-2222-3333-444455556666", "name": "Review Stage", "organization_id": "org_abc123", "tag_count": 4 } ``` # Create Target Material Source: https://docs.atomscale.ai/api-reference/target-materials/create POST /target-materials/ Create a target material with an optional layer stack Creates a new target material. A target material is the intended layer stack and substrate for a sample. Provide the sample details and, optionally, the layers that make up the stack. Details of the target material sample. Name for the target material sample Substrate the layers are grown on (e.g., `Si`) Arbitrary metadata to attach to the sample Array of layer definitions that make up the stack. Each layer can include `composition_string`, `layer_id`, `orientation`, `space_group`, `mp_id`, and `element_dict`. ## Response ID of the created target material sample Name of the target material sample Substrate the layers are grown on Metadata attached to the sample ID of the linked detail note, if any ID of the user who created the record Schema version of the record ISO 8601 timestamp of the last update ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/target-materials/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "sample": { "sample_name": "BaTiO3 on Si", "substrate": "Si" }, "layers": [ { "layer_id": 1, "composition_string": "BaTiO3", "orientation": "001" } ] }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/target-materials/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "sample": { "sample_name": "BaTiO3 on Si", "substrate": "Si" }, "layers": [ { "layer_id": 1, "composition_string": "BaTiO3", "orientation": "001" } ] } ) target_material = response.json() ``` ```json 200 theme={null} { "sample_id": 42, "sample_name": "BaTiO3 on Si", "substrate": "Si", "sample_metadata": {}, "detail_notes_id": null, "user_id": "user_abc123", "version": "0.269.1", "last_updated": "2024-01-15T11:00:00Z" } ``` # Create Target Material Layer Source: https://docs.atomscale.ai/api-reference/target-materials/create-layer POST /target-materials/layers/ Add a single layer to a target material Adds one layer to a target material's stack. The `layer_id` sets the layer position, and `sample_id` links the layer to its parent target material. Layer position within the stack ID of the parent target material Chemical composition of the layer (e.g., `BaTiO3`) Element counts or fractions that make up the layer Crystallographic orientation of the layer Space group of the layer Materials Project ID for the layer material Explicit record ID for the layer ## Response Layer record ID Layer position within the stack ID of the parent target material Chemical composition of the layer Element counts or fractions that make up the layer Crystallographic orientation of the layer Space group of the layer Materials Project ID for the layer material ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/target-materials/layers/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "layer_id": 1, "sample_id": 42, "composition_string": "BaTiO3", "orientation": "001" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/target-materials/layers/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "layer_id": 1, "sample_id": 42, "composition_string": "BaTiO3", "orientation": "001" } ) layer = response.json() ``` ```json 200 theme={null} { "id": 100, "layer_id": 1, "sample_id": 42, "composition_string": "BaTiO3", "element_dict": { "Ba": 1, "Ti": 1, "O": 3 }, "orientation": "001", "space_group": "P4mm", "mp_id": "mp-5020" } ``` # Delete Target Materials Source: https://docs.atomscale.ai/api-reference/target-materials/delete DELETE /target-materials/ Delete one or more target materials Permanently removes target material records, including their layer stacks. Array of target material IDs to delete (passed as repeated query parameters) ## Response Returns an empty response on success. ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/target-materials/?target_material_ids=42&target_material_ids=43" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/target-materials/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={"target_material_ids": [42, 43]} ) ``` ```json 200 theme={null} {} ``` # Delete Target Material Layer Source: https://docs.atomscale.ai/api-reference/target-materials/delete-layer DELETE /target-materials/layers/{sample_id}/{layer_id} Delete a single layer from a target material Removes one layer from a target material. Identify the layer by its parent target material (`sample_id`) and its position in the stack (`layer_id`). ID of the parent target material Layer position within the stack ## Response Returns an empty response on success. ```bash cURL theme={null} curl -X DELETE "https://api.atomscale.ai/target-materials/layers/42/1" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.atomscale.ai/target-materials/layers/42/1", headers={"X-API-KEY": "YOUR_API_KEY"} ) ``` ```json 200 theme={null} {} ``` # List Target Materials Source: https://docs.atomscale.ai/api-reference/target-materials/list GET /target-materials/ Retrieve target materials with their layer stacks Returns the target materials for your organization. A target material is the intended layer stack and substrate for a sample. Each entry describes one layer of a material, including its composition and crystallographic details. ## Response Returns an array of target material objects. Key fields shown below (the full response includes additional metadata fields). Target material record ID ID of the associated target material sample Name of the target material sample Substrate the layers are grown on (e.g., `Si`) Layer position within the stack Chemical composition of the layer (e.g., `BaTiO3`) Element counts or fractions that make up the layer Crystallographic orientation of the layer, if known Space group of the layer, if known Materials Project ID for the layer material, if matched ID of the linked detail note, if any Free-text content of the linked detail note Arbitrary metadata attached to the target material sample ID of the user who owns the record Name of the user who owns the record Schema version of the record ISO 8601 timestamp of the last update ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/target-materials/", headers={"X-API-KEY": "YOUR_API_KEY"} ) target_materials = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/target-materials/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "id": 42, "sample_id": 42, "sample_name": "BaTiO3 on Si", "substrate": "Si", "layer_id": 1, "composition_string": "BaTiO3", "element_dict": { "Ba": 1, "Ti": 1, "O": 3 }, "orientation": "001", "space_group": "P4mm", "mp_id": "mp-5020", "detail_notes_id": null, "detail_note_content": null, "sample_metadata": {}, "user_id": "user_abc123", "user_name": "Jane Researcher", "version": "0.269.1", "last_updated": "2024-01-15T11:00:00Z" } ] ``` # Parse Target Material Source: https://docs.atomscale.ai/api-reference/target-materials/parse POST /target-materials/parse Parse a natural-language material description into a structured stack Parses a free-text description of a material stack into a structured target material. Send a plain-language prompt (for example, "BaTiO3 on Si") and the endpoint returns the material name, substrate, and ordered layers. Free-text description of the material stack ## Response Concise label for the material (e.g., `BaTiO3/SrTiO3 on Si`) Substrate symbol (e.g., `Si`). Empty if the prompt describes no substrate. Layers ordered top-to-bottom (topmost first). Standard chemical formula string (e.g., `BaTiO3`) Crystallographic orientation, included only if stated in the prompt Space group, included only if stated in the prompt ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/target-materials/parse" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "BaTiO3 on SrTiO3 on Si" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/target-materials/parse", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "prompt": "BaTiO3 on SrTiO3 on Si" } ) parsed = response.json() ``` ```json 200 theme={null} { "material_name": "BaTiO3/SrTiO3 on Si", "substrate": "Si", "layers": [ { "formula": "BaTiO3", "orientation": null, "space_group": null }, { "formula": "SrTiO3", "orientation": null, "space_group": null } ] } ``` # Update Target Material Source: https://docs.atomscale.ai/api-reference/target-materials/update PATCH /target-materials/{target_material_id} Update the sample details of a target material Updates the sample-level details of a target material. All body fields are optional; only the fields you send are changed. ID of the target material to update New name for the target material sample New substrate the layers are grown on Reassign the target material to a different sample Replacement metadata for the sample ## Response ID of the target material sample Name of the target material sample Substrate the layers are grown on Metadata attached to the sample ID of the linked detail note, if any ID of the user who owns the record Schema version of the record ISO 8601 timestamp of the last update ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/target-materials/42" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "sample_name": "BaTiO3 on Si (revised)" }' ``` ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/target-materials/42", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "sample_name": "BaTiO3 on Si (revised)" } ) target_material = response.json() ``` ```json 200 theme={null} { "sample_id": 42, "sample_name": "BaTiO3 on Si (revised)", "substrate": "Si", "sample_metadata": {}, "detail_notes_id": null, "user_id": "user_abc123", "version": "0.269.1", "last_updated": "2024-01-15T12:00:00Z" } ``` # Update Target Material Layer Source: https://docs.atomscale.ai/api-reference/target-materials/update-layer PATCH /target-materials/layers/ Update a single layer of a target material Updates one layer of a target material. Identify the layer by its parent target material (`sample_id`) and position (`layer_id`), and send the fields you want to change. Layer position within the stack (used to identify the layer) ID of the parent target material (used to identify the layer) Chemical composition of the layer (e.g., `BaTiO3`) Element counts or fractions that make up the layer Crystallographic orientation of the layer Space group of the layer Materials Project ID for the layer material Explicit record ID for the layer ## Response Layer record ID Layer position within the stack ID of the parent target material Chemical composition of the layer Element counts or fractions that make up the layer Crystallographic orientation of the layer Space group of the layer Materials Project ID for the layer material ```bash cURL theme={null} curl -X PATCH "https://api.atomscale.ai/target-materials/layers/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "sample_id": 42, "layer_id": 1, "orientation": "111" }' ``` ```python Python theme={null} import requests response = requests.patch( "https://api.atomscale.ai/target-materials/layers/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "sample_id": 42, "layer_id": 1, "orientation": "111" } ) layer = response.json() ``` ```json 200 theme={null} { "id": 100, "layer_id": 1, "sample_id": 42, "composition_string": "BaTiO3", "element_dict": { "Ba": 1, "Ti": 1, "O": 3 }, "orientation": "111", "space_group": "P4mm", "mp_id": "mp-5020" } ``` # Replace Target Material Layers Source: https://docs.atomscale.ai/api-reference/target-materials/update-layers POST /target-materials/{target_material_id}/layers Replace the full layer stack of a target material Replaces a target material's entire layer stack. Send an array of layer objects, and they become the new stack for the target material. ID of the target material whose layers you are replacing The request body is an array of layer objects. Each layer supports the fields below. Layer position within the stack ID of the parent target material Chemical composition of the layer (e.g., `BaTiO3`) Element counts or fractions that make up the layer Crystallographic orientation of the layer Space group of the layer Materials Project ID for the layer material Explicit record ID for the layer ## Response Returns an empty response on success. ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/target-materials/42/layers" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '[ { "layer_id": 1, "composition_string": "BaTiO3", "orientation": "001" }, { "layer_id": 2, "composition_string": "SrTiO3" } ]' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/target-materials/42/layers", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json=[ { "layer_id": 1, "composition_string": "BaTiO3", "orientation": "001" }, { "layer_id": 2, "composition_string": "SrTiO3" } ] ) ``` ```json 200 theme={null} {} ``` # Finalize Tool State Stream Source: https://docs.atomscale.ai/api-reference/tool-state/finalize-stream POST /tool-state/stream/{data_id}/finalize Finalize a stream, marking it as complete Ends an active tool state streaming session and marks it as complete. Changes the pipeline status from `stream_active` to `success`. Once finalized, a stream cannot accept additional data. The data entry UUID for the streaming session ## Response The data ID for the finalized stream UUID for the processed data entry ```python SDK theme={null} from atomscale.streaming import TimeseriesStreamer streamer = TimeseriesStreamer(api_key="YOUR_API_KEY") streamer.finalize("d290f1ee-6c54-4b01-90e6-d701748f0851") ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/tool-state/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/finalize", headers={"X-API-KEY": "YOUR_API_KEY"} ) result = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/tool-state/stream/d290f1ee-6c54-4b01-90e6-d701748f0851/finalize" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "processed_data_id": "e3a1b2c3-4567-89ab-cdef-234567890abc" } ``` # Ingest Chunk Source: https://docs.atomscale.ai/api-reference/tool-state/ingest-chunk POST /tool-state/stream/chunk Ingest a single chunk of time series data for one channel Uploads a chunk of time series data for a single channel. Uses `chunk_index` to compute deterministic array positions, making the operation idempotent: retrying the same `(data_id, channel_name, chunk_index)` combination replaces the same slice rather than appending. Data ID returned from stream initialization Zero-based chunk index for ordering. Must be >= 0. Name of the data channel (e.g., `thickness`, `temperature`). Must be 1-255 characters. Array of Unix epoch timestamps in seconds Array of measured values corresponding to each timestamp Units for the values (e.g., `nm`, `C`) ## Response The data ID for this stream The channel name that was updated The chunk index that was ingested Total number of data points now stored for this channel ```python SDK theme={null} from atomscale.streaming import TimeseriesStreamer streamer = TimeseriesStreamer(api_key="YOUR_API_KEY", points_per_chunk=100) streamer.push( data_id="d290f1ee-6c54-4b01-90e6-d701748f0851", chunk_index=0, channel_name="thickness", timestamps=[1705312200.0, 1705312210.0, 1705312220.0], values=[0, 5.2, 10.1], units="nm", ) ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/tool-state/stream/chunk", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "chunk_index": 0, "channel_name": "thickness", "timestamps": [1705312200.0, 1705312210.0, 1705312220.0], "values": [0, 5.2, 10.1], "units": "nm" } ) result = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/tool-state/stream/chunk" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "chunk_index": 0, "channel_name": "thickness", "timestamps": [1705312200.0, 1705312210.0, 1705312220.0], "values": [0, 5.2, 10.1], "units": "nm" }' ``` ```json 201 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "channel_name": "thickness", "chunk_index": 0, "total_points": 3 } ``` # Ingest Multi-Channel Chunk Source: https://docs.atomscale.ai/api-reference/tool-state/ingest-chunk-multi POST /tool-state/stream/chunk/multi Ingest a chunk with multiple channels at once Uploads a chunk of time series data for multiple channels simultaneously. This is more efficient than making separate requests for each channel when you have data from multiple sensors at the same time points. Data ID returned from stream initialization Zero-based chunk index for ordering. Must be >= 0. Mapping of channel names to their data. Each channel contains: Array of Unix epoch timestamps in seconds Array of measured values Units for the values ## Response Returns an array of chunk responses, one for each channel. The data ID for this stream The channel name that was updated The chunk index that was ingested Total number of data points now stored for this channel ```python SDK theme={null} from atomscale.streaming import TimeseriesStreamer streamer = TimeseriesStreamer(api_key="YOUR_API_KEY", points_per_chunk=100) streamer.push_multi( data_id="d290f1ee-6c54-4b01-90e6-d701748f0851", chunk_index=0, channels={ "thickness": { "timestamps": [1705312200.0, 1705312210.0, 1705312220.0], "values": [0, 5.2, 10.1], "units": "nm", }, "deposition_rate": { "timestamps": [1705312200.0, 1705312210.0, 1705312220.0], "values": [0.52, 0.49, 0.52], "units": "nm/s", }, }, ) ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/tool-state/stream/chunk/multi", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "chunk_index": 0, "channels": { "thickness": { "timestamps": [1705312200.0, 1705312210.0, 1705312220.0], "values": [0, 5.2, 10.1], "units": "nm" }, "deposition_rate": { "timestamps": [1705312200.0, 1705312210.0, 1705312220.0], "values": [0.52, 0.49, 0.52], "units": "nm/s" } } } ) results = response.json() ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/tool-state/stream/chunk/multi" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "chunk_index": 0, "channels": { "thickness": { "timestamps": [1705312200.0, 1705312210.0, 1705312220.0], "values": [0, 5.2, 10.1], "units": "nm" }, "deposition_rate": { "timestamps": [1705312200.0, 1705312210.0, 1705312220.0], "values": [0.52, 0.49, 0.52], "units": "nm/s" } } }' ``` ```json 201 theme={null} [ { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "channel_name": "thickness", "chunk_index": 0, "total_points": 3 }, { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "channel_name": "deposition_rate", "chunk_index": 0, "total_points": 3 } ] ``` # Initialize Tool State Stream Source: https://docs.atomscale.ai/api-reference/tool-state/initialize-stream POST /tool-state/stream/initialize Initialize a new tool state time series stream Creates a new tool state streaming session by initializing data catalogue and processed tool state catalogue entries. Returns a `data_id` to use for subsequent chunk uploads. Number of points per chunk (fixed for the duration of the stream). Must be greater than 0. Human-readable name for the stream Growth instrument ID to link. Must belong to your organization. UUID of an existing physical sample to associate with this stream UUID of a project to associate with this stream ## Response UUID for the newly created stream. Use this ID for subsequent chunk uploads and finalization. UUID for the processed data entry ```python SDK theme={null} from atomscale.streaming import TimeseriesStreamer streamer = TimeseriesStreamer(api_key="YOUR_API_KEY", points_per_chunk=100) data_id = streamer.initialize( stream_name="MBE Run 2024-01-15 Thickness Monitor", physical_sample="GaN-001", project_id="d290f1ee-6c54-4b01-90e6-d701748f0851", ) ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/tool-state/stream/initialize", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "stream_name": "MBE Run 2024-01-15 Thickness Monitor", "points_per_chunk": 100, "project_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab" } ) result = response.json() data_id = result["data_id"] ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/tool-state/stream/initialize" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "stream_name": "MBE Run 2024-01-15 Thickness Monitor", "points_per_chunk": 100, "project_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "physical_sample_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab" }' ``` ```json 200 theme={null} { "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "processed_data_id": "e3a1b2c3-4567-89ab-cdef-234567890abc" } ``` # Get Tool State Timeseries Source: https://docs.atomscale.ai/api-reference/tool-state/timeseries GET /tool-state/{data_id}/timeseries/ Retrieve tool state timeseries data Returns tool state timeseries data, such as instrument sensor readbacks. Data is organized by property name, where each property has its own arrays of timestamps and values, allowing different sampling rates per property. The data entry UUID for the tool state data If `true`, returns `null` instead of an error when no timeseries data exists for this entry Only return data points after this elapsed time (seconds). Useful for incremental fetching during live monitoring. If set, only these property columns are returned. Omit to return all properties. If `true`, skip the data and return a metadata-only response listing the available properties ## Response Mapping of property names to their timeseries data. Each key is a property name (e.g., `thickness`, `temperature`). Array of time values relative to the start of the measurement (seconds) Array of Unix timestamps in milliseconds Array of measurement values Measurement unit for the values (e.g., `nm`, `C`) Maximum `relative_time_seconds` value across all properties ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.get(data_ids="d290f1ee-6c54-4b01-90e6-d701748f0851") tool_state = results[0] # ToolStateResult print(tool_state.timeseries_data) ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/tool-state/d290f1ee-6c54-4b01-90e6-d701748f0851/timeseries/", headers={"X-API-KEY": "YOUR_API_KEY"} ) tool_state_data = response.json() ``` ```bash cURL theme={null} curl "https://api.atomscale.ai/tool-state/d290f1ee-6c54-4b01-90e6-d701748f0851/timeseries/" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} { "properties": { "thickness": { "relative_time_seconds": [0, 10, 20, 30, 40, 50], "unix_timestamp_ms": [1705312200000, 1705312210000, 1705312220000, 1705312230000, 1705312240000, 1705312250000], "values": [0, 5.2, 10.1, 15.3, 20.0, 25.1], "units": "nm" }, "deposition_rate": { "relative_time_seconds": [0, 10, 20, 30, 40, 50], "unix_timestamp_ms": [1705312200000, 1705312210000, 1705312220000, 1705312230000, 1705312240000, 1705312250000], "values": [0.52, 0.49, 0.52, 0.47, 0.51, 0.50], "units": "nm/s" } }, "series_max_time": 50.0 } ``` # Complete Single-Part Upload Source: https://docs.atomscale.ai/api-reference/upload/complete-single-upload POST /data_entries/raw_data/staged/upload_urls/single/complete/ Finalize a single-part staged upload Finalizes a single-part staged upload after the file has been uploaded using the pre-signed URL. This is the single-part companion to the multi-part complete endpoint. Staging bucket type: `core`, `instrument`, or `stream` The server-assigned filename from the get upload URL response ## Response Returns an empty object on success. ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/data_entries/raw_data/staged/upload_urls/single/complete/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "new_filename": "staged_rheed_video.mp4" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/data_entries/raw_data/staged/upload_urls/single/complete/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "new_filename": "staged_rheed_video.mp4" } ) ``` ```json 200 theme={null} {} ``` # Complete Upload Source: https://docs.atomscale.ai/api-reference/upload/complete-upload POST /data_entries/raw_data/staged/upload_urls/complete/ Finalize a multi-part upload Finalizes a multi-part upload after all parts have been uploaded using the pre-signed URLs. You must provide the ETag returned by each part upload. Staging bucket type: `core`, `instrument`, or `stream` The upload session ID from the get upload URLs response The server-assigned filename from the get upload URLs response Array of ETag data for each uploaded part The ETag returned in the response headers when uploading each part The part number (starting from 1) ## Response Returns an empty object on success. ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/data_entries/raw_data/staged/upload_urls/complete/" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "upload_id": "abc123", "new_filename": "staged_rheed_video.mp4", "etag_data": [ {"ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"", "PartNumber": 1} ] }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/data_entries/raw_data/staged/upload_urls/complete/", headers={ "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json" }, json={ "upload_id": "abc123", "new_filename": "staged_rheed_video.mp4", "etag_data": [ {"ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"", "PartNumber": 1} ] } ) ``` ```json 200 theme={null} {} ``` # Get Staged Timeseries Source: https://docs.atomscale.ai/api-reference/upload/get-timeseries GET /data_entries/raw_data/staged/timeseries/{data_id} Retrieve timeseries data from a staged upload Returns timeseries data associated with a staged data entry. This is used for instrument timeseries data that has been uploaded but may not yet be fully processed. The data entry UUID ## Response Returns an array of timeseries channel objects. Unique identifier for this timeseries channel Data entry UUID Channel name Measurement unit for the values Component or sub-channel identifier Array of Unix timestamps (milliseconds) Array of measurement values corresponding to the timestamps ISO 8601 timestamp of last update ```bash cURL theme={null} curl "https://api.atomscale.ai/data_entries/raw_data/staged/timeseries/d290f1ee-6c54-4b01-90e6-d701748f0851" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.get( "https://api.atomscale.ai/data_entries/raw_data/staged/timeseries/d290f1ee-6c54-4b01-90e6-d701748f0851", headers={"X-API-KEY": "YOUR_API_KEY"} ) timeseries = response.json() ``` ```json 200 theme={null} [ { "timeseries_id": "ts_001", "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "system": "scienta", "version": "1.0", "name": "temperature", "unit": "C", "component": "substrate", "unix_times": [1705312200000, 1705312201000, 1705312202000], "values": [550.1, 550.3, 550.5], "last_updated": "2024-01-15T10:30:02Z" } ] ``` # Get Upload URLs Source: https://docs.atomscale.ai/api-reference/upload/get-upload-urls POST /data_entries/raw_data/staged/upload_urls/ Generate pre-signed URLs for multi-part file uploads Generates pre-signed URLs for uploading raw data files to Atomscale using multi-part upload. Each part gets its own pre-signed URL. Original filename with extension (e.g., `rheed_video.mp4`) Number of upload parts to generate URLs for Staging bucket type: `core`, `instrument`, or `stream` Physical sample UUID to associate with the upload Name for a new physical sample to create and associate Notes to attach to the data entry Data stream type (e.g., `rheed`, `optical`, `tool_state`) ## Response Returns an array of pre-signed upload URL objects, one per part. Upload session identifier Server-assigned filename for this part Pre-signed URL for uploading this part (PUT request) Data entry UUID created for this upload Part number ```python SDK theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") # The SDK handles multi-part upload automatically client.upload(files=["rheed_video.mp4"]) ``` ```python Python theme={null} import requests response = requests.post( "https://api.atomscale.ai/data_entries/raw_data/staged/upload_urls/", headers={"X-API-KEY": "YOUR_API_KEY"}, params={ "original_filename": "rheed_video.mp4", "num_parts": 1 } ) upload_urls = response.json() # Upload the file using the pre-signed URL with open("rheed_video.mp4", "rb") as f: requests.put(upload_urls[0]["url"], data=f) ``` ```bash cURL theme={null} curl -X POST "https://api.atomscale.ai/data_entries/raw_data/staged/upload_urls/?original_filename=rheed_video.mp4&num_parts=1" \ -H "X-API-KEY: YOUR_API_KEY" ``` ```json 200 theme={null} [ { "upload_id": "abc123", "new_filename": "staged_rheed_video.mp4", "url": "https://storage.atomscale.ai/uploads/staged_rheed_video.mp4?token=...", "data_id": "d290f1ee-6c54-4b01-90e6-d701748f0851", "part": 1 } ] ``` # MCP Server Source: https://docs.atomscale.ai/mcp-server Access Atomscale from Claude, IDEs, and other AI agents over the Model Context Protocol The [Model Context Protocol](https://modelcontextprotocol.io) (MCP) is an open standard for connecting AI applications to external systems. The Atomscale MCP server lets AI tools search, analyze, and manage your metrology data — RHEED, SEM, XPS, ellipsometry, Raman, photoluminescence, and more — directly, without leaving your assistant. Every request is scoped to your Atomscale account: tools act as **you**, within **your organization**, and respect the same permissions as the web app. ## Endpoint ``` https://api.atomscale.ai/mcp/ ``` The server speaks streamable-HTTP. The trailing slash is required. ## Setup On first connection your client opens a browser to sign in with your Atomscale account, then caches a token for subsequent requests. Headless clients can authenticate with an API key instead (see [Authentication](#authentication)). ```bash theme={null} claude mcp add --transport http atomscale https://api.atomscale.ai/mcp/ ``` Then run `/mcp` inside Claude Code to complete sign-in and list the tools. Go to **Settings → Connectors → Add custom connector**, then enter: ``` https://api.atomscale.ai/mcp/ ``` Approve the access request when prompted. Add to `~/.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "atomscale": { "url": "https://api.atomscale.ai/mcp/" } } } ``` Most MCP clients accept a remote server via `mcpServers` config: ```json theme={null} { "mcpServers": { "atomscale": { "url": "https://api.atomscale.ai/mcp/" } } } ``` Clients that don't support the interactive OAuth flow can send an API key as a Bearer token — see [Authentication](#authentication). ## Authentication Interactive clients sign in through your browser on first connect. There's nothing to configure — approve the access request and the client caches its token. For CI, scripts, or clients without a browser, send your API key as a Bearer token: ```json theme={null} { "mcpServers": { "atomscale": { "url": "https://api.atomscale.ai/mcp/", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` Generate a key from the [Atomscale Dashboard](https://app.atomscale.ai) under **User → Manage Account → Access**. Never commit your API key to version control. Use environment variables or a secrets manager. ## Tools Your assistant chooses which tools to call. Write tools (marked below) prompt for confirmation in most clients before making changes. | Tool | Access | Description | | - | - | - | | `search_data` | Read | Search the data catalogue | | `fetch_data` | Read | Get one data item's metadata by ID | | `fetch_results` | Read | Processed analysis results or AI summary for a data item | | `fetch_embeddings` | Read | Similarity embeddings for a data item | | `search_sample` | Read | Search physical samples | | `fetch_sample` | Read | Get one physical sample by ID | | `list_notebooks` | Read | List your organization's analysis notebooks | | `annotate_data` | Write | Apply tags and/or attach a note to a data item | | `create_notebook` | Write | Create an analysis notebook, optionally seeded with Python ([marimo](https://marimo.io)) source | | `edit_notebook` | Write | Commit a new Python ([marimo](https://marimo.io)) source version to a notebook | | `upload_data` | Write | Get presigned URLs to upload a new file | | `start_stream` | Write | Start a timeseries stream | | `push_stream_chunk` | Write | Push one chunk of points to an active stream | | `stop_stream` | Write | Finalize a stream | ### Streaming Streams let an instrument or agent send timeseries data point-by-point instead of uploading a finished file. `start_stream` opens a stream with a fixed chunk size (`points_per_chunk`) and returns its `data_id`. Push points with `push_stream_chunk`, one call per channel per chunk: * Every chunk must contain exactly `points_per_chunk` points. Only the final chunk before `stop_stream` may be shorter. Chunk length is not validated server-side, so a short chunk leaves a gap and an oversized chunk overwrites the start of the next slice. * `timestamps` are unix epoch seconds and pair 1:1 with `values`. * Chunks may arrive out of order, and retrying a `(channel_name, chunk_index)` pair replaces that slice, so retries are safe. * For multiple channels, call once per channel with the same `chunk_index`. Call `stop_stream` once all chunks are pushed to finalize the stream and trigger processing. A `ping` tool is also available for liveness checks and requires no authentication. # Case Studies Source: https://docs.atomscale.ai/platform/case-studies Validated demonstrations of Atomscale capabilities across deposition processes and characterization methods Real-world results from Atomscale deployments across MBE, CVD, and ALD processes. Each example describes what was demonstrated, the technical setup, measured outcomes, and the process engineering implications. Results shown here reflect actual Atomscale deployments. Specific metrics are reported where available. Customer identities are confidential. *** ## Automatic Detection of Atomic-Scale Defects Atomscale automatically detects and flags killer defects during MBE growth by extracting quantitative atomic spacing measurements from streaming RHEED data in real time. Streaming RHEED video data was ingested from an MBE chamber during epitaxial growth. Atomscale's adapter models transformed raw diffraction pattern images into quantitative time series of atomic spacing values. Anomaly thresholds were established relative to baseline spacing (1x), with deviations beyond 2x triggering automatic alerts. The system identified atomic spacing deviations indicative of defect nucleation and flagged them automatically during active growth runs. This enabled in-process adjustments and run-to-run insight extraction from data that was previously only reviewed qualitatively, if at all. Defect detection during MBE growth has historically depended on visual interpretation of RHEED patterns. Critical defects can nucleate and propagate before the signature is recognized through manual observation. Automated quantitative tracking converts a subjective, attention-dependent task into a systematic one, catching defects that would otherwise reach downstream characterization as unexplained yield loss. > "I have been hoping somebody would be able to do this for 20 years." > > — Staff Scientist *** ## Early Detection: 40 Seconds Ahead of Manual Analysis Atomscale detected a nucleating surface reconstruction 40 seconds earlier than manual observation of the same RHEED data, with higher consistency and no false positives from image processing artifacts. During an MBE growth run, both Atomscale and manual observation were used to monitor live RHEED pattern evolution. Detection timestamps were compared for the onset of a surface reconstruction change. Atomscale processed the streaming diffraction data continuously, while manual observation tracked the same feed under normal working conditions. Atomscale detected the reconstruction change at approximately 41 seconds into the relevant growth phase. Manual observation identified the same transition at approximately 81 seconds, a 40-second difference. Atomscale's detection was consistent across repeated observations with no noise or artifacts from classical image processing. In thin film growth, surface reconstruction transitions signal changes in growth mode, stoichiometry, or defect formation. A 40-second detection advantage represents a meaningful intervention window: time to adjust flux ratios, substrate temperature, or shutter sequences before a defect propagates through subsequent layers. For processes with narrow recipe windows, this is the difference between a correctable deviation and a scrapped run. *** ## In-Process Composition Prediction from Diffraction Data Atomscale's task-specific AI models predicted chemical composition during active growth by correlating real-time RHEED fingerprints with ex-situ XPS composition measurements from previous runs. A training dataset was assembled by pairing RHEED diffraction features captured during growth with post-growth XPS composition measurements from the same samples. Task-specific models were trained to map the real-time diffraction signal to chemical composition values. The models were then evaluated on held-out test samples. Predicted composition values closely tracked measured values across both training and test datasets, with train and test points following the ideal diagonal on a predicted-vs-measured correlation plot. The model operated across a composition range of 0.0 to 0.4, demonstrating accuracy on new samples not seen during training. Chemical composition is typically measured only after deposition is complete, using ex-situ techniques like XPS that add hours or days of latency to the feedback loop. By creating in-situ proxies for ex-situ measurements, Atomscale converts post-hoc quality checks into real-time process signals. This means composition drift can be detected and corrected during growth rather than discovered after the fact, saving the time and cost of lost trials. *** ## Real-Time ALD Feedback from Raw Ellipsometry Atomscale automatically extracted quantitative layer-by-layer comparisons of doped and undoped hafnia films from raw ellipsometry data during ALD, differentiating growth conditions and doping composition without manual analysis. ALD runs of HfO₂ and Zr-doped HfO₂ were deposited on SiO₂ with identical recipes except for substrate temperature (200°C, 250°C, 300°C). Raw in-situ ellipsometry data was ingested by Atomscale and processed into material fingerprints at the resolution of individual deposition cycles. Atomscale's fingerprints clearly differentiated temperature conditions across the undoped HfO₂ runs and showed pronounced divergence when Zr doping was introduced, even within the single-phase regime where surface roughness is the primary distinguishing signal. Layer-by-layer tracking showed the point at which doped and undoped films began to diverge in material state as thickness increased. ALD process development for advanced dielectrics like hafnia depends on precise control of composition and crystallization behavior, which vary with temperature and dopant concentration. Ellipsometry data is collected during nearly every ALD run but is rarely used for real-time decision-making because extracting quantitative comparisons from raw optical data requires significant manual effort. Atomscale automates this extraction, making every ALD cycle a source of actionable process intelligence rather than archived raw data. *** ## MOCVD Outlier Detection for Wafer Uniformity Atomscale trained surrogate models on CVD recipe parameters and sensor data to detect growth outliers resulting in non-uniform wafers, achieving greater than 96% accuracy while tuned for high detection certainty. A model was trained to map MOCVD recipe parameters and instrument sensor logs to ex-situ measurements of wafer uniformity. Uniformity was quantified using a distance metric between photoluminescence and Raman spectra taken at the wafer center and edge. The model was deliberately tuned to favor detection certainty over recall — minimizing false positives at the cost of occasionally missing borderline anomalies. The model achieved greater than 96% accuracy in identifying growth runs that produced non-uniform wafers. This enables real-time drift and outlier detection using data that is already collected during standard MOCVD production runs. Wafer uniformity is a critical yield metric in compound semiconductor manufacturing, but it is typically assessed only after growth is complete, often only after additional characterization steps. A surrogate model that flags uniformity outliers from in-process data allows engineers to catch drifting processes before an entire cassette of wafers is affected. The >96% accuracy at high certainty means alerts are trustworthy enough to act on without requiring manual verification of every flag. *** ## Next Steps Start using Atomscale with your own data. See how to use Atomscale to achieve specific goals. # Ellipsometry Source: https://docs.atomscale.ai/platform/characterization/ellipsometry Timeseries extraction from spectroscopic ellipsometry measurements Ellipsometry analysis imports spectroscopic ellipsometry data and extracts per-wavelength timeseries for psi, delta, depolarization, and incident intensity. It supports batch file uploads and real-time streaming from supported instruments. ## Overview The ellipsometry pipeline processes data in three stages: 1. **Data import**: reads raw instrument text exports, handling single files, directories, and compressed archives 2. **Data structuring**: parses measurements into a normalized table with columns for time, wavelength, psi, delta, depolarization, and incident intensity 3. **Timeseries construction**: pivots the table by metric and wavelength, producing one timeseries per (metric, wavelength) pair ### Key Metrics | Metric | What It Tells You | | - | - | | Psi | Amplitude ratio of p- and s-polarized reflected light, in degrees. Sensitive to film thickness and optical properties. | | Delta | Phase difference between p- and s-polarized reflected light, in degrees. Sensitive to thin film changes. | | Depolarization | Fraction of depolarized light. Indicates surface roughness, thickness non-uniformity, or backside reflections. | | Incident intensity | Intensity of the incident beam at each wavelength. Useful for monitoring source stability. | Each metric is extracted per wavelength, producing one timeseries per (metric, wavelength) pair with property names like `psi_400.5` or `delta_550.2`. ### Adding Ellipsometry Data You can add ellipsometry data in two ways: uploading data files, or streaming live from your instrument. #### Upload Upload ellipsometry data files through the data management page. The pipeline accepts single files, directories of files, and compressed archives (.zip, .tar, .gz, .tgz, .bz2). Analysis begins automatically once the upload completes. ### Viewing Results Once processing completes (or during a live stream), ellipsometry data is displayed in the standard chart view. **Timeseries chart**: The main analysis view. Each timeseries corresponds to a single metric at a single wavelength (e.g., `psi_400.5` or `delta_550.2`). Use the series toggle in the settings drawer to show or hide individual timeseries. **Chart controls**: * **X-axis type**: Switch between relative time and absolute timestamps * **Pan and zoom**: Navigate along the time axis to focus on specific intervals * **Tooltips**: Hover over data points to see values and units * **Multiple charts**: Add additional charts and sync them to compare different metrics side by side ### Analysis Pipeline The ellipsometry pipeline processes data through three stages. #### Stage 1: Data Import Raw ellipsometry text exports are read from two supported instrument formats: | Format | Input Structure | | - | - | | FilmSense | Single text file with embedded wavelength and measurement data | | Woollam | Directory of separate files per parameter (psi, delta, depolarization, reflectance) | The pipeline handles single files, directories, and compressed archives (.zip, .tar, .gz, .tgz, .bz2). #### Stage 2: Data Structuring Parsed measurements are assembled into a table with one row per (time, wavelength) observation, containing columns for relative time, wavelength, psi, delta, depolarization, and incident intensity. Rows are sorted by time, then by wavelength. #### Stage 3: Timeseries Construction The structured table is pivoted by metric and wavelength to produce individual timeseries. Each (metric, wavelength) combination yields one timeseries with a property name like `psi_400.5` or `delta_550.2`. Each metric is normalized before downstream processing. #### Streaming When running in stream mode, the pipeline operates incrementally. Only new data points are appended to existing timeseries rather than reprocessing from scratch. ## Related Documentation Use ellipsometry timeseries as part of real-time growth monitoring. Analyze ellipsometry data consistency across a growth. # Characterization Overview Source: https://docs.atomscale.ai/platform/characterization/index Automated analysis workflows for thin film characterization techniques Atomscale provides automated analysis workflows for common thin film characterization techniques. Each workflow processes raw instrument data to extract metrics, identify features, and generate actionable insights. ## In-Situ Techniques Analysis workflows for instruments operating during thin film growth. Surface structure analysis from diffraction patterns and intensity oscillations. Timeseries extraction from spectroscopic ellipsometry measurements. Video segmentation and morphology tracking from optical microscopy. ## Ex-Situ Techniques Analysis workflows for post-growth characterization instruments. Elemental composition analysis from X-ray photoelectron spectroscopy survey spectra. Object segmentation and morphological analysis from electron micrographs. Spectral analysis of photoluminescence emission measurements. Vibrational spectroscopy for material composition and crystal quality. Depth profiling of elemental composition from secondary ion mass spectrometry. ## Common Capabilities All characterization workflows share these features: | Feature | Description | | - | - | | Automated processing | Workflows run automatically when data arrives from connected instruments | | Metric extraction | Key measurements extracted and stored for trending and comparison | | Batch processing | Process historical datasets through the same analysis pipelines | | SDK access | Retrieve results programmatically via the Python SDK | | Export | Download a ZIP with the original files and processed results (JSON per data type, CSV for tool logs) | ## Getting Started Upload files with **Add Data**, start a live session from **Monitor** > **Monitor New Growth**, or connect instruments through the [desktop app](/platform/reference/desktop-app) integrations. See [Connect](/platform/get-started/connect) for details. Analysis runs automatically when data arrives. To change settings for a data item, open its **Configuration** drawer (for techniques that have one), or use **Actions** > **Reanalyze** to re-run analysis. Set up alerts for metrics that matter to your process. See [Detect Anomalies During a Run](/platform/guides/detect-anomalies) for alert options. Run a test analysis and confirm outputs match expectations before relying on the workflow in production. ## Related Documentation Use characterization alerts as part of real-time monitoring. Access analysis results programmatically and build custom pipelines. # Optical Images Source: https://docs.atomscale.ai/platform/characterization/optical-images Video segmentation and morphology tracking from optical microscopy Optical image analysis automatically segments features from sequences of optical microscopy images and tracks how their shape evolves over time. The pipeline uses a foundation segmentation model to identify regions of interest in each frame, then computes morphological metrics across the full sequence. ## Overview The optical image pipeline processes data in three stages: 1. **Frame normalization**: corrects for haze, uneven illumination, and low contrast 2. **Video segmentation**: a deep learning model segments features frame-by-frame, maintaining temporal consistency across the video 3. **Metric computation**: morphological properties are measured for each segmented feature per frame, then tracked as timeseries with statistical anomaly scoring ### Key Metrics | Metric | What It Tells You | | - | - | | Perimeter | Arc length of segmented feature boundaries, in pixels. | | Circularity | How close a feature's shape is to a perfect circle, computed as P²/(4πA). A value of 1.0 indicates a perfect circle. | | Edge roughness | Deviation of the feature boundary from its convex hull, as a fraction of the convex hull perimeter. Higher values indicate more irregular edges. | | Hausdorff distance | Maximum boundary displacement between consecutive frames, in pixels. Captures the largest local shape change between frames. | ### Adding Data Optical image data is a sequence of still images (.png, .jpg, .tif, or .bmp), with each image treated as one frame. Analysis runs incrementally: each time new images arrive for a data item, the new frames are analyzed and appended to the existing results. ### Viewing Results Once processing completes, the workspace shows several sections: **Video player**: Scrub through the image sequence frame by frame. The images are assembled into a video for playback, and the player displays metadata including FPS and total frame count. **Analysis region**: Shows the segmentation overlay on the current frame, highlighting the regions being tracked by the pipeline. **Timeseries chart**: Plots morphological metrics (perimeter, circularity, edge roughness, Hausdorff distance) across all frames. After the first 32 frames, each metric also gets three anomaly-scoring series that flag frames with unusual shape changes: a z-score, an EMA z-score, and an anomaly probability. These appear as separate series, for example **Perimeter Px Z Score**, **Perimeter Px Ema Z Score**, and **Perimeter Px Anomaly Probability**. A status badge indicates pipeline progress. The refresh button (**Refresh content**) reloads the latest results but doesn't re-run analysis. To re-run analysis, use **Actions** > **Reanalyze**. ### Analysis Pipeline The optical image pipeline processes image sequences through three stages. #### Stage 1: Frame Normalization Raw frames are corrected for uneven illumination and low contrast, and feature boundaries are enhanced to improve segmentation quality. #### Stage 2: Video Segmentation A deep learning segmentation model processes frames sequentially, producing instance masks that track features across time. For long videos, frames are processed in batches with overlap to maintain tracking consistency at batch boundaries. #### Stage 3: Metric Computation For each segmented feature in each frame, the pipeline computes perimeter, circularity, edge roughness (boundary deviation from the convex hull), and Hausdorff distance (maximum boundary displacement between consecutive frames). Once 32 frames of history are available, each metric is scored for every new frame: a z-score against the metric's running mean and standard deviation, an EMA z-score against an exponentially weighted moving average, and an anomaly probability derived from the larger of the two. # Photoluminescence Source: https://docs.atomscale.ai/platform/characterization/photoluminescence Spectral analysis of photoluminescence emission measurements Photoluminescence (PL) analysis processes spectroscopy files, reads acquisition settings from filenames, and normalizes emission intensity by laser power, exposure, and accumulation count. This makes spectra from different measurement conditions directly comparable. ## Overview The PL pipeline processes data in three stages: 1. **File parsing**: reads the spectral axis and intensity from uploaded .txt or .csv spectroscopy files, converting wavelength (nm) data to photon energy (eV) 2. **Metadata extraction**: reads laser power, exposure time, accumulation count, and excitation wavelength from the filename, along with descriptive details such as sample identity and measurement location 3. **Intensity normalization**: divides intensity by the product of power and exposure (and accumulation count, when present) so that spectra taken under different conditions can be compared on equal footing ### Key Metrics | Metric | What It Tells You | | - | - | | Energy | Spectral position of emission features, in eV. Wavelength (nm) data is converted to eV. | | Intensity | Emission strength. When power and exposure are known, intensity is normalized by acquisition parameters. Otherwise, it is not normalized. | ### Adding Data Upload .txt or .csv spectroscopy files through the data page. Each file should contain an energy (eV) or wavelength (nm) column and an intensity column. Processing starts automatically once the upload completes. The pipeline reads acquisition settings from the filename. Including laser power and exposure time in the filename enables automatic intensity normalization. Write each setting as a number followed by its unit, separated from other text by spaces, underscores, or hyphens: | Setting | Format | Example | | - | - | - | | Laser power | Number + `mW`, `µW` (or `uW`), `nW`, or `W` | `2mW` | | Exposure time | Number + `s`, `sec`, `ms`, or `min` | `30s` | | Accumulation count | 1 to 3 digits + `a`, `ac`, or `acc`, or `x` + digits | `10ac` or `x2` | | Excitation wavelength | Common laser line + `nm` | `532nm` | For example, `SampleA_center_532nm_2mW_30s_10ac.txt` is read as 2 mW, 30 s, 10 accumulations, and 532 nm excitation. The pipeline also reads descriptive details such as sample ID and measurement location from the filename when it can. An `x` + digits token is read as an objective magnification instead when the number is a common objective (4, 5, 10, 20, 40, 50, 60, 63, 100, or 150). For those accumulation counts, use the `ac` form, such as `10ac`. ### Viewing Results Once processing completes, the workspace displays a spectrum chart plotting energy vs. intensity. A status badge indicates pipeline progress. If the filename does not include both laser power and exposure time, the spectrum displays unnormalized intensity. The chart doesn't show whether normalization was applied; the `normalization_applied` field in the [API results](/api-reference/spectroscopy/photoluminescence) indicates it. API only ### Analysis Pipeline The PL pipeline processes each uploaded file through three stages. #### Stage 1: File Parsing The file is parsed into a spectral axis and intensity. Tab-, comma-, and semicolon-delimited files are supported. Multi-column map or line-scan files are reduced to a single representative spectrum (the median across positions when there are three or more). Wavelength (nm) data is converted to photon energy (eV), with a Jacobian correction applied to the intensity so that the spectral density is preserved. Spectra are placed on a common energy grid from 0.5 to 4.0 eV. #### Stage 2: Metadata Extraction The pipeline reads laser power, exposure time, accumulation count, and excitation wavelength from the filename using the naming convention described in the Guide tab, along with descriptive details such as sample identity and measurement location. Including laser power and exposure time in the filename enables automatic intensity normalization. #### Stage 3: Intensity Normalization When both power and exposure are present in the extracted metadata, intensity is normalized by dividing by their product. If an accumulation count is also present, it is included in the divisor. Unit conversions (e.g., uW to mW, ms to seconds) are applied automatically. If power or exposure is missing, normalization is skipped. # Raman Source: https://docs.atomscale.ai/platform/characterization/raman Vibrational spectroscopy for material composition and crystal quality Raman analysis processes spectroscopy files, reads acquisition settings from filenames, and corrects each spectrum before detecting peaks. Spectra are stored on a common Raman shift axis so that results from different files can be compared directly. ## Overview The Raman pipeline processes data in three stages: 1. **File parsing**: reads the spectral axis and intensity from uploaded .txt or .csv spectroscopy files 2. **Metadata extraction**: reads laser power, exposure time, accumulation count, and excitation wavelength from the filename, along with descriptive details such as sample identity and measurement location 3. **Spectrum processing**: removes cosmic-ray spikes, normalizes intensity by laser power and exposure (when known), calibrates the shift axis against silicon (when a silicon peak is present), subtracts the background, and detects peaks ### Key Metrics | Metric | What It Tells You | | - | - | | Raman shift | Spectral position in cm⁻¹. | | Intensity | Scattering intensity after spike removal and background subtraction, normalized by laser power and exposure when known. | ### Adding Data Upload .txt or .csv spectroscopy files through the data page. Each file should contain a Raman shift column and an intensity column. Processing starts automatically once the upload completes. To enable intensity normalization, include the laser power and exposure time in the filename, for example `SampleA_center_532nm_2mW_30s_10ac.txt`. See [Photoluminescence](/platform/characterization/photoluminescence) for the full filename convention. ### Viewing Results Once processing completes, the workspace displays an interactive spectrum chart. **Region filter**: Use the **Regions** dropdown above the chart to show or hide detected peaks. Each **Detected Peak** is shown as a shaded band on the chart. **Series filter**: Use the **Series** dropdown to show or hide the intensity trace. A configuration drawer is accessible via the gear icon, though no configuration options are available yet. ### Analysis Pipeline The Raman pipeline processes each uploaded file through three stages. #### Stage 1: File Parsing The file is parsed into a spectral axis and intensity. Tab-, comma-, and semicolon-delimited files are supported. Multi-column map or line-scan files are reduced to a single representative spectrum (the median across positions when there are three or more). If the axis is in nm, it is converted to Raman shift when the excitation wavelength is known from the filename; otherwise the spectrum is stored on its original axis. #### Stage 2: Metadata Extraction The pipeline reads laser power, exposure time, accumulation count, and excitation wavelength from the filename, along with descriptive details such as sample identity and measurement location. #### Stage 3: Spectrum Processing * **Despiking**: cosmic-ray spikes are removed from the spectrum. * **Power and exposure normalization**: when both laser power and exposure time are found in the filename, intensity is divided by their product (and by the accumulation count, when present). If either is missing, intensity is not normalized. * **Shift calibration**: when the spectrum contains a clear silicon peak within 15 cm⁻¹ of 520.7 cm⁻¹, the axis is shifted so that the peak lands at 520.7 cm⁻¹. Spectra without a credible silicon peak, or with an offset larger than 10 cm⁻¹, are left uncalibrated. * **Background subtraction**: a SNIP baseline is subtracted to remove fluorescence background. * **Peak detection**: peaks are detected and fitted. Features within 5 cm⁻¹ of zero shift (laser line leakage) are excluded. The corrections that were applied, including whether normalization ran and the calibration shift, are recorded with the results. API only # RHEED Source: https://docs.atomscale.ai/platform/characterization/rheed Surface structure analysis from diffraction patterns and intensity oscillations RHEED (Reflection High-Energy Electron Diffraction) analysis automatically extracts and quantifies diffraction features from RHEED videos and images. It supports stationary stages, rotating stages, and real-time streaming via screen capture. For rotating recordings, the pipeline automatically detects the rotational period, identifies high-symmetry reference frames, and applies per-rotation corrections so that metrics are extracted consistently at each azimuthal angle. ## Overview The RHEED pipeline processes data in three stages: 1. **Data cleaning and standardization**: auto-classifies the recording type (stationary vs. rotating), crops to the illuminated region, corrects detector orientation, and for rotating recordings, detects the rotational period and aligns each rotation cycle to consistent azimuthal positions 2. **Pattern extraction**: segments diffraction features using deep learning models and quantifies morphological metrics per feature 3. **Timeseries analysis**: tracks how extracted metrics evolve across video frames, detects transitions, and identifies changes in surface structure ### Key Metrics | Metric | What It Tells You | | - | - | | Lattice spacing | Derived from reciprocal-space feature spacing; in pixels until calibrated, then in Å. Tracks surface reconstruction changes. | | Strain | Relative change in diffraction feature spacing over time, calculated from horizontal spacing between neighboring patterns. | | Oscillation period | Period of specular intensity oscillations, related to deposition rate. | | Spot count | Number of diffraction features per frame. Changes indicate phase transitions, reconstructions, or target thickness reached. | | FWHM | Full width at half maximum of specular and first-order spots. | | Intensity | Integrated intensity of specular, half-order, and first-order reflections. | For rotating RHEED data, the pipeline automatically detects the rotational period, identifies high-symmetry reference frames, and aligns each rotation cycle before extracting metrics per azimuthal angle. This corrects for variations in rotation speed and ensures equivalent positions are compared consistently across cycles. ### Adding RHEED Data You can add RHEED data in two ways: uploading video files, or streaming live from your RHEED camera display. #### Upload Upload RHEED video files directly through the data management page. Analysis begins automatically once the upload completes. #### Stream Streaming captures your RHEED feed in real time via screen share, with analysis results delivered within seconds. Click **Add Data**, then select **Stream**. Click **Choose Source** and select the window that displays your RHEED camera feed, the same way you would screen-share on a video call. Drag the blue bounding box to frame the RHEED pattern area you want to capture. Enter a name for the stream and any other metadata. If your stage is rotating, enter an approximate rotation rate as a starting point for automatic period detection. Leave the stage rotation field blank for stationary stages. Click **Start Stream**. Analysis begins immediately and results appear in the workspace as frames are processed. During a stream, click **Stream Settings** in the workspace to **Pause**, **Resume**, or **End Stream**. You can pause only from the computer that is capturing the stream, and resuming requires selecting a capture source. Other users viewing the stream see a **Stop** button instead, which ends the stream after confirmation. ### Viewing Results Once processing completes (or during a live stream), the RHEED workspace shows several sections: **Video player**: Scrub through the recorded or live video. Reference frame positions are highlighted on the scrub bar for rotating recordings. You can jump to a specific frame number or drag the scrubber to isolate a moment in time. **Growth analysis chart**: The main analysis view. Select which timeseries to plot (lattice spacing, strain, intensity, FWHM, spot count, oscillation period) and choose the azimuth angle for rotating data. Changepoints appear as shaded bands when the detected surface reconstruction changes (see Technical Details). **Saved patterns**: Extract individual frames for detailed fingerprint analysis. Click **Save Frame** to extract the current video frame. Each saved frame shows the fingerprint overlay with extracted diffraction features, intensity maxima positions, and horizontal spacing between the specular spot and nearest features. ### Azimuth Calibration For rotating RHEED recordings, the pipeline automatically detects the rotational period and identifies high-symmetry reference frames. You can refine these in the azimuth calibration panel: * **Label azimuths** with crystal orientation names (e.g., `Al[100]`) * **Unit Calibration**: enter a spacing in pixels (**Relative Units**) and the known distance it corresponds to (**Real Units**, in nm, Å, or px). Lattice spacing is then reported as a real-space value in Å instead of pixels. ### Reanalysis If you need to adjust analysis parameters, open the workflow configuration drawer to update reference frames or rotational period, then re-run the pipeline. ### Comparing Patterns Save multiple frames and use the pattern comparison tools to view fingerprint overlays side by side or generate difference masks between two patterns. This is useful for identifying subtle changes in surface structure across a growth run. ### Analysis Pipeline The RHEED pipeline processes data through three stages, followed by transition detection. #### Stage 1: Data Cleaning and Standardization Raw RHEED recordings are prepared for analysis. The pipeline auto-classifies the recording as stationary or rotating, crops to the illuminated pattern region, corrects detector orientation, and normalizes intensity across recordings. For rotating recordings, the pipeline automatically detects the rotational period, identifies high-symmetry reference frames within each rotation cycle, and aligns frames across cycles to correct for variations in rotation speed. This ensures that equivalent azimuthal positions are compared consistently when building per-angle timeseries. #### Stage 2: Pattern Extraction Diffraction features are segmented from each frame using deep learning models that separately identify streaks and spots. For each extracted feature, morphological metrics are measured, including FWHM, eccentricity, area, centroid position, and position relative to the specular spot. #### Stage 3: Timeseries Analysis For video data, the pipeline tracks how extracted metrics evolve across frames. For rotating data, frames are grouped by azimuthal angle so each metric is tracked per orientation. For stationary data, frames are sampled at regular intervals. **Computed metrics**: strain (relative change in feature spacing), lattice spacing (converted from reciprocal-space measurements), intensity (specular, half-order, and first-order reflections), FWHM, oscillation period, spot count, and per-feature shape properties. #### Transition Detection Changepoint detection is temporarily disabled. New runs won't generate changepoints until it's turned back on. This section describes how detection works when it's enabled. The pipeline flags a changepoint when the detected surface reconstruction changes, for example from 2x1 to 4x1, or between 2x1 and no detected reconstruction. Changepoints appear as shaded bands on the growth analysis chart, colored by severity: **High**, **Moderate**, or **Low**. Reconstruction changes are flagged as **Moderate**, but the chart shows only **High** changepoints by default. To show other severities, click the gear icon (**Chart settings**) next to **Growth Analysis**, then turn on **Moderate** or **Low** under **Changepoints**. #### Streaming Pipeline For real-time streaming, the pipeline operates incrementally. Only new frames are processed and appended to existing timeseries rather than recomputed from scratch. ## Related Documentation Use RHEED metrics as part of real-time growth monitoring. Analyze RHEED data consistency across a growth. # SEM Source: https://docs.atomscale.ai/platform/characterization/sem Object segmentation and morphological analysis from electron micrographs SEM (Scanning Electron Microscopy) analysis automatically detects and segments objects from micrographs, then extracts morphological features for each object. The model provides per-object confidence scores, and unreliable detections are filtered out before results are presented. ## Overview The SEM pipeline processes data in four stages: 1. **Image preparation**: detects and removes the metadata banner, reads the scalebar for spatial calibration 2. **Segmentation**: detects and segments individual objects using a deep learning model with uncertainty estimation 3. **Feature extraction**: measures morphological properties per object, including size, shape, and spacing metrics 4. **Filtering**: removes low-confidence detections and flags objects near the image border ### Key Metrics | Metric | What It Tells You | | - | - | | Equivalent diameter | Average object size derived from major and minor axis lengths. | | Circularity | Shape regularity (4πA/P²). A value of 1.0 indicates a perfect circle. | | Eccentricity | How elongated an object is. Values near 0 are circular; values near 1 are highly elongated. | | Solidity | Ratio of object area to its convex hull area. Lower values indicate more concave or irregular shapes. | | Nearest neighbor distance | Spacing between objects, measured both centroid-to-centroid and edge-to-edge. | | Mean uncertainty | Segmentation confidence for each object. Used to filter unreliable detections. | ### Adding Data Upload SEM images (PNG or TIF) through the data page. Analysis begins automatically once the upload completes. ### Viewing Results Once processing completes, the workspace shows several sections: **Image viewer**: Displays the SEM image with a segmentation fingerprint overlay highlighting detected objects. **Outlier detection**: An interactive feature analysis view. Use the facet dropdown to switch between groups of related features (size, shape, spacing, etc.). Each feature is displayed as a histogram with objects classified as inliers or outliers: * **Green**: outlier (low end) * **Blue**: inlier * **Red**: outlier (high end) Drag the boundary lines on any histogram or enter numeric values to adjust the outlier thresholds. Bounds are saved per browser automatically. ### Configuration Open the configuration drawer (gear icon) to set spatial calibration. Enter the pixel distance and corresponding real-world distance with units to convert measurements from pixels to physical units. You can save and load workflow configuration presets from the dropdown at the top of the drawer. ### Analysis Pipeline The SEM pipeline processes each uploaded image through four stages. #### Stage 1: Image Preparation The pipeline automatically detects and crops the instrument metadata banner, then reads the scalebar to establish spatial calibration. If calibration is already present in the image metadata (e.g., from a TIF header), scalebar detection is skipped. #### Stage 2: Segmentation The image is divided into overlapping patches and each patch is processed by a deep learning segmentation model that produces both an object mask and a per-pixel uncertainty map. Patches are then stitched back together into a full-image segmentation. #### Stage 3: Feature Extraction For each segmented object, the pipeline computes morphological properties including area, perimeter, axis lengths, eccentricity, solidity, and orientation. Derived metrics are calculated from these base measurements: circularity, equivalent diameter, mean uncertainty, mean intensity, and nearest neighbor distances (centroid-to-centroid and edge-to-edge). #### Stage 4: Filtering Objects are grouped by segmentation uncertainty, and high-uncertainty detections are removed. Objects near the image border are flagged since they may be only partially visible. # SIMS Source: https://docs.atomscale.ai/platform/characterization/sims Depth profiling of elemental composition from secondary ion mass spectrometry SIMS (Secondary Ion Mass Spectrometry) analysis parses depth profile data from Excel workbooks or delimited text files, extracting concentration-vs-depth curves for each detected ionic species. The pipeline automatically identifies depth and species columns, detects measurement units from column headers, and produces aligned depth profile arrays. ## Overview The SIMS pipeline processes data in three stages: 1. **File reading**: for Excel files, locates the correct worksheet by looking for a "Processed data" sheet or falling back to the first sheet with numeric data. For text files, finds the header row and skips any instrument preamble 2. **Column identification**: pattern-based matching separates the depth column from species concentration columns, and detects units from the column headers 3. **Data extraction**: produces aligned depth and concentration arrays for each species, with metadata recording the species detected and units used ### Key Metrics | Metric | What It Tells You | | - | - | | Depth | Sputter depth or time into the sample. Reported in nm, um, or seconds depending on the measurement. | | Concentration | Elemental abundance at each depth point. Units depend on the instrument output: at/cm³, at%, or wt%. | | Species | The ionic species detected in the measurement. Each species produces a separate depth-concentration profile. | ### Adding Data Upload depth profile data as Excel workbooks (.xlsx or .xls) or as delimited text (.txt, .csv, or .dat, separated by tabs, commas, or semicolons) through the data page. Processing starts automatically once the upload completes. The pipeline expects a worksheet with a depth column and one or more numeric columns for species concentrations. Column headers should include unit information (e.g., "Depth (nm)", "Si (at/cm³)") for automatic unit detection. ### Viewing Results SIMS results aren't displayed in the Atomscale app; the data item page shows "This file cannot be analyzed but is stored." Once processing completes, retrieve the depth profiles through the API: [Get SIMS Data](/api-reference/sims/get) returns one depth-concentration profile per detected species, and [Get SIMS Species Profile](/api-reference/sims/get-species) returns the profile for a single species. If the automatic unit detection picks the wrong units, you can override depth and concentration units in the pipeline inputs (`depth_units`, `concentration_units`) when you re-run analysis through the API. ### Analysis Pipeline The SIMS pipeline processes each uploaded file through three stages. #### Stage 1: File Reading The parser detects whether the file is an Excel workbook or delimited text from its contents. For Excel files, it locates the correct worksheet, looking for a processed data sheet or falling back to the first sheet with numeric data. For text files, it takes the column headers from the line just above the first numeric row, so vendor preamble lines are skipped. #### Stage 2: Column Identification Columns are automatically classified as depth or species concentration based on header text. Units (nm, um, s for depth; at/cm³, at%, wt% for concentration) are detected from column headers. Both depth and concentration units can be overridden via pipeline inputs if automatic detection is incorrect. #### Stage 3: Data Extraction The pipeline cleans the data and produces aligned depth and concentration arrays for each species. # XPS Source: https://docs.atomscale.ai/platform/characterization/xps Elemental composition analysis from X-ray photoelectron spectroscopy survey spectra XPS (X-ray Photoelectron Spectroscopy) analysis uses a machine learning model to estimate elemental composition from survey spectra produced by an Al K-alpha source. Composition is inferred from the entire spectrum signature rather than from peak fitting. The pipeline also computes a Shirley background and per-line peak areas for detected peaks, which are returned with the results. API only ## Overview The XPS pipeline processes data in four stages: 1. **Data ingestion**: parses uploaded spectrum files, detects energy type (kinetic vs. binding), applies transmission correction for VAMAS files, and charge-corrects the binding energy axis to adventitious carbon 2. **Pre-processing**: normalizes and resamples the spectrum to a standard grid for the model 3. **Composition inference**: an ML model predicts elemental composition and uncertainty from the full spectrum signature, without peak fitting 4. **Peak matching**: detected spectral peaks are matched to known experimental binding energies and assigned element and orbital labels ### Key Metrics | Metric | What It Tells You | | - | - | | Predicted composition | Elemental atomic percentages estimated by the ML model, with uncertainty from Monte Carlo dropout. | | Detected peaks | Binding energy positions of identified peaks, each labeled with element and orbital (e.g., "O 1s"). | | Predicted formula | Stoichiometric formula derived from the predicted composition (e.g., "Al2O3"). | ### Adding XPS Data Upload XPS survey spectrum files through the data management page. Supported formats: VAMAS (.vms), PHI SPE (.spe), and text (.txt, .dat). Analysis begins automatically once the upload completes, and results are typically ready within 10-30 seconds. ### Viewing Results Once processing completes, the XPS workspace shows two main sections: **XPS spectrum chart**: Plots intensity versus binding energy. Detected peaks appear as highlighted regions labeled with element and orbital assignments (e.g., "C 1s", "O 1s"). You can toggle the visibility of the intensity series, peak highlight regions, and identification labels. Zoom on the binding energy axis to inspect specific regions. **Element composition chart**: Bar chart showing predicted atomic percentage for each element, with error bars representing the model's uncertainty. Toggle the presence and uncertainty series independently. If the spectrum covers less than 50% of the composition model's energy range (roughly 0-950 eV binding energy for Al K-alpha), which in practice means region scans, the chart shows "Composition not reported" instead of a composition. Detected peaks still appear on the spectrum chart. ### Configuring Element Constraints By default, the pipeline runs in unconstrained mode, predicting which elements are present and their concentrations. You can constrain the analysis to a specific set of elements for improved accuracy. Click the configuration icon next to the workflow status indicator. Click cells in the interactive periodic table to select or deselect elements. You can also load a previously saved configuration from the dropdown. Click **Re-analyze with Configuration** to run the pipeline with your selected elements. Results update in about 10 seconds. When the initial composition is predicted (unconstrained), constraining to known elements typically improves accuracy. The configuration drawer indicates when the current composition is predicted rather than confirmed. ### Reanalysis If you need to adjust element selections, open the configuration drawer, update your choices, and click re-analyze. Each re-analysis replaces the previous results. ### Analysis Pipeline The XPS pipeline processes data through four stages. #### Stage 1: Data Ingestion Raw spectrum files are parsed into energy and intensity arrays. Supported formats are VAMAS (.vms), PHI SPE (.spe), and plain text (.txt, .dat). The parser auto-detects whether data is in kinetic or binding energy and converts as needed, assuming an Al K-alpha source (1486.6 eV) by default. **Charge correction**: by default, the binding energy axis is shifted so that the adventitious carbon C 1s peak sits at 284.8 eV. The pipeline searches for the C 1s peak between 278 and 298 eV and skips the correction if the strongest candidate has a signal-to-noise ratio below 5. Charge correction can't be turned off in the app. The applied shift is recorded with the results. API only #### Stage 2: Pre-processing The spectrum is normalized, resampled to a standard grid, and optionally filtered to reduce noise and remove inelastic background signal. This standardizes inputs across different instruments and acquisition settings. #### Stage 3: Composition Inference An ML model estimates elemental composition from the pre-processed spectrum. The model uses the full spectrum signature rather than fitting individual peaks, which makes it robust to overlapping peaks and noisy data. Composition is withheld when the spectrum covers less than 50% of the model's energy grid (roughly 0-950 eV binding energy for Al K-alpha). Region scans fall well below this threshold, so they report peaks but no composition. **Two modes of operation**: * **Unconstrained**: the model predicts which elements are present and their concentrations. Elements with concentrations below a threshold are excluded. * **Constrained**: the user provides a list of elements, and the model predicts concentrations only for those elements. The model provides uncertainty estimates (mean and standard deviation) for each element's predicted concentration. **Training data**: the model is trained on a large corpus of labeled spectra combining expert-labeled experimental references and physically simulated spectra that account for background interactions, noise, and peak broadening. **Supported elements**: periods 1-6, excluding H, He, noble gases (Ne, Ar, Kr, Xe), Pm, and all period 7+ elements. #### Stage 4: Peak Matching Spectral peaks are detected and matched to known experimental binding energies, then labeled with element symbol and orbital type (e.g., "Al 2p", "O 1s"). A stoichiometric formula is generated from the predicted composition. For each detected peak, the pipeline also integrates the peak area above a Shirley background. These areas are returned with the results but aren't shown in the app, and they don't feed the composition, which comes from the ML model. API only ## Related Documentation Use XPS composition data as part of quality control. Optical characterization to complement XPS surface analysis. # Act Source: https://docs.atomscale.ai/platform/get-started/act Turn real-time process intelligence into decisions, interventions, and control The final step in the Atomscale workflow is acting on the information extracted from your process data. The [Analyze](/platform/get-started/analyze) step gives you the insights: similarity trajectories, derived metrics, and within-run uniformity. This page covers what you do with those insights: detecting problems automatically, deciding how to respond, and progressively closing the loop toward automated control. ## Alerts and Changepoint Detection Atomscale's alerting system operates on our derived intelligence layer that holistically profiles growths, allowing them to catch problems that are invisible to conventional monitoring that operates on fixed combinations of sensors. Traditional alerting operates on tool state: temperature out of range, pressure spike, flow rate deviation. Atomscale alerts operate on **materials state**: the derived metrics that describe what's actually happening to the film being deposited. | Traditional Alerting | Atomscale Alerting | | - | - | | Temperature exceeds setpoint | Predicted composition drifting from target | | Pressure outside range | Surface reconstruction deviating from expected sequence | | Flow rate drop | Run trajectory diverging from process fingerprint library | | Manual RHEED observation | Quantitative lattice spacing or spot count anomaly | Atomscale's anomaly detection has demonstrated **greater than 96% accuracy** and can flag issues **40 seconds earlier** than manually monitoring the same process visually. ### Types of Alerts Atomscale continuously scores each run against your process fingerprint library. When a run's trajectory diverges from expected behavior, an alert fires, even if every individual tool parameter looks normal. This is particularly valuable for catching subtle defects like compositional drift or surface quality degradation that don't manifest in any single sensor reading. Beyond single-run anomalies, Atomscale tracks trends across runs. If your process is gradually shifting (for example, source depletion causing a slow composition drift over days or weeks), drift alerts flag the trend before any individual run fails. For specific derived metrics, you can set explicit thresholds. Unlike raw parameter thresholds (which any SCADA system handles), these operate on Atomscale's extracted quantities: * Predicted composition outside target range * Surface roughness estimate exceeding specification * Lattice spacing deviating from expected value ### Setting Up Alerts Select from changepoint detection (automatic, model-driven), drift tracking (cross-run trending), or threshold-based (explicit limits on derived metrics). Specify which data streams, runs, or recipes the alert applies to. Alerts can be scoped to a specific tool, material system, or recipe family. Assign a severity level (**Info**, **Warning**, or **Critical**) and configure how you're notified: in-app, email, or webhook for integration with external systems. Start with conservative thresholds and tighten as you build confidence in normal process variation. Atomscale provides alert history to help you calibrate sensitivity and reduce false positives. ## Respond to Anomalies When an alert fires, the next step is deciding what to do. Atomscale supports a range of responses depending on the severity and your integration level. ### Operator-in-the-Loop For most teams starting with Atomscale, the initial workflow is operator-assisted: Atomscale detects and surfaces the issue, and a human decides what to do. Click through from an alert to the Analyze view. Compare the flagged run against reference runs to understand exactly what diverged and when. Log observations and decisions directly on the run timeline. These annotations feed back into your process library for future reference. Use the alert context to decide on recipe adjustments: modify setpoints, extend or shorten growth stages, or compensate for detected drift. For critical anomalies, decide to terminate a run early rather than wasting time and materials on a doomed process. ### Run Disposition After a run completes, Atomscale's analysis helps with go/no-go decisions before committing to downstream processing or characterization. Rather than waiting days or weeks for ex-situ measurement results, you can use in-process predictions to triage runs immediately. ## Integrating Process Control For teams ready to move beyond operator-assisted monitoring, Atomscale supports progressively tighter integration with AI-driven process control. Closed-loop control requires careful validation. Most teams start with monitoring and alerting, build confidence in Atomscale's predictions, and then incrementally close the loop. The integration roadmap below reflects this progression. ### Integration Levels Atomscale observes and notifies. All decisions and actions are made by operators. This is where most teams start. Atomscale suggests specific adjustments based on detected deviations. For example, recommending a flux correction when predicted composition drifts from target. Operators review and execute. Atomscale sends adjustment commands to the process controller via SECS/GEM or tool API, with operator approval required before execution. Atomscale continuously monitors and adjusts the process in real-time without operator intervention. The system optimizes for consistency of output (the target material properties) rather than consistency of tool setpoints. ### Agent-Based Process Control At the most advanced integration level, Atomscale's AI agent acts as a process copilot. The agent combines real-time derived metrics with the full process fingerprint library to make context-aware decisions. Key capabilities include: * **Real-time recipe adjustment**: Modify growth parameters mid-run to correct for detected deviations, such as adjusting source temperatures to maintain target composition. * **Natural language customization**: Configure agent behavior and decision context using natural language instructions, adapting the scope and sensitivity of automated control without code changes. * **Audit trail**: Every automated action is logged with full context: what was detected, what the agent decided, and what command was sent. This provides complete traceability for process validation. ## Track and Review Acting on process data is only valuable if you can measure whether those actions improved outcomes. Atomscale provides tools to close the learning loop. ### Run Reports Each run generates a summary that includes: Derived metrics timeline with key events and transitions highlighted Comparison to target values and reference baselines Any anomalies detected and actions taken (manual or automated) Annotations and operator notes ### Process Improvement Tracking Over time, track how interventions, whether manual adjustments informed by Atomscale or automated corrections, affect yield and process stability. This historical record is what transforms individual run insights into systematic process improvement. ## Next Steps You've completed the getting started guide! Continue exploring: Detailed guide on configuring anomaly detection for active runs. Compare runs and understand what changed when results differ. Set up monitoring and analysis for specific characterization methods. Programmatic access to Atomscale's analysis and control capabilities. # Analyze Source: https://docs.atomscale.ai/platform/get-started/analyze Compare runs, track active growths, and extract process understanding from your data Once your data is connected to Atomscale, the next step is analyzing it for information. The Analyze step turns raw process data into actionable insights by answering two core questions: 1. **How is this run the same or different from previous runs?** Compare any run against your full process history using learned similarity embeddings, instead of parameter-by-parameter matching. 2. **How uniform is this run internally?** Assess consistency within a single run, from whole-recipe sequences down to individual atomic layers and segment-to-segment variation. Everything in this section feeds directly into the [Act](/platform/get-started/act) step, where the metrics, comparisons, and similarity scores here power real-time alerts, anomaly detection, and process control. ## Exploring Run Similarity Comparing successful and failed runs is one of the most challenging tasks in process engineering. ***Run-to-run similarity*** uses learned embeddings to compare runs across their full process signatures, surfacing relationships that individual parameters wouldn't reveal. ### How It Works In the Explore Similarity page, choose a metric, transformation, and time scale window for the similarity calculation. These settings control what aspects of the process signature are used for comparison. A 2D scatter plot shows your data items positioned by similarity. Runs that were similar cluster together, while runs that diverged are far apart. Points are colored by the selected metric value. Click any point and select View Similar Data to see ranked matches with similarity scores. Filter results by project, physical sample, or tags to focus your comparison. ### When to Use This Run-to-run similarity is useful when you need to diagnose an unexpected result. Instead of manually comparing numerous parameter logs, you can start from a problem run and immediately find its closest matches, both good and bad, to isolate what changed. It's also valuable for process transfer: when bringing up a recipe on a new tool, similarity analysis shows you how closely the new tool's behavior matches the original across the full process fingerprint rather than just a handful of setpoints. ## Real-Time Monitoring During a live run, the **Monitor** page lets you watch a growth unfold in real time. This makes analysis actionable by following the process as it happens instead of waiting for the run to finish. ### Starting a Session On the Monitor page, click Monitor new growth and pick an entry point: capture a screen or window from your browser, or stream programmatically via the Atomscale Python SDK. Once the stream starts, the live session view opens automatically. RHEED video, growth metrics, and tool state update continuously as data arrives. ### Reading the Session The live session view shows three synchronized panels: * **RHEED Video**: the live diffraction stream, with one panel per azimuth. * **Growth Metrics**: time series of derived RHEED metrics as the growth progresses. This shows you the specific metrics that are changing. * **Tool State**: instrument parameter logs during the growth, providing context for changes in the growth metrics. During active streaming, all panels refresh automatically. A latency indicator shows how current the displayed data is. See the [Monitoring reference](/platform/reference/monitoring/index) for a full walkthrough of the Monitor page. ## Assess Within-Run Uniformity **Explore Similarity** can be used with segments of a single run to assess how consistent it is internally, identifying variation between segments, layers, or across different regions of the process timeline. This catches issues that run-to-run analysis alone can miss: a growth that looks acceptable overall but has early-stage drift, mid-run excursions, or layer-to-layer inconsistency that will affect device performance. ## Visualize and Explore Whenever you open a data item, physical sample, or project, Atomscale displays interactive charts for the associated data. These charts show both raw tool parameters and Atomscale's derived metrics all synchronized on the same timeline. Open chart settings to choose which series to display. Series are organized by data stream (RHEED, optical, metrology, etc.), then by series type (e.g., specular intensity, lattice spacing), then by individual data item. Toggle series individually or in bulk. Right-click a chart to add or remove additional charts, each with independent axis configuration. Configure the x-axis type per data stream (e.g., timestamp, relative time, frame number). Tooltips and zoom synchronize across charts that share the same x-axis type, so you can correlate events across different data streams. Series from live data sources update automatically. During active streaming, charts refresh continuously to show incoming data. The same derived metrics powering real-time monitoring and alerting are visible here in their raw time-series form. ## Next Steps The analysis capabilities on this page (similarity scoring, uniformity assessment, and real-time monitoring) are the foundation for everything in the Act step. The same models that let you compare runs historically also power real-time anomaly detection and alerting. Set up alerts, respond to anomalies, and close the loop with process control. Technical details on how similarity, anomaly detection, and tool state workflows work under the hood. # Connect Source: https://docs.atomscale.ai/platform/get-started/connect Integrate your growth and characterization data with Atomscale The first step to using Atomscale is connecting your data sources. When data arrives, Atomscale's adapter models automatically process it into structured, analysis-ready representations. This page covers the most common connection methods and walks you through your first integration. ## Connection Methods Atomscale supports multiple ways to bring data into the platform, from zero-setup screen capture to fully automated programmatic integration: Upload characterization data or process log files manually. **Best for**: Initial evaluation, ad-hoc usage, historical data **Requires**: Accessible data files in a supported format Stream data by capturing the screen of a workstation or instrument GUI. **Best for**: Zero-setup live streaming, instruments without accessible data files, getting started with real-time analysis quickly **Requires**: Access to the GUI window to capture from Use the Atomscale desktop agent to automatically upload files in a local directory. **Best for**: Set-and-forget automated upload where files are already being written to disk **Requires**: Atomscale desktop agent, API key Upload files or stream data programmatically using the Python SDK. **Best for**: Automated 24/7 real-time data analysis, high-volume upload, custom integration with existing infrastructure **Requires**: SDK setup in a Python application, API key, stable network connectivity ## Your First Connection Choose the path that best fits your situation: ### Option A: Upload a File The quickest way to get data into Atomscale, useful for initial evaluation or loading historical data. In the Atomscale dashboard, click Add Data in the sidebar and select Upload from the connection options. **Drag and drop** your characterization data or process log files, or **click to browse**. Atomscale auto-detects most common instrument file formats. Click Upload to start uploading files in the background. The platform will notify you when uploads complete and processing is ready. ### Option B: Stream via Screen Capture The quickest way to get live data flowing, with no agent install or API setup required. In the Atomscale dashboard, click Add Data in the sidebar and selectScreen Capture. Choose the instrument GUI or application window to capture from. Atomscale streams the visual content and extracts quantitative data in real-time. Confirm that Atomscale is extracting the expected data from the captured window. Derived metrics should begin appearing within seconds. Screen Capture is particularly useful for instruments where the data isn't easily accessible as files or API streams, such as RHEED camera feeds or proprietary instrument GUIs. ### Going Further Once you've verified that Atomscale processes your data correctly, consider setting up persistent connections for ongoing use: * **File Watcher**: Install the desktop agent to automatically upload files from a monitored directory. In the dashboard, click **Add Data** → **File Watcher** to download the agent and configure a watched directory. * **Python SDK**: For full programmatic control, see the [SDK documentation](/sdk) to set up automated streaming or bulk upload pipelines. ## Supported Instruments Atomscale includes built-in adapters for common characterization and process data across thin film growth: | Category | Instruments | | - | - | | Diffraction | RHEED (STAIB, kSA, custom), XRD, XRR | | Spectroscopy | XPS (PHI, Kratos, Thermo), Raman, Photoluminescence, Ellipsometry, OES, SIMS | | Microscopy | SEM, AFM, Optical images | | Process Logs | MBE, MOCVD, CVD, ALD, PVD, and sputtering systems, plus recipe files | | General | CSV, HDF5, IMM, TIFF, VMS, and other common formats | Don't see your instrument or file format? Contact our team at [support@atomscale.ai](mailto:support@atomscale.ai) to quickly set up a new integration. ## Data Organization As you connect data, Atomscale organizes it around a few core concepts that carry through to analysis and monitoring: * **Data Items** are individual files or data streams: a single RHEED recording, an XPS spectrum, or a process log. * **Physical Samples** group data items associated with a sample. A physical sample might have in-situ RHEED data, ex-situ XPS, and the growth process log, all linked together. * **Projects** group related physical samples by growth system, research campaign, product line, or however fits your workflow. Projects are used for run-to-run analysis and monitoring. ## Explore Your Data Once data is connected, the **Data Catalog** is where you find it all. Click Data in the sidebar to open a table of all your data items, where you can search, sort, and filter across all fields and metadata types. ## Next Steps With data connected, Atomscale's adapters extract derived metrics like lattice spacing, intensity profiles, and composition signatures, depending on your data type. You're ready to start comparing runs, monitoring growths, and exploring your process data. Compare runs, track active growths, and extract process insights from your data. Full list of supported formats, advanced configuration, and troubleshooting. # Overview Source: https://docs.atomscale.ai/platform/get-started/index Get up and running with Atomscale in three steps Welcome to Atomscale. This guide walks you through connecting your process and characterization data, extracting insights from it, and using those insights to improve your outcomes from passive monitoring to real-time intervention. ## Connect → Analyze → Act Using Atomscale follows a cycle with three steps: Bring your data into Atomscale via file upload, screen capture, file watcher, or the Python SDK. Atomscale's adapter models automatically process raw instrument data into structured, analysis-ready representations. [Connect your data →](/platform/get-started/connect) Compare runs against your full process history using learned similarity embeddings. Assess within-run uniformity down to individual layers. Monitor active growths in real time. [Start analyzing →](/platform/get-started/analyze) Set up anomaly detection and drift alerts that operate on Atomscale's derived metrics. Respond to issues during a run, make immediate go/no-go decisions, and progressively close the loop toward automated process control. [Take action →](/platform/get-started/act) ## Prerequisites Before you begin, ensure you have: An Atomscale account with appropriate permissions. Access to the data sources you want to connect: characterization files, process logs, or a live instrument GUI for screen capture. Network connectivity between your data sources and Atomscale (or files to upload for offline evaluation). Don't have an account yet? Contact your organization's Atomscale administrator or [reach out to our team](mailto:support@atomscale.ai). ## What You'll Achieve By the end of this guide, you will: 1. **Have Atomscale processing your data**, with adapter models extracting derived metrics like lattice spacing or composition predictions from your raw instrument data. 2. **Be able to compare any run against your process history** using similarity embeddings that capture the full process signature. 3. **Have real-time monitoring configured**, tracking active growths and receiving alerts when the process diverges from expected behavior. 4. **Understand the path to closed-loop control**, from operator-assisted decision-making through agent-based process intervention. ## Choose Your Path Connect your first data source and follow the full guide. Skip to analysis if your team has already set up data connections. Jump to alerts and process control if you're already familiar with Atomscale's analysis tools. Learn about what Atomscale can do before committing to setup. Ready to begin? Let's [connect your first data source](/platform/get-started/connect). # Detect and Respond to Anomalies Source: https://docs.atomscale.ai/platform/guides/detect-anomalies Detect process anomalies across instruments, predict quality outcomes, and take corrective action Atomscale monitors your process data for anomalies using learned embeddings to catch subtle patterns across instruments, correlate anomalies across data sources, and predict developing problems. Depending on your integration level, the platform can recommend or execute corrective actions. Changepoint detection is temporarily disabled. New runs won't generate changepoints until it's turned back on. This page describes how detection works when it's enabled. ## What Gets Detected Anomaly detection covers three general categories: **Unusual patterns in individual timeseries.** The system encodes timeseries windows into a learned representation using a timeseries model fine-tuned on process data. Outliers in this latent space correspond to patterns that deviate from what the model has learned as normal for your process. This captures both sudden anomalies and gradual drift. Because detection operates on learned embeddings rather than fixed thresholds, it catches subtle structural changes that raw value comparisons miss. Recipe-data drift is also detected for metrology sources by comparing measured values against expected recipe setpoints. **Co-occurring anomalies across instruments that tell a coherent story.** A temperature drift that coincides with a RHEED spot spacing change and an optical morphology shift is a stronger signal than any one alone. After per-parameter detection completes, the system identifies temporal overlaps and embedding-space similarities to group related anomalies. Correlated groups carry compound severity: multiple instruments flagging simultaneously is stronger evidence of a real process deviation. **Problems detected before they fully develop.** The system forecasts timeseries trajectories with confidence intervals. When the forecast crosses an anomaly threshold, a predicted anomaly is flagged for advance warning. A **growth quality score** tracks run health in real time by combining signals from all active data sources into a probability of the final outcome meeting your target specifications. A dropping quality score can signal a compound process shift even when no individual parameter has triggered an error. ## Viewing Anomalies ### On Timeseries Charts Anomalies appear directly on timeseries plots: | Anomaly Type | Visual Indicator | | - | - | | Point anomaly | Marker at the detected timestamp | | Drift anomaly | Highlighted region spanning the detection window | | Correlated group | Highlighted region spanning the detection window | | Forecast anomaly | Dashed region in the projected portion of the timeseries | Markers are color-coded by severity: Warning and Error. Click any marker to see the anomaly type, classification label, score, and detection metadata. The growth quality score displays as a running overlay on the monitoring dashboard. ### Anomaly Summary Each run includes an anomaly summary with: * Counts by type and severity * Growth quality score for the run * Anomaly classification labels * Correlated anomaly groups Zero errors with a few warnings and a high quality score likely means a normal run. Multiple errors or a low quality score warrants investigation. ### Filtering and Querying You can filter anomalies by: | Filter | Options | | - | - | | Data source | Any data source (e.g. characterization or tool state) | | Anomaly type | Point anomaly, drift, recipe-data drift, predicted anomaly | | Classification | Root cause category | | Severity | Warning or error | | Correlated groups | Only anomalies correlated across sources | | Time range | Before or after a given time | | Property | Specific timeseries property (e.g., pyrometer channel, RHEED spot spacing) | | Quality score impact | How much the anomaly affected the growth quality score | ## Real-Time Alerts During streaming runs, anomaly detections are delivered in real time to any of the following channels: Alerts appear in the monitoring dashboard and notification panel during active runs. Email notifications including anomaly details and a link to the affected growth session. Route alerts to create messages in Slack channels. Configure custom alerting channels. ### Per-Project Thresholds Each project can override detection thresholds. Tighten sensitivity for critical parameters or relax thresholds for parameters with known noise. Configuration is available under project settings. Default thresholds are learned baselines calibrated from your organization's historical data. Per-project overrides build on top of these defaults. ## Responding to Anomalies ### During an Active Run When an anomaly is detected during a live run, Atomscale surfaces it alongside a recommended corrective action. Check which parameter triggered, its severity, classification, and detection window. The system shows a recommended corrective action with the expected effect and confidence level. Review the safety envelope: the hard constraints for affected parameters that no corrective action can exceed. The recommended action is always within these bounds. Accept the recommended action, modify its parameters, or dismiss it. Approved or modified actions trigger effects depending on integration level (e.g. assist or control). Dismissed anomalies are logged as acknowledged with no intervention. Track actions and outcomes on an intervention history log. The log records the detection, the action taken, and subsequent parameter behavior to improve future recommendations. For an overview of the progression from monitoring to autonomous control, see [Integrating Process Control](/platform/get-started/act#integrating-process-control). ### After Historical Data Upload For historical data, anomaly detection provides diagnostic value. Review the anomaly timeline against your process log and ex-situ characterization to see whether detected anomalies correlate with quality outcomes. Confirmed and dismissed anomalies feed back into the classification model, and anomaly-outcome pairs contribute to growth quality score training. ## How Anomaly Detection Works ### Detection Process As timeseries data arrives, characterization workflows extract features and embed them into the model's latent space. Outlier detection scores each embedding against learned baselines, then correlates related anomalies across instruments. A classification model assigns root cause categories, forecasts predicted anomalies, and routes to alert channels based on severity. For more details, see the [Changepoint Detection - Technical Reference](/platform/reference/workflows/changepoint-detection#technical). ### Batch Processing When you upload historical data, anomaly detection runs automatically after the analysis pipeline completes. Each timeseries is evaluated independently, then cross-source correlation runs after all sources are processed. Anomalies appear in the UI once processing finishes. ### Live Streaming During a live run, anomaly detection runs incrementally with each incoming data chunk. The system maintains a rolling detection context across chunks to detect both sudden anomalies and emerging trends. Forecasts and the growth quality score update with each chunk. Error-level anomalies trigger real-time alerts. ### Severity Levels | Severity | Meaning | Action | | - | - | - | | Warning | Score elevated but within tolerance, or minor quality score impact. May self-correct. | Review after the run or if warnings accumulate. | | Error | Score exceeds the critical threshold, or quality score has dropped significantly. | Investigate immediately. Triggers real-time alerts in streaming runs. | An anomaly that significantly drops the quality prediction is more likely to be assigned Error-level, even if its individual score is moderate. ### Thresholds and Sensitivity The system uses learned baselines calibrated from your organization's historical data, adapted to the patterns of each data source, recipe, and instrument combination. Per-project threshold overrides let you adjust sensitivity: relax thresholds for parameters with natural variation, or tighten them for critical parameters. ## Next Steps When an anomaly is detected, compare the run against references to understand the deviation. Find consistency problems within a single run that may not appear as time-domain anomalies. Technical details on the embedding pipeline, outlier detection, and forecasting algorithms. Programmatic access to anomaly data, alerts, and control actions. # Diagnose Why Runs Differ Source: https://docs.atomscale.ai/platform/guides/diagnose-run-differences Understand what changed when a run doesn't match expectations by inspecting it over time and comparing it across your dataset When a run produces unexpected results, the first question is always: *what's different?* Atomscale provides two complementary approaches to answer this, depending on the level of detail you need. ## Two Approaches to Diagnosis **Inspect a single run over time to see exactly what happened.** The [Monitor](/platform/reference/monitoring/index) page replays a run with its growth metrics, tool state, and detected events on one synchronized timeline. It shows time-resolved detail on how the process evolved, which metrics shifted and when, and what the tool was doing at the moment of a change. Use this when you need to understand *exactly when and how* a run departs from expected behavior. **What you see:** * **Growth metrics** charts of derived RHEED metrics across the course of the run * **Tool state** instrument parameter logs, synchronized with the metrics so you can line up a change with the conditions that produced it * **Activity timeline** of detected events and changepoints, which you can filter, relabel, and annotate ### Setting Up Select the session from the **Sessions** sidebar on the [Monitor](/platform/reference/monitoring/index) page. An active run appears under **Ongoing**; a finished run appears under **Completed**. For a completed run, use the playback bar to replay it and adjust the speed. For an active run, the view follows the live edge as data streams in. See [Session View](/platform/reference/monitoring/session-view). Watch the growth metrics and tool state together. Where a metric shifts, check the tool state at the same moment to see whether an instrument event coincides with it. Open the **Activity** tab in the Details sidebar to see detected changepoints. Filter by label or magnitude to find the moments where the run's behavior changed. **Find similar and dissimilar runs across your entire dataset.** Explore Similarity uses learned embeddings to position every run by its overall process signature. Instead of inspecting one run in isolation, you see where a run sits relative to your full history: which clusters it belongs to, which it's far from, and how different it is from any other run. Use this to understand *where a run fits* in the broader population and discover unexpected groupings or outliers. **What you see:** * **Similarity map** where runs cluster by process signature, with outliers visually separated * **Ranked matches** with similarity scores for any selected run * **Filtering** by project, sample, or tags to focus on relevant subsets ### Setting Up In the Explore Similarity page, choose a metric, transformation, and time scale window. These control what aspects of the process signature are used for comparison. A 2D scatter plot shows your runs positioned by similarity. Similar runs cluster together; divergent runs sit far apart. Points are colored by the selected metric value. Click any point and select View Similar Data to see ranked matches with similarity scores. Filter by project, sample, or tags to focus your comparison. ## What to Look For Both approaches follow the same logic: identify *where* the run diverges, assess *how much*, and connect that to *what it means* for your outcome. ### Identifying Divergence In the **Monitor session view**, watch the growth metrics for the point where they depart from the run's earlier behavior or from what a healthy run looks like. The synchronized tool state shows whether a specific instrument event coincides with the shift, and the activity timeline flags changepoints automatically so you can jump straight to them. In **Explore Similarity**, look at where the problem run sits relative to runs with known outcomes. A run that clusters with failures but used a "good" recipe suggests the issue is in execution, not design. A run between clusters may indicate a borderline process condition. ### Assessing Significance Not every difference matters. The **Monitor session view** shows the *magnitude* of a shift over time: a brief excursion that self-corrects is different from a sustained drift. **Explore Similarity** scores quantify how far a run sits from its nearest neighbors, which you can compare against the natural spread of your process. Cross-reference with [anomaly detection](/platform/guides/detect-anomalies) results: if a run looks anomalous in similarity analysis and also triggered drift or point anomalies, that strengthens the case that the deviation is real. ### Connecting to Outcomes The diagnostic value comes from linking process divergence to outcome differences. When the **Monitor session view** shows a run shifting during a specific phase, check whether that phase historically correlates with the outcome property that degraded. When **Explore Similarity** shows a run clustering with failures, examine what those failures have in common. Runs that are "close" in embedding space tend to produce similar outcomes, making the distance itself a meaningful diagnostic signal. ## Choosing the Right Approach | Scenario | Approach | Why | | - | - | - | | You need to know exactly when and how a single run changed | Monitor session view | Time-resolved metrics, tool state, and changepoints on one synchronized timeline | | You want to find what a problem run is most similar to | Explore Similarity | Searches your full dataset without pre-selecting references | | An active run looks like it's drifting | Monitor session view | Follows the live edge so you can decide whether to intervene before the run completes | | Process transfer to a new tool | Explore Similarity | Shows how new-tool behavior compares to original-tool signatures across your history | | Recurring intermittent failures | Both | Explore Similarity identifies what failing runs share; the session view shows the full detail of each | ## Documenting Findings When you identify the source of divergence, record it: * **Tag the run** with descriptive labels (e.g., "temperature-drift", "flux-excursion") so it's findable in future similarity analyses. * **Add notes** in the session's Metadata documenting which parameters diverged, your hypothesis, and any corrective action taken. * **Build up labeled examples** of good and bad runs over time so your similarity comparisons grow more informative as your dataset matures. ## Next Steps Diagnose consistency problems within a single run that similarity analysis alone may not surface. Set up automated detection to catch divergence patterns before they require manual diagnosis. # Identify Uniformity Issues Source: https://docs.atomscale.ai/platform/guides/identify-uniformity-issues Find consistency problems within a single run by inspecting it over time and comparing its segments across your dataset A run can look acceptable in aggregate but contain internal variation that affects device performance: early-stage drift, mid-run excursions, or layer-to-layer inconsistency. Atomscale catches these by inspecting a run over time and by comparing segments of it against the rest of your dataset. ## Two Approaches to Uniformity The same tools used to [diagnose why runs differ](/platform/guides/diagnose-run-differences) apply within a single run. Instead of comparing entire runs, you look at how the run behaves across its own phases, layers, or time windows. **Inspect a single run over time to see where its behavior changes.** The [Monitor](/platform/reference/monitoring/index) session view replays a run with its growth metrics, tool state, and detected events on one timeline. Watching how the metrics evolve across the run reveals drift, excursions, and phases that behave differently from the rest. **What you see:** * **Growth metrics** charts of derived RHEED metrics across the full run, where drift and excursions show up as the values move * **Tool state** instrument parameters synchronized with the metrics, giving context for why a particular stretch of the run varied * **Activity timeline** of detected changepoints, marking the moments where the run's behavior shifted ### Setting Up Select the session from the **Sessions** sidebar on the [Monitor](/platform/reference/monitoring/index) page. Use the playback bar to scrub through the run, or follow the live edge for an active one. See [Session View](/platform/reference/monitoring/session-view). Look for stretches where a metric drifts away from the run's earlier behavior, or where one phase, layer, or cycle spreads more than the others. Open the **Activity** tab to see detected changepoints. These mark transitions between phases and the moments where uniformity breaks down. **See how all segments within a run relate to each other across your full dataset.** Use Explore Similarity for a broad view of internal consistency without pre-selecting references. Every segment is positioned by its process fingerprint, so you can immediately see whether segments cluster tightly (good uniformity) or spread out (variation present). **What you see:** * **Similarity map** where segments cluster by process signature (tight clustering = high uniformity, spread = variation) * **Ranked matches** with similarity scores for the most and least similar segments * **Filtering** by project, sample, or tags to compare segments against previous runs ### Setting Up In the Explore Similarity page, choose a metric, transformation, and time scale window appropriate for your segment granularity (e.g., per-cycle for ALD, per-phase for recipes). Segments from the same run appear alongside segments from other runs. Look at how tightly the current run's segments cluster and whether any sit apart. Click any outlier segment and select View Similar Data to see ranked matches. If an outlier is more similar to segments from a *different* run than to its own neighbors, that's a strong signal of a uniformity break. ## What to Look For ### Drift Over Time Segments that gradually shift away from early-run behavior indicate drift. In the **Monitor session view**, this appears as growth metrics that steadily trend away from where they started. In **Explore Similarity**, drifting segments form a gradient on the map rather than a tight cluster. ### Outlier Segments A single segment that diverges sharply from the rest points to a transient disturbance, easiest to spot in **Explore Similarity** where the outlier sits visibly apart. ### Phase-Specific Variation Some recipe phases may be consistently less uniform than others. If segments from one phase always spread more widely, that phase likely needs tighter process control. The **Monitor session view** makes this visible in the growth metrics, showing which derived properties vary most during the problematic phase. ### Periodic Structure Consistency For multilayers and superlattices, watch how each cycle behaves in the growth metrics on the **Monitor session view**. Consistent cycles repeat nearly identical metric patterns. If early cycles differ from late cycles, or if specific cycles stand out, the time-resolved view shows exactly where within the cycle the variation occurs. **Explore Similarity** complements this: plot the cycles on the map and consistent ones cluster tightly while outliers separate. ## Connecting Uniformity to Outcomes Within-run uniformity metrics become most valuable when connected to final device or material properties. Runs with tight internal clustering tend to produce more consistent outcomes. If loosely clustered runs correlate with degraded performance, that gives you a quantitative uniformity threshold to monitor against. ## Choosing the Right Approach | Scenario | Approach | Why | | - | - | - | | Seeing where within a run a cycle or phase varies | Monitor session view | Time-resolved metrics and changepoints show exactly when the run's behavior shifts | | Checking whether all segments in a run are consistent | Explore Similarity | Immediately reveals outliers and clustering without pre-selecting references | | Identifying which recipe phase has the most variation | Explore Similarity | Segments from different phases naturally separate on the map if they behave differently | | Understanding why a specific layer is different | Monitor session view | Detailed metrics and tool state pinpoint what changed during that layer | | Tracking uniformity improvement across iterations | Explore Similarity | Compare segment clustering tightness across runs to see if uniformity is improving | ## Next Steps Compare entire runs against each other when the issue is between runs rather than within one. Set up automated detection to catch uniformity problems as they develop during active runs. # Guides Overview Source: https://docs.atomscale.ai/platform/guides/index Goal-oriented workflows for process monitoring, analysis, and optimization Guides help you accomplish specific goals using Atomscale. Each guide addresses a common problem that process engineers face and walks through how to solve it. ## Available Guides Catch problems in real time while your process is running. Configure alerts, monitor key parameters, and respond before issues affect your outcome. Understand what changed when a run doesn't match expectations. Compare against references, identify deviations, and correlate with outcomes. Find consistency problems within a single run. Analyze variation across time, layers, or phases to pinpoint where uniformity breaks down. ## Choosing a Guide | If you need to... | Start with | | - | - | | Monitor an active growth and catch problems early | [Detect Anomalies During a Run](/platform/guides/detect-anomalies) | | Understand why a specific run went wrong | [Diagnose Why a Run Differs](/platform/guides/diagnose-run-differences) | | Find where variation occurs within a growth | [Identify Uniformity Issues](/platform/guides/identify-uniformity-issues) | ## Prerequisites These guides assume you have: * Connected at least one data source to Atomscale (see [Connect](/platform/get-started/connect)) * Growth or characterization data to work with * Familiarity with your process parameters and typical operating ranges ## Related Documentation New to Atomscale? Start here for initial setup and core concepts. Technical details on analysis workflows and integrations. # Atomscale Documentation Source: https://docs.atomscale.ai/platform/index Real-time process intelligence for advanced materials manufacturing Atomscale is an ***integrated process environment*** for advanced materials manufacturing. We connect and unify data across tools, transform raw signals into quantitative process fingerprints, and integrate those into a real-time representation of your material in process. This operationalized data drives a process intelligence and agentic framework for **real-time monitoring and closed-loop process control.** ## The Problem Advanced materials are designed at the atomic scale but **manufactured with little visibility** into what's actually happening during processing. Feedback on critical material properties typically arrives hours or days later after ex-situ characterization, long after it's possible to correct issues in-process or abort failing runs early. This gap is growing. As materials become increasingly complex, process windows narrow and the design space for experimentation explodes. Traditional process development relying on parametric statistical models scale poorly with the amount of interacting variables in modern tools and recipes. The result: **most useful process data collected during growth isn't used** because it isn't actionable in real time and standard operating procedures rely on only a fraction of available signal. ## What Atomscale Does Atomscale extracts **1000x more information from existing process data and makes it usable in real time.** Continuous monitoring provides active process control to improve yield and throughput for complex materials, and a quantitative foundation for predictive anomaly and process drift detection. ### Key Capabilities Stream data from growth systems and metrology tools to get automated analysis and actionable insights as layers form with 100% of your data signal. Physics-aware AI models predict process state in real time and generate actionable in-situ proxies for costly ex-situ measurements. Automatically detect process drift, defects, and flag out-of-distribution runs before they impact yield with better consistency than manual monitoring. Close the loop from monitoring to intervention with process adjustments informed by direct materials feedback, compounding value as your process history grows. ## How Atomscale Works Atomscale transforms raw process data into actionable insights in three stages: Tool-specific adapter models ingest and unify data across make and measure tools in real-time, transforming raw signals into comprehensive fingerprints that unlock large-scale pattern recognition, analysis, and automation. Timeseries foundation models embed adapter outputs to answer: How does the current run compare to previous runs? How uniform is the process from segment to segment, down to individual atomic layers and up to whole-recipe sequences? Generate specific process interventions: flag anomalies, identify drift, and recipe adjustments in real time to improve yield and achieve target outcomes, creating a path towards a adaptive control and a self-driving process of record. ## What Differentiates Atomscale * **Works with raw data**: Eliminates rigid statistical assumptions by working directly with raw signals instead of point-solution outputs from individual analysis tools. * **Real-time, short-loop feedback**: Scalable feedback that replaces homegrown scripts, brittle manual analysis, and heavy MES add-ons. * **Flexible across tools and materials**: Tool-specific adapters handle integration complexity so the platform works across deposition methods and characterization techniques without requiring you to build custom pipelines. * **Compounds over time**: Every run, successful or not, enriches the models. The intelligence layer adapts and grows as fast as your process development does. ## Next Steps See how teams use Atomscale for defect detection, composition control, and process optimization. Connect your first data source and explore the platform. # Accessibility Source: https://docs.atomscale.ai/platform/reference/accessibility Atomscale's commitment to accessible design Atomscale is committed to making our platform accessible to all users. This document describes our accessibility features, compliance status, and guidance for accessible use. ## Commitment We believe that scientific tools should be usable by everyone. Atomscale is designed to be: * Perceivable by users with visual impairments * Operable by users with motor impairments * Understandable by users with cognitive differences * Robust across assistive technologies ## Compliance ### WCAG Conformance Atomscale targets **WCAG 2.1 Level AA** conformance: | Principle | Status | Notes | | - | - | - | | Perceivable | Conformant | Alt text, color contrast, resizable text | | Operable | Conformant | Keyboard navigation, focus management | | Understandable | Conformant | Consistent navigation, error identification | | Robust | Conformant | Valid markup, ARIA support | ### VPAT A Voluntary Product Accessibility Template (VPAT) is available upon request. Contact [accessibility@atomscale.ai](mailto:accessibility@atomscale.ai). ## Accessibility Features ### Visual UI scales with browser zoom up to 200%. Information not conveyed by color alone. Support for major screen readers. ### Keyboard Navigation All functionality is available via keyboard: | Action | Shortcut | | - | - | | Navigate between elements | `Tab` / `Shift+Tab` | | Activate buttons/links | `Enter` or `Space` | | Open dropdowns | `Enter` or `Space` | | Navigate dropdown items | `Arrow keys` | | Close dialogs | `Escape` | | Navigate tabs | `Arrow keys` | Skip links are available to bypass repetitive content. ### Screen Reader Compatibility All interactive elements have appropriate: * ARIA labels * Role definitions * State announcements * Live region updates ## Reporting Issues Found an accessibility barrier? We want to know. ### How to Report 1. Email [accessibility@atomscale.ai](mailto:accessibility@atomscale.ai) 2. Include: * Description of the barrier * What you were trying to do * Assistive technology used (if any) * Browser and operating system 3. We'll respond within 2 business days ### Feedback Process 1. **Acknowledgment**: We confirm receipt of your report 2. **Investigation**: We reproduce and assess the issue 3. **Resolution**: We fix the issue or provide a workaround 4. **Verification**: We confirm the fix addresses your need ## Resources ### Training * Keyboard shortcuts: Contact support for the full reference * Screen reader quick start: Contact [accessibility@atomscale.ai](mailto:accessibility@atomscale.ai) ### External Resources * [WebAIM WCAG Checklist](https://webaim.org/standards/wcag/checklist) * [A11y Project](https://www.a11yproject.com/) ## Contact For accessibility questions or assistance: * Email: [accessibility@atomscale.ai](mailto:accessibility@atomscale.ai) * Phone: Available upon request for accessibility support # File Watcher Source: https://docs.atomscale.ai/platform/reference/connecting-data/file-watcher Automatically upload files from a local directory The standalone File Watcher app is legacy. For new setups, use the **File Watcher** integration in the [Atomscale desktop app](/platform/reference/desktop-app) instead. See [Integrations](/platform/reference/connecting-data/integrations). The File Watcher is a desktop application that monitors a local directory and uploads new files to Atomscale automatically. It is useful when your instruments write data to a shared folder and you want that data to appear in the platform without manual intervention. ## Installation Open the Add Data dialog in the sidebar, then select the File Watcher tab. Click Download File Watcher to get the installer. This tab appears only when you use Atomscale in a browser. Run the downloaded installer and follow the on-screen prompts. The File Watcher authenticates with your API key. If you don't already have one, generate a key from **Add Data > API Integration**, or click your name at the bottom of the sidebar and select **Manage account** > **Access**. Copy and save the key; it is only displayed once. ## Configuration Open the File Watcher and go to the **Settings** tab. The following settings control how files are detected and uploaded. ### Watch Path The absolute path to the directory the File Watcher monitors (for example, `/Users/lab/atomcloud_sync`). Use the **Browse** button to select a directory. ### API Key Paste the API key you generated in Atomscale. The File Watcher validates the key on entry, so you will see an error if it is invalid or expired. API keys expire. When you generate a key, you choose its **Expiration**: 7, 30, 60, or 90 days (the default), or **Never**. Keys that go unused for 90 days are revoked automatically, and generating a new key immediately expires your previous one. Once the key stops working, uploads fail until you paste a new key here. ### Traversal Depth Controls how many directory levels below the watch path to scan. Set to **0** to watch only the top-level directory, or increase it to detect files in subdirectories. For example, a depth of **2** reaches files two folders deep. ### Group Uploaded Files When enabled, files are organized into physical samples based on the name of their top-level folder within the watch path. For example, if the watch path contains folders `GBL_UNLIT/` and `DR1_CAST/`, each folder becomes a physical sample in Atomscale. ### File Coalescing Glob patterns that consolidate multiple files into a single data item after upload. For example, adding `*.jpg` and `*.txt` as patterns means all JPEG files in a folder are grouped into one data item, and all text files into another. Patterns are matched against file names only, scoped to each top-level folder. ### Auto Upload When enabled, newly detected files are uploaded automatically. This toggle resets when the watch path changes to prevent accidental uploads. ## Folder Structure The File Watcher expects files to be organized by sample. Under the watch path, create a folder for each sample, then place the associated files inside it: ``` watch_path/ ├── GBL_UNLIT/ │ ├── img_001.jpg │ ├── img_002.jpg │ ├── ... │ └── metrology.txt ├── DR1_CAST/ │ ├── img_001.jpg │ ├── img_002.jpg │ ├── ... │ └── metrology.txt ``` With **Group Uploaded Files** enabled and coalescing patterns set for `*.jpg` and `*.txt`, each top-level folder maps to a physical sample, optical images are consolidated into one data item, and metrology files into another. Optical image file names should be sequentially numbered (for example, `img_001.jpg`, `img_002.jpg`) so they are ordered correctly after upload. ## Uploading Files Files can be uploaded three ways: * **Per file**: click the upload icon on any row in the **Files** tab. * **Bulk**: select multiple files and choose **Upload eligible files** from the actions menu. * **Automatic**: enable **Auto Upload** in Settings to upload new files as they appear. Upload progress and status are shown in the Files tab. Each file transitions through **Pending**, **Queued**, **Uploading**, and **Uploaded** (or **Error** if something went wrong). Files that errored can be retried. ## Advanced Settings Under the collapsible **Advanced Settings** section you can: * **Polling**: enable polling-based file detection, which is more reliable for network or cloud storage drives. * **Await File Stability**: wait for a file's size to stabilize before processing, which prevents uploading files that are still being written. * **Reset Settings**: clear all configuration and return to defaults. ## Technical Details This section covers system requirements, networking, and security information for IT teams evaluating the File Watcher for deployment. ### Application Overview The File Watcher is a lightweight desktop application built on Electron. It runs in user space and does not install system services, modify system-level settings, or require administrator privileges. It monitors a user-selected directory for new files and uploads them to Atomscale over HTTPS. ### System Requirements * Windows 10 or later (macOS and Linux are also supported) * Minimal resource footprint: under 200 MB of disk space and roughly 100 MB of RAM during normal operation * Outbound internet access to the Atomscale API and AWS S3 ### Installation and Updates The Windows installer is a standard Squirrel-based `.exe` that installs to the current user's `AppData\Local` directory. No elevated permissions or administrator approval is needed. The application checks for updates automatically (shortly after launch, then every 10 hours) and notifies the user when a new version is available. Updates are downloaded in the background and applied only when the user chooses to restart. All installers and updates are code-signed with SHA-256. ### Network Requirements The File Watcher makes **outbound HTTPS connections only**. It does not open any listening ports or accept inbound traffic. | Destination | Purpose | | - | - | | Atomscale API (`api.atomscale.com`) | Authentication, upload coordination, and pipeline triggers | | AWS S3 (`s3.us-east-1.amazonaws.com`) | File upload via time-limited presigned URLs | No VPN, firewall exceptions for inbound traffic, or special DNS configuration is required. If your network uses an allowlist for outbound traffic, permit HTTPS (port 443) to the destinations above. ### Authentication and Security * **Authentication**: API key passed in a request header. No cookies, session tokens, or stored passwords. * **Encryption in transit**: all communication uses TLS/HTTPS. There is no HTTP fallback. * **Upload authorization**: files are uploaded to S3 using presigned URLs that expire after one hour. * **Code integrity**: the application bundle is packaged as a signed, read-only archive (ASAR) with integrity validation enabled. * **Process isolation**: the UI renderer has no direct access to Node.js or the file system. All operations go through a controlled bridge between the interface and the main process. ### Local Data Storage The application stores its configuration (watch path, API key, upload history) in a small JSON file under the user's application data directory: * **Windows**: `%APPDATA%\atomcloud-file-watcher\` * **macOS**: `~/Library/Application Support/AtomCloud File Watcher/` Log files are written alongside the configuration and automatically rotate at 1 MB. No uploaded file content is cached locally. # Integrations Source: https://docs.atomscale.ai/platform/reference/connecting-data/integrations Connect instruments and workstations with managed integrations Integrations automatically detect and upload data from your local tools and workstations to Atomscale. Each integration is a small connector program that the Atomscale desktop app downloads, runs, and supervises for you. From the **Integrations** page you can install integrations, configure them, and monitor their status. Integrations are managed from the [Atomscale desktop app](/platform/reference/desktop-app). The **Integrations** page is not available when using the platform in a browser. ## How Integrations Work An integration collects data from a local source, such as a watched folder or an instrument, and hands it to the desktop app. The app buffers the data on disk, then uploads it to Atomscale over HTTPS using your signed-in session. ```text theme={null} Local source Atomscale desktop app Cloud ┌─────────────┐ ┌───────────────────────────────────┐ ┌───────────┐ │ Instrument, │ → │ Integration → buffer → upload │ → │ Atomscale │ │ folder, ... │ │ (disk) queue │ │ platform │ └─────────────┘ └───────────────────────────────────┘ └───────────┘ ``` A few properties of this design are worth knowing: * **No machine credentials**: Integrations never hold API keys and never connect to Atomscale themselves. All uploads go through the desktop app, authenticated as the signed-in user. * **Durable buffering**: Data is stored on disk until it is uploaded, so network outages and app restarts do not lose data. * **Sign-out safe**: If you sign out, integrations keep collecting data locally and uploads pause. Uploading resumes automatically when you sign back in. Each installed integration is independent, with its own configuration, status, and activity log. You can install multiple copies of the same integration, for example to watch two different folders, and rename each copy to tell them apart. ## Adding an Integration Select **Integrations** in the desktop app sidebar. Click Add Integration. A dialog lists the integrations available to your workspace. Click Add on the integration you want. The app downloads the connector and verifies its checksum before installing it. When installation completes, you are taken to the integration's configuration page. Adding an integration only downloads it. It does not upload anything until you configure and start it. Some catalog entries may be temporarily unavailable, for example when an integration is not built for your operating system or requires a newer version of the desktop app. Hover over a disabled entry to see the reason. ## Configuring an Integration Open an integration from the Integrations table, then click Configure. A running integration must be stopped before its configuration can be changed. The form shows the settings most installations need, such as paths, device identifiers, and grouping options. The available fields depend on the integration. Optional tuning settings are hidden by default. Turn on the **Show advanced settings** switch at the top of the form to reveal them. Every integration includes these advanced settings: * **Upload Backlog Size Limit**: Caps how much unsent data is buffered on disk. Leave it blank to size the buffer automatically based on free disk space. * **Free Disk Space To Keep**: The amount of disk space the buffer will never consume. The app remembers the switch position for each integration. Hiding advanced settings does not discard them: their values are still saved when you apply the configuration. Fill in the form and click Apply Configuration. Invalid values are highlighted inline; the integration itself also validates the configuration and can reject specific fields, in which case the affected inputs are highlighted with the reason. If a rejected field is an advanced setting, the **Show advanced settings** switch turns on so you can see it. Applying a configuration saves it but does not start the integration. Start it explicitly when you are ready to collect data. ## Starting and Stopping Use the Start Integration and Stop Integration buttons on the integration's detail page. The desktop app remembers this choice: integrations you started are launched automatically the next time the app opens, and integrations you stopped stay stopped. If an integration crashes, the app restarts it automatically with an increasing delay. An integration that crashes repeatedly in a short window is **quarantined**, meaning the app stops restarting it. After fixing the underlying problem, press Resume Integration to clear the quarantine. Restarting the desktop app also clears it. ## Monitoring The Integrations table shows each installed integration with its name, type, version, and current status. You can search by name and filter by type. Click a row to open the detail page, which includes: * **Details**: The connector, its description, type, and installed version. * **Health**: Current status, last successful upload, and running time. * **Upload Queue**: The state of pending data uploads for this integration. * **Activity Logs**: A searchable history of events, filterable by severity, with an option to open the log files on disk. A **Standard** / **Advanced** selector at the top of the detail page switches between this summary and an **Advanced** view with process, delivery, and disk diagnostics for troubleshooting and support. This selector only changes what the detail page displays; advanced configuration settings are on the configuration page. ### Status Reference | Status | Meaning | | - | - | | **Starting** | The integration is launching. | | **Running** | The integration is collecting and uploading data. | | **Stopped** | The integration is not running. | | **Uploads paused** | The integration is collecting data, but uploads are paused (for example, while signed out). | | **Error** | The integration reported a problem. Check the activity logs for details. | | **Quarantined** | The integration crashed repeatedly and the app stopped restarting it. | | **Update available** | A newer version can be installed. | | **Revoked** | The installed version was withdrawn and will not run. Update or uninstall it. | ## Updating an Integration The desktop app checks for new integration versions at launch and every 5 minutes afterward. You can also check on demand with the refresh control next to the search box. With **Automatic updates** turned on (the default), the app installs integration updates by itself: * It waits until the integration has collected no data for at least 15 minutes, then counts down 5 minutes before installing. If the integration starts collecting data again, the countdown is canceled until the next idle period. * While you are viewing the integration, a dialog shows the countdown with **Update now** and **Delay 1 hour** buttons. * If your installed version is **Revoked** and a newer version is available, the countdown starts right away and the update installs even if the integration is collecting data. To install updates only manually, turn off **Automatic updates** under **Application** in your account settings (see [Desktop App settings](/platform/reference/desktop-app#settings)). When an update is available, the integration shows an **Update available** badge and its detail page shows an **Update Available** card with the new version. Click Update Integration to install it right away. * If the integration is running, the update briefly suspends uploading while the new version is installed, then the integration restarts automatically. Buffered data is preserved. * Releases marked as a **Breaking change** may migrate your saved configuration to a new format. The migration happens automatically during the update. * If an update fails, the previous version is restored automatically and a notice explains what went wrong. ## Uninstalling an Integration Stop the integration, then open its configuration page and turn on **Show advanced settings**. The **Danger zone** section appears at the bottom of the form. Click Uninstall integration and confirm. Uninstalling removes the integration's local program and configuration. You can reinstall it at any time. Uninstalling also deletes the integration's local upload queue, including any collected data that has not been uploaded yet. Reinstalling does not restore it. Before you uninstall, check the **Upload Queue** on the integration's detail page and wait for pending uploads to finish. If you are signed out, sign back in first so the queue can drain. ## Upload Settings Upload bandwidth and the number of simultaneous file uploads are controlled per computer, not per integration, under **Data Ingestion** in your account settings. Changes apply immediately to all active integrations. Timeseries data is prioritized over file uploads so that live monitoring stays responsive. ## Technical Details This section summarizes networking and security behavior for IT teams evaluating integrations for deployment. * **Outbound HTTPS to Atomscale**: All communication with Atomscale uses TLS on port 443. See [Security](/platform/reference/security#desktop-app-and-integrations) for the main destinations. * **Inbound rules for live RHEED capture**: On Windows, when the **RHEED Camera (GenTL)** integration starts with the **Standard (GenTL)** or **Network (GVSP Tap)** frame source, the app asks for administrator approval (once per installed copy) to add an inbound Windows Firewall rule. The rule allows UDP traffic from the local subnet to that integration's program only, so the camera's network stream can reach it. If approval is declined, the integration still starts and logs a warning. Turning on **Integration admin access** (see [Desktop App settings](/platform/reference/desktop-app#settings)) adds further program-scoped inbound rules as part of its setup. Other integrations do not accept inbound traffic, and the desktop app accepts connections from other computers only in Connection Sharing **Hub** mode. * **Downloads are verified**: Integration programs are downloaded from Atomscale over HTTPS and verified against a SHA-256 checksum before installation. * **Process isolation**: Each integration runs as a separate local process. An integration may receive data from instruments on your local network (for example, **RHEED Camera (GenTL)** receives the camera's stream), but it never connects to Atomscale. It hands its data to the desktop app, which uploads it. * **Authentication**: Uploads are authenticated with short-lived tokens tied to the signed-in user. To keep you signed in across restarts, the app stores your sign-in session on disk, encrypted with Windows DPAPI. It also creates a random device secret for each installation, stored under `%APPDATA%\Atomscale\gateway\`, which identifies the computer to Atomscale. Integrations hold no credentials. * **Local storage**: Configuration, logs, and buffered data live under the desktop app's application data directory. Buffered data is kept until it uploads successfully, up to a 30-day limit. * **Local logs**: Integration activity logs are written as daily files under the desktop app's application data directory, at `%APPDATA%\Atomscale\gateway\logs\`. You can also open this folder directly from an integration's **Activity Logs** section. # Manual Upload Source: https://docs.atomscale.ai/platform/reference/connecting-data/manual-upload Upload files to Atomscale through the web interface Manual upload lets you drag and drop files directly into Atomscale from your browser. ## How to Upload Click Add Data in the sidebar. The Upload tab is selected by default. Drag and drop files into the dashed upload area, or click Browse your Computer to open a file picker. You can add multiple files at once. Each file shows a **data stream** selector. Atomscale auto-detects the stream type when possible; you can also select one manually. Use the Add to Physical Sample dropdown below the file list to link the uploaded data to an existing sample, or create a new one. Click Upload Files. A progress indicator shows the upload percentage. The dialog closes automatically once the upload completes. ## What Happens After Upload Uploaded data appears in the [Data catalog](/platform/reference/models/data-items). # Programmatic Integration Source: https://docs.atomscale.ai/platform/reference/connecting-data/programmatic Upload data and stream frames using the Python SDK For scripted or automated workflows, you can upload data to Atomscale programmatically using the Python SDK. This page covers API key setup and gives a quick overview of the SDK. For the full guide, see the [SDK Quickstart](/sdk/quickstart). ## Generate an API Key Click Add Data in the sidebar, then select the API Integration tab. You can also manage API keys by clicking your name at the bottom of the sidebar and selecting **Manage account** > **Access**. Choose an **Expiration** (7, 30, 60, or 90 days, or **Never**; the default is 90 days), then click Generate API Key. The key is displayed once. Copy it immediately and store it somewhere safe. Generating a new key immediately expires the previous one, and keys that go unused for 90 days are revoked automatically. Any applications using an expired or revoked key will lose access until you give them a new key. ## Install the SDK ```bash theme={null} pip install atomscale ``` ## Upload Files Create a client and upload files in a few lines: ```python theme={null} from atomscale import Client files = [ "/data/growths/2025-02-10/RHEED-stationary.mp4", "/data/growths/2025-02-10/RHEED-rotating.imm", ] client = Client(api_key="YOUR_API_KEY") client.upload(files=files) ``` The client reads `AS_API_KEY` (or `ATOMSCALE_API_KEY` if `AS_API_KEY` is not set) from the environment when no key is passed explicitly. ## Next Steps Full walkthrough of client setup, search, and upload. Detailed guide on uploading files with the SDK. # Screen Capture Source: https://docs.atomscale.ai/platform/reference/connecting-data/screen-capture Stream instrument data by recording your screen Screen capture lets you record a window on your computer and stream frames to Atomscale in real time. This is currently used for RHEED data: frames are captured from your RHEED software window, uploaded continuously, and analyzed with the RHEED Fingerprinting workflow. ## How to Stream Click Add Data in the sidebar, then select the Stream tab. Click Choose source. Your browser will prompt you to select a window, screen, or tab to capture. Selecting from the Window section is recommended for best results. A live preview of your selected source appears. Drag and resize the capture area overlay to frame the region you want to record. Fill in the configuration form: | Field | Description | | - | - | | **Data Name** | Defaults to "RHEED Stream @ \[Date]". Change it to something descriptive. | | **Physical Sample** | Optionally associate the stream with a sample. | | **Workflow** | Currently fixed to **RHEED Fingerprinting**. | | **Stage Rotation (rpm)** | Set the rotation speed for rotating-stage growths. Leave empty for stationary RHEED. | Click Start Stream. Frames are captured and uploaded in the background. While streaming you can Pause, Resume, or End Stream. An elapsed-time timer is displayed in the controls. You can close the Add Data dialog while a stream is active. The recording continues in the background, and the sidebar button shows the stream status. ## Interrupted Streams If a stream is interrupted (for example, by closing the browser tab), Atomscale marks it as interrupted the next time you open Atomscale in the same browser. An active stream that receives no data for two hours is also marked as ended with an error. Neither can be resumed. To finalize the data already captured, open the stream's data item page, click Stream Settings, and end the stream. To keep recording, start a new stream. # Desktop App Source: https://docs.atomscale.ai/platform/reference/desktop-app Install and use the Atomscale desktop app for Windows The Atomscale desktop app is a native application for Windows. It includes the full Atomscale platform, plus capabilities that require running on the computer where your data lives: * **[Integrations](/platform/reference/connecting-data/integrations)**: Install and supervise connectors that collect data from local instruments and folders. * **Connection Sharing**: Let lab computers without internet access upload through another computer. * **Background data collection**: Integrations keep collecting and uploading even when the app window is closed. * **Application modes**: Run the full product, or a focused data-ingestion mode for instrument workstations. ## Installation Installers are code-signed by Atomscale and are available for Windows (x64 and 32-bit). Contact Atomscale if you need the installer. Run the installer. The app installs per user, by default into `%LOCALAPPDATA%\Programs\Atomscale` (you can choose a different folder in the installer), and creates Desktop and Start Menu shortcuts. No administrator rights are required to install or update. If Atomscale is already installed, the installer asks how to proceed: * **Reinstall and keep my data**: Sign-in state, settings, downloaded integrations, and queued local data are preserved. This is the default. * **Reinstall and erase all local data**: Permanently removes sign-in state, settings, downloaded integrations, and queued local data for the current Windows account. ## First-Run Setup The first time you open the app, a setup assistant walks you through connecting the computer to Atomscale: The app checks how this computer reaches the Atomscale platform. If the computer has no direct internet access, you can enter the address of another lab computer that shares its connection (see [Connection Sharing](#connection-sharing)) and test it here. Choose how the computer will be used: **Monitor and analyze growths** for the full application, or **Connect an instrument** for a focused data-collection setup. This sets the [application mode](#application-modes), which you can change later. Sign in with your Atomscale account to connect the installation to your organization. Optionally add the integrations this computer will run. Adding an integration only downloads it; you configure and start it later from the [Integrations](/platform/reference/connecting-data/integrations) page. If the computer has a working internet connection, you can optionally enable Connection Sharing so other lab computers can upload through it. You can leave setup at any point with Exit setup, and return to it later from your profile menu under **Onboarding**. Re-running setup does not change your current settings until you make new choices. ## Signing In Sign-in with a social or SSO provider opens in your system browser, then returns you to the app automatically. Your session persists across app restarts. Signing out does not stop data collection. Integrations keep collecting locally, uploads pause, and everything queued is uploaded automatically when you sign back in. ## Application Modes The app runs in one of two modes, chosen during setup and changeable under **Application** in your account settings: | Mode | Description | | - | - | | **Full** | The complete product for managing data, monitoring growths, and analyzing runs. | | **Data Ingestion** | A focused mode for workstations that only need to collect and upload data. The interface is reduced to the data catalog, integrations, and personal settings. | Switching modes reloads the app. Installed integrations and queued uploads are unaffected. ## Connection Sharing Many instrument workstations have no direct internet access. Connection Sharing lets one internet-connected computer running Atomscale act as a relay for others on the same local network. ```text theme={null} Instrument workstations Internet-connected computer ┌──────────────┐ │ Outpost │────┐ └──────────────┘ │ local network ┌──────────────┐ HTTPS ┌───────────┐ ├────────────────►│ Hub │──────────►│ Atomscale │ ┌──────────────┐ │ └──────────────┘ └───────────┘ │ Outpost │────┘ └──────────────┘ ``` Each computer runs in one of three modes, configured in the **Connection Sharing** dialog (available from the Integrations page, or from the **Connection Settings** button on the sign-in screen): | Mode | Description | | - | - | | **Normal** | Connects to Atomscale directly, using the computer's own network settings. This is the default. | | **Hub** | Also carries traffic for nearby Atomscale computers that can't reach the internet. The Hub listens on a local port (8484 by default) and shows the addresses to enter on the other computers, along with a list of connected computers. | | **Outpost** | Reaches Atomscale through a Hub on the same network. Enter the address shown on the Hub computer and test it before saving. | A few things to know: * Connection Sharing works between computers on the same local network. * An Outpost never falls back to a direct connection. If its Hub is unreachable, uploads queue locally until the Hub is back or you point the Outpost at a different connection. This ensures traffic only ever flows through the route you configured. * Enabling Hub mode prompts once for a Windows Firewall rule so other computers can reach it. ## Running in the Background Closing the app window does not quit the app. It keeps running in the system tray so integrations, uploads, and Connection Sharing continue uninterrupted. Click the tray icon to reopen the window, or use its menu to quit. The tray menu also includes an **Open at Login** toggle, which is enabled by default so data collection resumes automatically after a reboot. If you quit while data collection or uploads are in progress, the app asks for confirmation first, since quitting stops collection and leaves queued data on disk until the app is opened again. ## Updates The app checks for updates shortly after launch and every 15 minutes afterward, and downloads them in the background. When an update is ready, a notification appears with an Update button. If integrations are running, the app lists them and asks for confirmation before restarting, since the restart briefly interrupts data ingestion. You can also check manually from **Help > Check for updates**. Updates install silently and never require administrator rights. With **Automatic updates** turned on (the default), the app also installs a downloaded update by itself once all integrations have collected no data for at least 15 minutes. If an update is still not installed 48 hours after it downloads, it installs even while integrations are collecting data. Before an automatic restart, a dialog counts down 5 minutes with **Update now** and **Delay 1 hour** buttons. The same setting controls automatic [integration updates](/platform/reference/connecting-data/integrations#updating-an-integration). Turn it off under **Application** in your account settings to install updates only manually. ## Settings Desktop-specific settings live in your profile menu, alongside your account settings: | Page | What it controls | | - | - | | **Application** | The [application mode](#application-modes), the switches described below, and an **Open log folder** button for accessing application logs when troubleshooting. | | **Data Ingestion** | Upload bandwidth (50, 250, or 500 Mbps, or Unlimited) and the number of concurrent file uploads. Shared by all integrations on the computer; changes apply immediately. | | **Onboarding** | Re-opens the [first-run setup](#first-run-setup) assistant. | The **Application** page includes these switches: * **Integration admin access**: Required for the **Network (GVSP Tap)** frame source of the **RHEED Camera (GenTL)** integration, which needs administrator access. Turning it on asks once for administrator approval and registers a Windows scheduled task that runs as SYSTEM, so the integration can start with administrator access without further prompts. Turning it off removes that setup and also asks for administrator approval. * **Automatic updates**: Installs downloaded app and integration updates automatically. On by default. See [Updates](#updates). * **Show developer bar**: Shows live performance metrics along the bottom of the window. Off by default. ## Local Data The app stores its settings, logs, and queued integration data in the current user's application data directory at `%APPDATA%\Atomscale`. Data queued for upload is kept until it uploads successfully, up to a 30-day limit. Application updates and reinstalls preserve this directory unless you explicitly choose to erase it. # FAQ Source: https://docs.atomscale.ai/platform/reference/faq Frequently asked questions about Atomscale Common questions about Atomscale's platform, technology, and how it fits into your workflow. ## How Atomscale Works Atomscale is an AI platform that extracts **1000x more information** from your existing process data and makes it available in real time. We connect to your instruments, transform raw data into quantitative materials-state information, and surface insights that help you detect anomalies, diagnose drift, and optimize processes during the run, not after it. We call this the **Integrated Process Environment (IPE)**: a unified system that brings real-time and asynchronous measurements into a single feedback loop for materials processing. Atomscale operates through three stages: 1. **Connect**: Tool-specific adapter models ingest and unify raw data across your make and measure instruments. Data flows in through real-time streaming interfaces, file watchers, or our programmatic client, with subsecond inference. 2. **Analyze**: A timeseries foundation model embeds adapter model outputs into similarity embeddings that provide a quantitative foundation for comparison. This answers questions like: *How is the current run the same or different from previous runs?* and *How uniform is this run from segment to segment?* — with resolution from individual atomic layers up to whole-recipe sequences. 3. **Act**: Process intelligence flags out-of-distribution runs, identifies trends toward anomalies, and provides the foundation for real-time recipe adjustments and closed-loop process control. Adapter models are proprietary pipelines customized to specific data types (e.g., RHEED video, ellipsometry signals, tool sensor logs). They transform raw source data in real time into generalized, comprehensive fingerprints of the data stream. This enables frictionless use of the data for large-scale pattern recognition and automation, without requiring you to build custom analysis for each data type. Traditional process control relies on parametric statistical models built from a design of experiments, with fixed setpoints and periodic manual adjustments. This approach has several limitations: the design space explodes in complexity for modern tools, most process data goes unused because it isn't actionable in real time, and feedback only happens after deposition is complete. Atomscale works directly with raw data instead of point-solution outputs, eliminates rigid statistical assumptions, and provides real-time, short-loop feedback at scale. In demonstrated cases, Atomscale detects changes in material state **40 seconds earlier** than manual observation, with higher consistency, no noise artifacts, and no reliance on classical image processing. | Atomscale enables | Replacing | | - | - | | Real-time physical property extraction across material systems | Manual analysis with point solutions | | Information extraction from file artifacts into a unified data model | Catalogs of files in proprietary formats | | Rapid generation of internally consistent, machine-readable datasets | Noisy, subjective conclusions from manual analysis | | Recognition of proxy relationships mapping external measurements to real-time feedback | Feedback between trials only after full measurement sets | | Reactive process control informed by direct materials feedback | Indirect process control informed only by tool controllers | Results depend on your process and data, but demonstrated outcomes include: * **Earlier anomaly detection**: Detecting nucleating surface reconstructions 40 seconds ahead of manual observation * **High-accuracy predictions**: Surrogate models for wafer uniformity achieving >96% accuracy from recipe and sensor data alone * **In-situ composition estimation**: Correlating diffraction features with ex-situ composition measurements to enable real-time composition predictions on future runs * **Trial success prediction**: Predicting growth success or failure in 90% of cases from an initial set of roughly 10 labeled samples * **Quantitative layer-by-layer comparisons**: Automatically differentiating growth conditions and doping compositions from raw ellipsometry data in ALD workflows More broadly, customers see improved yield consistency, faster process optimization cycles, and the ability to identify process–outcome correlations that might otherwise take months to discover. ## Supported Tools & Data Atomscale supports the following deposition methods: * **Molecular beam epitaxy (MBE)**: most mature capabilities * **Chemical vapor deposition (CVD / MOCVD)** * **Atomic layer deposition (ALD)** * **Physical vapor deposition (PVD)** * **Sputtering** * **Atomic layer etch**: in active development The platform is designed to be flexible across tool types. If you're working with a deposition method not listed here, [reach out](mailto:support@atomscale.ai) to discuss your setup. Atomscale integrates data from a wide range of in-situ and ex-situ characterization techniques: * **Diffraction**: RHEED, XRD, XRR * **Spectroscopy**: XPS, Raman, Photoluminescence, Ellipsometry, OES, SIMS * **Microscopy**: SEM, AFM, Optical images Our adapter model architecture is designed to extend to new data types. If your instrument generates structured or streaming data, we can likely support it. Our customers work across advanced materials applications including: * **Silicon photonics** — barium titanate on silicon, perovskite/silicon tandems * **III-V compound semiconductors** — GaN, GaAs, InP, SiC, and combinations for photonics, optoelectronics, and quantum devices * **Next-generation transistors** — controlling chemical composition for 2D FET channel materials * **Quantum cascade lasers** — real-time feedback for dynamic process control across alternating layer stacks * **Advanced magnets and functional materials** If you're working on thin film deposition of active electronic or photonic materials, Atomscale can likely help — [contact us](mailto:support@atomscale.ai) to discuss your specific use case. ## Integration & Getting Started Atomscale connects to your existing instruments and data sources — we work alongside your current setup rather than replacing anything. The platform ingests data through multiple interfaces including real-time streaming, file watchers, and a programmatic API client. We support standard control interfaces including recipe files, control commands, and SECS/GEM API. Insights are delivered through our web interface, API, and real-time alerts. We use a three-stage approach: 1. **Proof of concept with historical data**: We onboard your existing data at no cost to demonstrate value on your specific process. This validates that our models extract meaningful information from your data. 2. **Integration with live data**: We configure the platform for your production environment with real-time data connections, alerting, and controls integration. 3. **Always-on monitoring**: Based on demonstrated value, we ramp to continuous operation with every-run monitoring, operator assistance, and (where appropriate) automated intervention. Our serial model architecture is designed for exactly this. The adapter models handle tool-specific data transformation, while the foundation model provides generalized pattern recognition. This means the platform adapts to your specific processes without requiring massive retraining. Even with small labeled datasets — as few as 10 samples in some cases — we can produce practical models for continuous tunability over your process design space. Atomscale offers three deployment options: * **Cloud (Web UX)** — Hosted platform accessible through your browser * **API** — Programmatic integration for automation workflows and custom tooling * **On-premises** — Local deployment for environments with strict data requirements Your process data stays within the agreed deployment boundary. We work with your security and IT teams to meet your organization's requirements. ## Pricing & Engagement Atomscale uses a **usage-based model** tied to the volume of data ingested and processed. This aligns our pricing with the value you receive — you pay for what you use, and costs scale with your operations rather than requiring a large upfront commitment. The initial proof of concept uses historical data and is provided at no cost. Atomscale serves several personas: * **Process engineers and module owners**: Primary users who develop, maintain, and diagnose the process of record. They interact with the platform daily during runs. * **Metrology engineers**: Use the platform to bridge in-situ and ex-situ measurements, building stronger connections between characterization and process. * **Engineering managers and fab directors**: Track KPIs including yield improvement, downtime reduction, and product variance. Typically the decision makers and economic buyers. ## Company Background Advanced, application-specific materials are critical for the next generation of technological progress — but compelling materials often get stuck at the lab scale. We can propose new materials faster than we can prove they work in production. Atomscale is building purpose-specific AI to bring real-time feedback, visibility, and automation to advanced materials manufacturing. Our product vision progresses through three stages: 1. **Real-time analysis**: A platform to find an edge in your data by using 100% of your signal in real time 2. **Virtual characterization**: Intelligence layer models that predict the state of the process relative to past runs or in absolute terms 3. **Self-driving process of record**: Adaptive, active process control that optimizes for consistency of output rather than consistency of tool state By enabling dynamic control of complex process physics, we're creating a new standard for how advanced materials get made, with the repeatability and precision needed to scale electronics, photonics, quantum technologies, and energy storage. Atomscale was founded by **Chris Price** and **Jason Munro**, two materials scientists with a shared goal of turning decade-long synthesis campaigns into tractable computational problems. **Chris Price** has spent nearly a decade at the intersection of quantum mechanics, AI, and physics-based modeling. His career spans deep technical research and enterprise data products, focused on translating advanced science into commercially viable technologies. He holds a PhD from the University of Pennsylvania. **Jason Munro** earned his PhD in computational materials science from Penn State and continued as a staff scientist at Lawrence Berkeley National Laboratory. There, he served as a lead developer of the [Materials Project](https://materialsproject.org), the world's largest open database of simulated materials data. The team brings expertise spanning materials science, physics, AI/ML, high-performance computing, scientific software, and life sciences. Personal backgrounds, advisors, and investors span institutions including Duke, Penn, Berkeley, The Materials Project, Notre Dame, MIT, and the Semiconductor Research Corporation. # IP and Data Source: https://docs.atomscale.ai/platform/reference/ip-and-data Data ownership, privacy controls, and IP protection Atomscale handles sensitive research and manufacturing data. This page covers what data we collect, how we protect your intellectual property, and the privacy controls available to you. **Last reviewed**: July 2026. We review this policy at least annually and update it whenever our data practices change. ## Data Ownership Your data remains yours. Atomscale's [Terms of Service](/platform/reference/terms-of-service) establish clear ownership boundaries: * **Customer data ownership**: All data you upload, generate, or derive within Atomscale belongs to your organization. * **Full export rights**: You can export your data at any time in standard, non-proprietary formats (CSV, JSON, TIFF, HDF5). * **Deletion on request**: When you delete data or close your account, your data is permanently removed from all systems, including backups, within 30 days. ## Data We Collect ### Customer Data Data you upload or generate through the platform: | Data Type | Examples | Storage | | - | - | - | | Measurement data | RHEED images, XPS spectra, ellipsometry readings | Encrypted object storage | | Process data | Growth recipes, tool parameters, timeseries | Encrypted database | | Analysis results | Fingerprints, similarity scores, anomaly flags | Encrypted database | | Metadata | Sample names, project labels, annotations | Encrypted database | ### Platform Usage Data Data generated by your use of the platform: | Data Type | Purpose | Retention | | - | - | - | | API request logs | Troubleshooting and rate limiting | 90 days | | Feature usage metrics | Product improvement | Aggregated, 1 year | | Error reports | Reliability improvement | 30 days | Usage metrics are aggregated and never include the contents of your research data. We track *that* an analysis was run, not *what* the analysis contained. ## Data Isolation ### Workspace-Level Isolation Each organization's data is logically isolated at the infrastructure level: | Layer | Isolation Mechanism | | - | - | | Database | Separate schemas per organization with row-level security | | Object Storage | Dedicated prefixes with IAM-enforced access boundaries | | Processing | Isolated compute contexts for analysis workflows | | Caching | Organization-scoped cache partitions | ### Cross-Organization Protections Atomscale enforces strict boundaries between organizations: * No shared queries across organization boundaries * No data leakage through search, recommendations, or analytics * Separate encryption keys per organization * Independent audit logs per organization ## Third-Party Sharing Atomscale does not sell customer data or share it with third parties for marketing. Customer data leaves our systems only in these cases: * **Infrastructure subprocessors**: Data is stored and processed on cloud infrastructure (AWS) under encryption; subprocessors act on our instructions and cannot access your data for their own purposes. * **AI assistant requests**: When you invoke AI features, the relevant data excerpts are sent to the configured model provider to fulfill your request. Providers are contractually barred from training on this data. * **Legal requirements**: We disclose data only when required by law, and we notify affected customers unless legally prohibited. Connecting an external client to your account (for example an MCP client such as Claude) authorizes that client to act as you; data returned to it is governed by that client's own privacy terms. ## Privacy Controls ### Data Deletion When you delete data from Atomscale: 1. **Immediate**: Data is removed from the active database and becomes inaccessible. 2. **Within 24 hours**: Data is purged from all caches and search indices. 3. **Within 30 days**: Data is removed from encrypted backups. For account closure, all organization data follows this same deletion timeline. ### Data Export Export your data at any time through: * The platform UI (individual files, datasets, or project-level exports) * The API (programmatic bulk export) * A formal data portability request to [support@atomscale.ai](mailto:support@atomscale.ai) ## Contact For questions about data handling, privacy, or this policy, or to exercise a data request (export, deletion, correction), contact [support@atomscale.ai](mailto:support@atomscale.ai). ## Related Documentation Security architecture, access controls, and compliance. The terms governing your use of the platform, APIs, and SDKs. # Data Items Source: https://docs.atomscale.ai/platform/reference/models/data-items Individual instrument and characterization files Data items are individual files uploaded to Atomscale. Each data item represents a single measurement or recording from an instrument: a RHEED video, an XPS spectrum, an SEM micrograph, a tool state log, and so on. ## Data Types Every data item is classified by its data type. The platform supports these types, grouped by category: ### RHEED | Data Type | Description | | - | - | | RHEED | RHEED video from a rotating or stationary substrate. Rotation is detected automatically. | | RHEED Image | A single RHEED pattern image. | | RHEED Radial Scan | A radial scan sequence for lattice-spacing analysis. | ### Spectroscopy | Data Type | Description | | - | - | | XPS | X-ray photoelectron spectroscopy spectrum. | | Raman | Raman spectroscopy data. | | Photoluminescence | Photoluminescence emission spectrum. | | OES | Optical emission spectroscopy data. | | ARPES | Angle-resolved photoemission spectroscopy data. | ### Imaging | Data Type | Description | | - | - | | SEM | Scanning electron microscopy image. | | Optical Image | Optical microscopy or camera image. | ### X-ray | Data Type | Description | | - | - | | XRD | X-ray diffraction data. | | XRR | X-ray reflectivity data. | ### Other | Data Type | Description | | - | - | | Tool State | Instrument log data from the growth tool (temperatures, pressures, flow rates). | | Ellipsometry | Ellipsometry measurement data. | | SIMS | Secondary ion mass spectrometry data. | | Recipe | Growth recipe file. | | Other | Any file type not covered above. | ## Pipeline Status After upload, each data item moves through a processing pipeline. The status reflects where it is in that process: | Status | Description | | - | - | | Pending | Queued for processing. | | Running | Analysis workflow is actively processing. | | Success | Processing completed successfully. | | Error | Processing failed. | | Stored | File stored but not processed (quarantined). | | Stream Active | Live stream is currently recording. | | Stream Paused | Live stream is paused. | | Stream Interrupted | Live stream was unexpectedly interrupted. | | Stream Finalizing | Live stream is being finalized into a data item. | | Stream Error | An error occurred during streaming. | ## Associations Each data item can be linked to: | Association | Relationship | Description | | - | - | - | | Physical Sample | Optional, one | The growth run this data came from. | | Growth Instrument | Optional, one | The instrument that produced this data. | | Target Material | Optional, one | The material being measured. | | Tags | Many-to-many | Flexible labels for filtering and grouping. | | Notes | Optional, one | Free-form notes attached to this data item. | You can set these associations from the Data catalog table or from a data item's detail page. ## Adding Data The **Add Data** dialog provides four methods for getting data into the platform: 1. **Upload**: manually select files from your computer. Uploaded files can trigger [automations](/platform/reference/workflows) if configured. 2. **Stream**: record your screen to stream RHEED data directly into the platform. Only RHEED data is currently supported for streaming. 3. **File Watcher**: watch a local directory for new files and upload them automatically. The recommended way is the **File Watcher** integration in the [Atomscale desktop app](/platform/reference/desktop-app), which uploads using your signed-in session (see [Integrations](/platform/reference/connecting-data/integrations)). In the browser, this tab offers the legacy standalone [File Watcher](/platform/reference/connecting-data/file-watcher) Windows application, which requires an API key from your user settings. The desktop app's **Add Data** dialog doesn't show this tab. 4. **API Integration**: use your API key to programmatically upload data. See the [API reference](/api-reference/upload/get-upload-urls) for details. ## Related Group data items by growth run. Process data items to extract analysis results. # Overview Source: https://docs.atomscale.ai/platform/reference/models/index Understanding Atomscale's data model and core concepts Atomscale organizes data around three core entities: **Projects**, **Physical Samples**, and **Data Items**. These form a containment hierarchy that mirrors how experimental work is structured. ## Hierarchy ``` Organization └── Projects └── Physical Samples └── Data Items ``` | Level | Description | Example | | - | - | - | | Organization | Your company or institution. All users and data belong to one. | "Acme Semiconductors" | | Project | Groups physical samples for comparison and analysis. | "GaN LED optimization" | | Physical Sample | Represents a growth run, grouping related data from that run. | "GaN-2024-01-15" | | Data Item | An individual uploaded file: a RHEED video, XPS scan, recipe, etc. | "rheed\_rotating\_001.mp4" | Physical samples can belong to multiple projects, and data items can exist without being assigned to a physical sample. ## Core Entities Group physical samples related to a specific initiative, allowing you to view and analyze all their data in one place. Represent growth runs and group instrument and characterization data from the same run for comparison and analysis. Individual metrology, instrument, and characterization files uploaded to Atomscale. ## Metadata You can associate metadata with physical samples and data items to add context and enable filtering: | Metadata Type | Description | Example | | - | - | - | | Growth Instrument | The synthesis instrument used (MBE, PVD, sputter, CVD, ALD, PLD). | "MBE-1", "Sputter System A" | | Target Material | The material being grown, including substrate and layer information. | "GaN on Sapphire" | | Tags | Flexible labels organized by category for filtering and grouping. | "experiment:doe-q1", "priority:high" | Each of these metadata types can be managed from the **Metadata** section in the sidebar. Notes can also be attached to projects, physical samples, and data items for free-form context. # Projects Source: https://docs.atomscale.ai/platform/reference/models/projects Grouping physical samples for comparison and analysis Projects group physical samples related to a specific initiative, allowing you to view and analyze all their data in one place. A project can contain any number of physical samples, and a physical sample can belong to multiple projects. ## The Project Page A project is free-form and displays four sections: 1. **Notes**: free-form text that auto-saves as you type 2. **Physical Samples**: the samples assigned to this project, with a count badge and a link to add more 3. **Analysis Results**: a chart aggregating processed results across all data items in the project 4. **Similarity**: explore how data items in this project compare to others in your organization ## Creating a Project To create a project, provide a name and optional notes. After creation, you can add physical samples from the Physical Samples page by assigning them to the project using the **Projects** column. ## Related The core unit contained within projects. Individual files grouped under physical samples. # Physical Samples Source: https://docs.atomscale.ai/platform/reference/models/samples Representing growth runs and grouping related data Physical samples group instrument and characterization data from the same run together for comparison and analysis. You can think of a physical sample as the central organizing unit for a single experiment or growth. ## Associations Each physical sample can be linked to the following: | Association | Relationship | Description | | - | - | - | | Growth Instrument | Optional, one | The synthesis instrument used for this growth (MBE, PVD, CVD, etc.). | | Target Material | Optional, one | The material being grown, including substrate and layer definitions. | | Projects | Many-to-many | Physical samples can belong to multiple projects. | | Data Items | One-to-many | Individual files (RHEED, XPS, SEM, etc.) linked to this physical sample. | | Notes | Optional, one | Free-form notes that auto-save as you edit. | ## Creating a Physical Sample When creating a physical sample, you provide: 1. **Sample Name** (required): a descriptive name for the growth run 2. **Growth Instrument** (optional): select from instruments configured in Metadata 3. **Target Material** (optional): select from materials configured in Metadata 4. **Notes** (optional): goals, remarks, processing conditions, or other context After creation, you can link data items to the physical sample from the Data page by setting the **Physical Sample** column on each data item. ## Detail Page The physical sample detail page shows: 1. **Header**: editable name with a "Physical Sample" badge 2. **Metadata bar**: last modified date, growth instrument, target material, project assignment, and owner 3. **Notes**: auto-saving free-form text 4. **Data Items**: a list of all data items linked to this physical sample, with a count badge and a link to add more from the Data page 5. **Analysis Results**: a chart aggregating processed results across all linked data items 6. **Similarity**: explore how this physical sample's data compares to others in your organization ## Related Individual files grouped under physical samples. Group physical samples for cross-sample analysis. # Monitoring Source: https://docs.atomscale.ai/platform/reference/monitoring/index Watch growths in real time and review completed sessions on the Monitor page The **Monitor** page lets you watch growths in real time as they stream into Atomscale, and review completed sessions afterward. It brings live RHEED video, growth metrics, and tool state together in one view so you can follow a run as it unfolds. ## The Three Regions The Monitor page is divided into three regions: The left sidebar. Ongoing and completed growths, organized by campaign. The center. Live video and growth metrics for the selected session. The right sidebar. Activity, metadata, and resources for the selected session. Both side regions are collapsible, so you can give the center content more room when you need it. ## Sessions The Sessions sidebar lists the growths available to monitor and review. Click **Monitor new growth** (or press Ctrl+N) to begin streaming a new growth. See [Starting a Growth](/platform/reference/monitoring/starting-a-growth) for the entry points. Sessions are split into an **Ongoing** group and a **Completed** group. Ongoing sessions show a live indicator and elapsed time; completed sessions show the date they ran. Within each group, sessions are organized by campaign. A recency filter controls how many days of sessions appear. If a group is empty, you'll see "No sessions in the last X days. Adjust the recency filter to show more." Increase the range to surface older growths. Select any session to load it in the center [Session View](/platform/reference/monitoring/session-view). ## Next Begin a new monitoring session by streaming live data. Read the live and completed session view, panel by panel. # Session View Source: https://docs.atomscale.ai/platform/reference/monitoring/session-view Read the live and completed session view, panel by panel Selecting a session loads it in the center **Content** region, with the **Details** sidebar on the right. The view adapts to whether the session is streaming live or already complete. ## Content A breadcrumb at the top links to the session's **Project** and **Sample**. The left (Sessions) and right (Details) sidebars are collapsible, so you can widen the content area while you watch a run. ### Session controls While a growth is streaming, you control the session with **Pause**, **Resume**, and **End**, and an elapsed-time timer shows how long it has been running. A status indicator reflects the stream state (for example, active, paused, or interrupted). For a completed session, a playback bar replays the growth. Use play/pause, switch the speed between **1x**, **8x**, and **16x**, and drag the scrubber to jump to any point in the run. ### Panels The content area shows three kinds of panels for the selected session: * **RHEED Video**: one panel per azimuth, with its own settings menu. Each panel plays the live stream while the growth is running, or the recorded video for a completed session. * **Growth Metrics**: charts of derived RHEED metrics with a chart settings menu. While streaming, a latency indicator shows how current the live data is. * **Tool State**: charts of instrument parameters during the growth. A layout toggle switches the charts between horizontal and vertical arrangements. The live session view shows **RHEED Video**, **Growth Metrics**, and **Tool State**. When a panel has no data you'll see an empty state instead: * **No RHEED video found for this wafer.** Stream a new growth or attach a RHEED data item to the physical sample to populate the view. * **All RHEED data is hidden.** Re-enable RHEED items from the **Resources** tab to show them. * **No tool state data available.** Add or stream tool state data to the physical sample to view it. ## Details The Details sidebar groups information about the selected session into three tabs. A timeline of detected events and changepoints during the growth. Filter the timeline by **Label**, **Deviation**, and **Assigner**. You can relabel an event and edit its label's description. Changepoint detection is temporarily disabled, so new growths won't add changepoints to the timeline until it's turned back on. Editable notes for the growth, plus wafer details (name, start time, target material, growth instrument) and the campaign it belongs to. The data items attached to the session's physical sample, grouped by type: RHEED, Tool State, and any other data groups. Toggle items on or off to control what appears in the content panels. Each group shows an empty state when no data of that type is attached. # Starting a Growth Source: https://docs.atomscale.ai/platform/reference/monitoring/starting-a-growth Begin a new monitoring session by streaming live growth data To monitor a growth in real time, you start a stream from the Monitor page. Click **Monitor new growth** (or press Ctrl+N), then pick how you want to send data to Atomscale. ## Entry Points Stream growth data by capturing a screen or window from your browser. Best when your RHEED software runs on the same machine as your browser. Stream growth data programmatically via the Atomscale Python SDK. Best for headless systems or custom acquisition pipelines. ### Screen capture Choose the window or screen showing your instrument output and frame the capture area. Frames are uploaded and analyzed continuously while the session runs. See [Screen Capture](/platform/reference/connecting-data/screen-capture) for the full capture flow, including positioning the capture area and configuring stream settings. ### Programmatic Use the Atomscale Python SDK to open a session and stream RHEED frames from your own systems. See [Programmatic](/platform/reference/connecting-data/programmatic) for connecting via the SDK, and [Stream RHEED](/sdk/stream-rheed) for the streaming API. Once the stream starts, you're taken to the live [Session View](/platform/reference/monitoring/session-view), where the growth appears in the Ongoing group as it runs. # Onboarding Source: https://docs.atomscale.ai/platform/reference/onboarding Account creation, organization setup, and user management This page covers how users sign up for Atomscale, how organizations are structured, and how to manage members and roles. ## Account Creation Creating a new account requires the following steps: Request an invitation from an existing Admin user in the organization. An email with a link to join the organization is sent. After accepting the invitation, create your account with an email and password, or use a supported social login. Atomscale provisions your user record and organization membership. This takes a few seconds. After sync, you're taken to your organization's home page. If you are trying to create an organization, please contact [support@atomscale.ai](mailto:support@atomscale.ai) to provision the first admin account. ## Organizations Every Atomscale user belongs to an organization. Organizations are the top-level container for all projects, data, and members. ## Inviting Members Organization admins can invite new members from organization settings. Click on your Organization name in the sidebar, then select Manage. Enter the email addresses of the people you want to invite and click Invite. Invited users receive an email with a link to sign up or sign in and join the organization. Invitations are restricted to email addresses that share your organization's domain. For example, if your account is `alice@acme.com`, you can only invite other `@acme.com` addresses. ## Roles Each member of an organization has one of the following roles: | Role | Description | | - | - | | Admin | Invite members and configure organization settings | | Member | Standard access to projects, data, and workflows | Roles are assigned when a user is invited and can be changed later by an admin. ## Managing Your Account Access your personal settings by clicking your User name in the bottom-left corner of the sidebar. From there you can: * Update your profile information and email * Change your password * Manage API keys from the Access tab ## Related Documentation Security architecture, access control, and audit logging. # Security Source: https://docs.atomscale.ai/platform/reference/security Data isolation, encryption, and organizational safeguards Atomscale secures, encrypts, and completely isolates your data from other organizations using industry best practices. All data encrypted at rest and in transit using organization-specific keys. Strictly partitioned by organization with no cross-org access. OAuth 2.0 + MFA authentication, hashed API keys, least privilege. ## Data Architecture Each organization's data is encrypted with a unique key. Data cannot be fetched without the matching organization key. Every data request is authenticated against the requesting organization's encryption key. Without a matching key, data is inaccessible — even to Atomscale engineers. Within each organization, data is organized into separate categories: * **Process data**: run records and streaming process information * **Tool state**: equipment and instrument status * **Metrology**: characterization and measurement data * **Context**: materials system labels, stored separately from raw data to protect combined IP ## Data Security ### Strict partitioning Data is strictly partitioned by organization. There is zero cross-organization access or comingling. ### Encryption everywhere All data is encrypted at rest and in transit using industry-standard protocols. ### Organization-specific keys Data cannot be fetched without a matching organization encryption key. Each organization has a unique key. ### Separated storage Raw data and labeled context (materials system) are stored in separate locations to protect combined IP. ### US data residency All data is stored in US data centers for US entities. ## Access Controls Industry-standard OAuth 2.0, multi-factor authentication (MFA), and hashed API keys with secure endpoints. CISO compliant. Passed multiple independent customer security audits. SOC 2 audit in process. Tight 3rd and 4th party exposure control. Security audits of all vendors used. Minimal external dependency surface. Security-in-depth principles. Regular patching and updating. Best practices for securing all infrastructure. ## Desktop App and Integrations This section summarizes how the [Atomscale desktop app](/platform/reference/desktop-app) and its [integrations](/platform/reference/connecting-data/integrations) behave on your network. For more detail, see the integrations [Technical Details](/platform/reference/connecting-data/integrations#technical-details). **Outbound connections** use HTTPS on port 443. The main destinations are: | Destination | Purpose | | - | - | | `api.atomscale.ai` | Atomscale API | | Atomscale-owned Amazon S3 buckets (`s3.amazonaws.com` and `s3.us-east-1.amazonaws.com` endpoints) | File uploads and integration downloads | | `atomscale-desktop-updates.s3.us-east-1.amazonaws.com` | App update feed | | `browser-intake-datadoghq.com` | Diagnostic log intake | | `clerk.atomscale.ai`, `accounts.atomscale.ai` | Sign-in (Clerk) | **Inbound connections** are allowed only through program-scoped Windows Firewall rules, each added after administrator approval: * **Live RHEED capture**: The **RHEED Camera (GenTL)** integration receives the camera's stream from the local subnet over UDP. * **Connection Sharing Hub mode**: A Hub accepts TCP connections from the local subnet and relays them only to a fixed list of hosts the Atomscale app uses. * **Integration admin access**: Turning on this setting, which the **Network (GVSP Tap)** RHEED capture source requires, registers a Windows scheduled task that runs as SYSTEM. It also adds an inbound TCP rule for the Atomscale app that is not limited to the local subnet. **Credentials on the computer**: Integrations hold no API keys. The app stores your sign-in session encrypted with Windows DPAPI, plus a random per-installation device secret that identifies the computer to Atomscale. ## Team Security All employees and the organization are bound by strict confidentiality agreements. Internal firewalls ensure your information stays protected. * **Least privilege access**: Information is only available to team members who need to know or access it. * **Confidentiality agreements**: All employees are bound by comprehensive confidentiality agreements. * **Security training**: All team members have completed security training and background checks. * **Internal firewalls**: Customer service teams operate with internal information barriers. ## Support Model Atomscale maintains a support account within each customer organization to assist with configuration, customer setup, and continuous improvement. This account operates securely as a member of your organization. * **Operates within the silo**: The Atomscale support account lives inside your organization's data boundary, subject to the same encryption and isolation as your team. * **No data leaves the silo**: System configuration and data setup are performed in-place. Data is never extracted, copied, or moved outside the organizational boundary. * **Same security guarantees**: The support account is governed by the same org-specific encryption key, access controls, and audit trail as all other users. * **Scoped access**: Support access is limited to configuration and setup tasks, following least-privilege principles. # Terms of Service Source: https://docs.atomscale.ai/platform/reference/terms-of-service The terms governing access to and use of the Atomscale platform, APIs, SDKs, and related services Atomic Data Sciences, Inc. d/b/a Atomscale **Effective**: July 29, 2026 These Terms of Service (the “Terms”) govern access to and use of Atomscale’s websites, hosted platform, software, application programming interfaces, software development kits, documentation, AI-enabled features, workflows, and related services (collectively, the “Services”). BY ACCESSING OR USING THE SERVICES, PLACING AN ORDER, OR ACCEPTING AN ORDER FORM THAT REFERENCES THESE TERMS, CUSTOMER AGREES TO THESE TERMS. THE INDIVIDUAL ACCEPTING THESE TERMS REPRESENTS THAT THEY HAVE AUTHORITY TO BIND CUSTOMER. THE SERVICES ARE OFFERED FOR BUSINESS USE ONLY, NOT FOR PERSONAL OR CONSUMER USE. ## 1. Agreement to Terms These Terms are between Atomic Data Sciences, Inc., a Delaware corporation doing business as Atomscale (“Atomscale”), and the business or other legal entity on whose behalf the Services are accessed or used (“Customer”). Customer may authorize its employees, contractors, and other personnel to use the Services on its behalf (“Authorized Users”) and is responsible for their compliance with these Terms. The Services may be purchased through online checkout or an ordering document, statement of work, or other ordering mechanism (each, an “Order Form”). If Customer and Atomscale enter into a master services agreement, subscription agreement, data processing addendum, Order Form, or other written agreement covering the Services, that agreement controls to the extent it expressly conflicts with these Terms. A purchase order does not modify the parties’ agreement unless Atomscale expressly agrees in writing. ## 2. Intellectual Property Rights Atomscale and its licensors retain all right, title, and interest in the Services; software; documentation; models; model weights; algorithms; methods; templates; workflows; interfaces; designs; and all related improvements and intellectual property (“Atomscale Technology”). Subject to the parties’ agreement and payment of applicable fees, Atomscale grants Customer a limited, non-exclusive, non-transferable, non-sublicensable right during the applicable subscription term to access and use the Services and documentation for its internal business purposes. Customer receives no rights except those expressly granted. Components distributed under open-source or other separate licenses are governed by those licenses to the extent of a conflict. Neither party may use the other’s marks publicly without permission. ## 3. User Representations Customer represents and warrants that the person accepting these Terms has authority to bind Customer; account and billing information will be accurate and current; Customer and its Authorized Users will use the Services only for lawful business purposes; and Customer has all rights and permissions necessary to provide Customer Data and authorize its processing. Customer is responsible for account administrators, permissions, integrations, and access controls; for protecting credentials, API keys, tokens, and certificates; and for promptly notifying Atomscale at [support@atomscale.ai](mailto:support@atomscale.ai) of suspected unauthorized access. Customer will not share Authorized User login credentials, API keys, or access tokens with any person who is not an Authorized User bound by these Terms. Customer must ensure that qualified personnel review and validate AI-assisted outputs and process recommendations before relying on them. ## 4. Fees and Payment Pricing, currency, billing frequency, usage allowances, subscription term, and renewal terms are those presented at checkout or stated in the applicable Order Form. Customer will pay all fees when due. Except as expressly provided in these Terms or an Order Form, payment obligations are non-cancelable and fees paid are non-refundable. Overdue undisputed amounts may accrue interest at the lesser of 1.5% per month or the maximum lawful rate, plus reasonable collection costs. Fees exclude sales, use, value-added, withholding, and similar taxes. Customer is responsible for taxes arising from its purchases, other than taxes based on Atomscale’s net income. ## 5. Cancellation A subscription renews automatically only if the checkout flow or applicable Order Form expressly says that it will. Customer may cancel a self-service renewal through available account controls or by contacting Atomscale before the renewal date. Unless otherwise stated, cancellation stops renewal and takes effect at the end of the then-current paid term; it does not create a refund for that term. Termination rights for breach are stated in Section 12. ## 6. Prohibited Activities Customer may access and automate the Services through interfaces, APIs, SDKs, clients, agents, and integrations that Atomscale provides, documents, or expressly authorizes, subject to applicable technical limits and the Order Form. Customer will not, and will not permit a third party to: use the Services unlawfully or violate another person’s rights; introduce harmful code; probe or test vulnerabilities without prior written authorization; bypass access controls, rate limits, usage limits, or security measures; interfere with the Services or another customer’s use; access another organization’s data; use unauthorized scraping or automated means; rent, resell, sublicense, or provide the Services as a standalone service bureau; reverse engineer or attempt to discover source code, model weights, or non-public algorithms except where law prohibits the restriction; or systematically extract protected elements of the Services to build or train a competing general-purpose product. Customer will additionally not use the Services, including any AI Outputs, as the basis for controlling safety-critical equipment or any activity where the failure or inaccuracy of the Services could reasonably be expected to result in death, serious bodily injury, or significant environmental harm, unless expressly authorized in a separate written agreement. ## 7. User Generated Contributions The Services are not designed as a public content-sharing forum. Data, files, measurements, recipes, tool parameters, time-series data, images, spectra, metadata, annotations, prompts, and other content that Customer or its systems submit to the Services are “Customer Data.” Customer is responsible for the legality, accuracy, and quality of Customer Data and for maintaining any backups appropriate to its operations. Customer Data must not include content that Customer lacks authority to provide or that would make Atomscale’s processing unlawful. Customer will not submit, and will not permit any Authorized User to submit, any of the following to the Services unless Atomscale has expressly authorized that specific use in writing: (a) protected health information regulated by HIPAA; (b) credit card, debit card, bank account, or other financial account numbers; (c) Social Security numbers, government-issued identification numbers, or other similarly sensitive government identifiers; or (d) special categories of personal data as defined under the GDPR or a comparable data protection law (collectively, “Restricted Data”). Before submitting personal data governed by the GDPR or a substantially similar law, Customer must first accept Atomscale’s then-current data processing addendum. Submission of Restricted Data in violation of this Section is a material breach of these Terms, and Customer is solely responsible for any resulting losses. ## 8. Customer Data As between the parties, Customer owns all Customer Data and the customer-specific analysis, outputs, and results Atomscale generates for Customer from it, including fingerprints, similarity scores, predictions, and anomaly flags specific to Customer’s data (“Customer Outputs”). Customer Data and Customer Outputs exclude Atomscale Technology, Aggregated Data, and Usage Data, and exclude any general models, improvements, or know-how that Atomscale develops, all of which remain owned by Atomscale. Customer grants Atomscale and its subprocessors a limited, worldwide, non-exclusive, royalty-free license to host, copy, transmit, process, display, analyze, and otherwise use Customer Data to provide, secure, support, and maintain the Services; to develop, train, and improve Atomscale’s products, services, and machine learning models; to comply with law; to prevent fraud or abuse; to fulfill Customer’s instructions; and for any other lawful business purpose. Atomscale may also generate, use, and disclose Aggregated Data and Usage Data for any lawful business purpose, including to operate, secure, analyze, benchmark, market, and improve the Services and Atomscale’s business. “Aggregated Data” means statistical, derived, or analytical information based on Customer Data that does not identify Customer, an Authorized User, or any particular customer. “Usage Data” means information about how the Services are accessed and used, such as logs, performance metrics, feature-usage, and configuration data, that is not itself Customer Data. Aggregated Data and Usage Data are owned by Atomscale. Customer may export Customer Data using available platform or API functionality or by making a portability request. Exports will be available in standard, non-proprietary formats such as CSV, JSON, TIFF, or HDF5 where applicable. When Customer deletes data or closes its account, Atomscale will remove the data from active systems and make it inaccessible, purge it from caches and search indices within 24 hours, and remove it from encrypted backups within 30 days. Atomscale may retain limited records required by law if they remain protected and unused for other purposes. Atomscale will maintain reasonable administrative, technical, and organizational safeguards designed to protect Customer Data. When Customer invokes an AI or integration feature that uses a third-party provider, Customer authorizes Atomscale to send the data excerpts reasonably necessary for that request. Atomscale requires subprocessors to follow Atomscale’s instructions and prohibits configured model providers from using Customer Data to train their models. Atomscale does not sell personal User Data or share it for third-party marketing. Additional information appears in the Atomscale [IP and Data Policy](/platform/reference/ip-and-data). Where a signed master services agreement, subscription agreement, or data processing addendum between the parties expressly addresses the use of Customer Data, that agreement controls to the extent of any conflict with this Section, consistent with Section 1. Otherwise, if the Atomscale IP and Data Policy or an applicable privacy notice conflicts with these Terms, the document providing greater protection for Customer Data controls. ## 9. Submissions Customer is not required to provide suggestions, ideas, or other feedback. If Customer voluntarily provides feedback, Customer retains ownership of it and grants Atomscale a worldwide, perpetual, irrevocable, royalty-free, transferable, and sublicensable license to use and incorporate the feedback without restriction or attribution. Atomscale will not publicly identify Customer as the source without permission. This Section does not apply to Customer Data. ## 10. U.S. Government Rights The Services and documentation are “commercial products,” “commercial computer software,” and “commercial computer software documentation” as those terms are used in applicable U.S. federal acquisition regulations. Any use by or on behalf of the U.S. Government is subject to these Terms and only those rights customarily provided to other business customers, consistent with applicable law. ## 11. Site Management Atomscale may manage, monitor, maintain, and secure the Services; establish reasonable technical and usage limits; deploy updates; and remove or restrict material that violates the parties’ agreement or law. Customer authorizes Atomscale to connect to and exchange data with Customer systems and third-party integrations as Customer configures them. Third-party products and services remain subject to their own terms. Atomscale may suspend affected access if reasonably necessary to address a security risk, unlawful activity, material violation, harm to the Services or another customer, or overdue undisputed fees. Where practicable, Atomscale will give advance notice, limit suspension to the affected portion, and restore access promptly after resolution. Emergency action may be taken without advance notice. ## 12. Term and Termination These Terms begin when Customer accepts them or first uses the Services and continue while Customer has access. Each paid subscription continues for the term stated at checkout or in the applicable Order Form. Either party may terminate an applicable agreement for material breach if the breach is not cured within 30 days after written notice, or within 10 days for nonpayment. Either party may terminate immediately if the other ceases business without a successor or becomes subject to insolvency proceedings not dismissed within 60 days. On expiration or termination, Customer’s right to use the affected Services ends, except for an agreed transition period, and accrued fees remain due. If Customer terminates for Atomscale’s uncured material breach, Atomscale will refund prepaid fees for the unused portion of the terminated subscription. If Atomscale terminates for Customer’s uncured material breach, unpaid committed fees become due to the extent permitted by law. On expiration or termination, Customer will immediately cease all use of the Services, including through any cached credentials, API keys, or integrations, and, upon Atomscale’s request, will delete or destroy any copies of Atomscale Technology in its possession or control; this does not apply to Customer Data, which Customer may retain or export as otherwise permitted under these Terms. Provisions that by their nature should survive will survive. ## 13. Modifications and Interruptions Atomscale may improve, update, or modify the Services. Atomscale will not materially reduce the core functionality of a paid Service during a current subscription term without providing a commercially reasonable alternative, except when needed for security, legal, third-party, or technical requirements. Atomscale may modify usage limits, rate limits, or pricing applicable to free or self-serve tiers at any time on reasonable notice (for example, by posting a notice in the product or emailing Customer’s account contact), effective prospectively. Service levels, support commitments, implementation services, and on-premises or edge deployments apply only if stated in an Order Form or separate agreement. The Services may be unavailable because of maintenance, emergencies, third-party systems, or events outside Atomscale’s reasonable control. Atomscale does not guarantee uninterrupted operation unless a separate service-level commitment applies. Trials, previews, prototypes, and beta features may be changed or discontinued at any time and are provided without service levels unless otherwise agreed. Atomscale may update these Terms by posting a revision with a new effective date and giving reasonable notice of material changes. For an active paid subscription, a materially adverse change will generally take effect at renewal unless earlier application is required by law or reasonably necessary for security or abuse prevention. ## 14. Governing Law These Terms and any dispute arising from them are governed by the laws of the Commonwealth of Massachusetts, without regard to conflict-of-law principles. The United Nations Convention on Contracts for the International Sale of Goods does not apply. ## 15. Dispute Resolution Before filing a claim, the party raising the dispute will give the other written notice describing the issue and requested relief. The parties will try in good faith for 30 days to resolve it through business representatives with authority to settle. This requirement does not prevent a party from seeking urgent injunctive or equitable relief. Any proceeding that is not resolved informally must be brought exclusively in the state or federal courts located in Suffolk County, Massachusetts, and each party consents to personal jurisdiction and venue there. EACH PARTY KNOWINGLY AND IRREVOCABLY WAIVES, TO THE FULLEST EXTENT PERMITTED BY LAW, ANY RIGHT TO A JURY TRIAL IN A PROCEEDING ARISING OUT OF OR RELATING TO THESE TERMS OR THE SERVICES. Nothing limits either party’s right to seek equitable relief in any court of competent jurisdiction to protect intellectual property or Confidential Information. Customer acknowledges that any actual or threatened breach of Section 2 (Intellectual Property Rights), Section 6 (Prohibited Activities), or the confidentiality provisions of these Terms would cause Atomscale irreparable harm for which monetary damages would be an inadequate remedy, and that Atomscale is entitled to seek injunctive or other equitable relief, without the necessity of posting a bond where permitted by law, in addition to any other available remedy. ## 16. Corrections The Services and related materials may contain typographical errors, inaccuracies, or omissions. Atomscale may correct them and update information at any time. If a correction materially changes a pending online order before acceptance, Atomscale will notify Customer and allow Customer to confirm or cancel the affected order without charge. ## 17. Disclaimer For a paid subscription, Atomscale warrants that the Services will materially conform to the applicable documentation when used as authorized. Customer must notify Atomscale of a material nonconformity within 30 days after discovery. Atomscale will use commercially reasonable efforts to correct it. If Atomscale cannot do so within a reasonable period, either party may terminate the affected Service and Atomscale will refund prepaid unused fees. This is Customer’s exclusive remedy for breach of this warranty. The Services may generate predictions, classifications, similarity results, anomaly alerts, analyses, code, recommended parameters, or other AI-assisted outputs (“AI Outputs”). AI Outputs are probabilistic and may be incomplete, inaccurate, or unsuitable for a particular process, instrument, material system, or operating condition. Qualified personnel must independently review and validate them. Unless a separate written agreement expressly defines an automated-control deployment, AI Outputs must not be the sole basis for safety-critical equipment control. Customer remains responsible for appropriate interlocks, operating limits, fail-safe behavior, validation, oversight, and compliance. EXCEPT FOR THE EXPRESS WARRANTY ABOVE AND TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SERVICES, AI OUTPUTS, DOCUMENTATION, TRIALS, AND BETA FEATURES ARE PROVIDED “AS IS” AND “AS AVAILABLE.” ATOMSCALE AND ITS LICENSORS DISCLAIM ALL IMPLIED AND STATUTORY WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, NONINFRINGEMENT, AND WARRANTIES ARISING FROM COURSE OF DEALING OR USAGE OF TRADE. ATOMSCALE DOES NOT WARRANT UNINTERRUPTED OR ERROR-FREE OPERATION, ACCURATE OR COMPLETE AI OUTPUTS, OR ANY PARTICULAR TECHNICAL, PROCESS, YIELD, PERFORMANCE, SAFETY, OR BUSINESS RESULT. ## 18. Limitations of Liability TO THE MAXIMUM EXTENT PERMITTED BY LAW, NEITHER PARTY NOR ITS LICENSORS WILL BE LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL DAMAGES; LOSS OF PROFITS, REVENUE, GOODWILL, OR BUSINESS OPPORTUNITY; LOSS OR CORRUPTION OF DATA; OR BUSINESS INTERRUPTION, EVEN IF ADVISED THAT SUCH DAMAGES WERE POSSIBLE. TO THE MAXIMUM EXTENT PERMITTED BY LAW, EACH PARTY’S TOTAL AGGREGATE LIABILITY ARISING OUT OF OR RELATING TO THE SERVICES, THESE TERMS, AND ALL APPLICABLE ORDER FORMS WILL NOT EXCEED THE FEES PAID OR PAYABLE BY CUSTOMER FOR THE AFFECTED SERVICES DURING THE 12 MONTHS BEFORE THE EVENT GIVING RISE TO LIABILITY. IF CUSTOMER USED ONLY FREE SERVICES, ATOMSCALE’S TOTAL LIABILITY WILL NOT EXCEED US\$100. These exclusions and caps do not apply to Customer’s payment obligations; either party’s fraud, gross negligence, or willful misconduct; Customer’s infringement or misappropriation of Atomscale Technology; either party’s breach of confidentiality; or liabilities that cannot lawfully be limited. The limitations apply regardless of the form of action and are a fundamental basis of the parties’ agreement. ## 19. Indemnification For paid Services, Atomscale will defend Customer against a third-party claim alleging that Customer’s authorized use of the unmodified Services infringes a United States patent, copyright, or trademark, and will indemnify Customer against damages and reasonable costs finally awarded or agreed in an approved settlement. This obligation does not apply to claims arising from Customer Data, Customer specifications, unauthorized use, modifications not made by Atomscale, combination with items not supplied by Atomscale, continued use after notice, or trials and beta features. Atomscale may procure continued use, modify or replace the affected Service, or terminate it and refund prepaid unused fees. Customer will defend Atomscale and its affiliates against third-party claims arising from Customer Data, Customer’s products or processes, Customer’s unlawful or unauthorized use, or Customer’s material breach of Section 6, and will indemnify them against damages and reasonable costs finally awarded or agreed in an approved settlement, except to the extent caused by Atomscale’s breach. The indemnified party must give prompt notice, reasonable cooperation at the indemnifying party’s expense, and control of the defense and settlement to the indemnifying party. Delayed notice reduces obligations only to the extent it materially prejudices the defense. No settlement may admit fault by or impose non-monetary obligations on the indemnified party without its written consent. ## 20. Electronic Communications, Transactions, and Signatures Customer consents to receiving agreements, notices, disclosures, invoices, and other communications electronically. Electronic acceptance, records, and signatures satisfy legal requirements that communications be in writing or signed and have the same effect as originals. Customer is responsible for maintaining current legal, billing, and administrative contact information. ## 21. Miscellaneous “Confidential Information” means non-public information disclosed by one party to the other that is marked confidential or reasonably should be understood as confidential. Customer Data is Customer’s Confidential Information; Atomscale’s non-public technology, security information, product plans, and pricing are Atomscale’s Confidential Information. The recipient will use Confidential Information only for the parties’ agreement, protect it with at least reasonable care, and disclose it only to personnel, advisers, contractors, and subprocessors who need to know it and are bound by protective obligations. Information lawfully public, previously known without restriction, received lawfully from another source, or independently developed is not Confidential Information. Legally compelled disclosure is permitted with prompt notice where lawful. On termination of an agreement or upon the disclosing party’s written request, the receiving party will delete or destroy the disclosing party’s Confidential Information in its possession, except as required by law or the receiving party’s standard backup and archival practices, in which case this Section will continue to apply to the retained information. Neither party may assign an agreement without prior written consent, except to an affiliate or with a merger, reorganization, acquisition, or sale of substantially all relevant assets if the assignee assumes the agreement and is not the other party’s direct competitor. The parties are independent contractors. These Terms create no partnership, joint venture, agency, fiduciary, employment relationship, or third-party beneficiary. Neither party is liable for delay caused by events beyond its reasonable control, including internet or infrastructure outages, cyberattacks or hacking incidents (whether affecting Atomscale or a third-party provider), acts of government, natural disasters, or public health emergencies, but this does not excuse payment for Services already provided. Each party will comply with applicable trade laws, and Customer will not use the Services for a prohibited end use or by or for a restricted party. Customer represents and warrants that it is not (a) located in, or a resident or national of, any country subject to a comprehensive U.S. embargo; (b) identified on the U.S. Treasury Department’s Specially Designated Nationals list, the U.S. Commerce Department’s Denied Persons or Entity List, or any comparable restricted-party list; or (c) fifty percent (50%) or more owned, in the aggregate, by one or more parties described in (a) or (b). Atomscale may immediately suspend or terminate Customer’s access, without liability, if Atomscale reasonably believes such action is necessary to comply with export control or economic sanctions laws. A waiver must be written. If a provision is unenforceable, it will be modified to the minimum extent necessary and the remainder will continue. “Including” means “including without limitation,” and headings are for convenience. Legal notices to Atomscale may be sent to [support@atomscale.ai](mailto:support@atomscale.ai) or to the address in Section 22, Attn: Legal. Atomscale may send notices to Customer’s legal or administrative contact. These Terms, applicable Order Forms, and incorporated written agreements are the entire agreement for the Services and supersede prior communications. Conflicts are resolved in this order: signed master agreement; signed data processing addendum for data matters; Order Form; these Terms; and documentation. ## 22. Contact Us Questions and legal notices may be sent to [support@atomscale.ai](mailto:support@atomscale.ai) or to: Atomic Data Sciences, Inc. d/b/a Atomscale
76 Summer St., Suite 410
Boston, MA 02110
Attn: Legal ## Related Documentation Data ownership, privacy controls, and IP protection. Security architecture, access controls, and compliance. # Changepoint Detection Source: https://docs.atomscale.ai/platform/reference/workflows/changepoint-detection Real-time changepoint detection on RHEED streams, with labels inherited from reference changepoints in a project The Changepoint Detection workflow monitors RHEED streams in real time and flags changepoints as a growth unfolds: the moments where the detected surface reconstruction changes. Each detected changepoint is matched against reference changepoints you have already labelled elsewhere in the same project, so recurring events inherit consistent, human-meaningful labels. Surfacing these events as they happen lets you respond quickly to unexpected changes in deposition. When enabled, it runs automatically on [RHEED](/platform/reference/models/data-items#rheed) data. Detected changepoints are labelled automatically once the sample's project has labelled reference changepoints. See [Setting up reference changepoints](#setting-up-reference-changepoints). Changepoint detection is temporarily disabled. New runs won't generate changepoints until it's turned back on. This page describes how detection works when it's enabled. ## Detection Process For each monitored RHEED stream: 1. **Detect**: As the stream arrives, the workflow tracks the surface reconstruction detected in the RHEED pattern. A changepoint fires when the reconstruction changes between states, for example 2x1 to 4x1, or 2x1 to no detectable reconstruction. 2. **Score severity**: Each changepoint is assigned a severity (**High**, **Moderate**, or **Low**) based on which transition occurred. By default, every RHEED transition is **Moderate**. 3. **Label from references**: Each new changepoint is compared with the nearest labelled reference changepoints in its project and assigned a label with a confidence score. If the result isn't confident enough, the changepoint stays **Unlabelled** until someone labels it. ## Setting up reference changepoints Automatic labels are based on changepoints you have already labelled in the same [project](/platform/reference/models/projects). Changepoints are still detected without labelled references, but they stay unlabelled. Setting a project up as the label source takes a few steps. Pick the [project](/platform/reference/models/projects) that will hold both the reference samples and the sample you want to monitor, or create a new one. Add the [physical samples](/platform/reference/models/samples) from one or more earlier growths to the project, then label the changepoints on their RHEED data items. Reference changepoints are found through the samples in the project, and automatic labeling favors the labels already used on them. Labels themselves are shared across your organization, so you can reuse the same label in any project. Add the physical sample for the new growth to the same project, so its detected changepoints can be matched against the references. Start streaming. Detected changepoints are matched against the project's references and labelled automatically as they appear. If the project has no labelled changepoints for this workflow yet, detected changepoints are still flagged, but they remain unlabelled. Label the matching changepoints on samples already in the project to use them as references. Only labelled changepoints with the same data modality and property are considered as references. Every RHEED changepoint is recorded on the detected surface reconstruction, so any labelled RHEED changepoint in the project can serve as a reference for a new one. ## How changepoints appear Detected changepoints surface in two places in the [Monitor](/platform/reference/monitoring) view. **As bars on the timeseries chart**: In the Growth Metrics panel, each changepoint is drawn as a colored vertical band over the chart at the time it occurred. The color encodes severity: red for **High**, amber for **Moderate**, and blue for **Low**. By default, the chart shows only **High** changepoints, so RHEED changepoints (which default to **Moderate**) are hidden until you turn them on. To show them, open the chart settings (the gear icon, or right-click the chart and choose **Chart Settings**) and turn on **Moderate** and **Low** under **Changepoints**, or click **Enable All**. Instantaneous changepoints are widened to a minimum so they stay visible, and the time axis can be switched between elapsed time and absolute timestamps. **In the activity timeline**: The Details sidebar's [Activity tab](/platform/reference/monitoring/session-view) lists every changepoint as a row, ordered through the run, showing its time, a short description, and its label (or **Unlabelled**) in a badge colored by severity. Select a row to see its **Confidence**, **Deviation**, and **Assigner**. You can filter the timeline by **Label**, **Deviation**, and **Assigner**, relabel an event with **Manual Relabel** (where you can also edit the label's description), or request a suggested label with **Assistant Relabel**. An info card shows which project is supplying the labels and the labels available. ## Severity Severity drives the color of the changepoint in the chart and timeline. For RHEED, it comes from which reconstruction transition occurred: | Severity | Color | Meaning | | - | - | - | | **High** | Red | The most severe level. Investigate promptly. | | **Moderate** | Amber | Worth review. RHEED reconstruction changes default to this level. | | **Low** | Blue | A minor event. | RHEED severities come from a built-in table of transitions, so your organization's saved configuration doesn't change them. ## Detection Detection runs incrementally as the stream arrives, so changepoints are flagged in near real time rather than only after a run completes. For RHEED, the workflow follows the surface reconstruction classified in each frame, and changepoints are recorded on the `detected_rheed_reconstruction` property. Two rules shape the raw output: * **Debounce**: a new reconstruction must persist for several consecutive frames before the transition counts, which suppresses brief flickers. * **Transition table**: only listed transitions fire, each with its own severity. By default, these are 2x1 to 4x1 and 2x1 to no detectable reconstruction, in either direction, all at **Moderate**. 1x1 frames are treated as 2x1, so a change between 1x1 and 2x1 never fires, and neither does a change between no detectable reconstruction and 4x1. ## Reference-based labeling When a changepoint is detected, the workflow looks for the closest labelled reference changepoints and uses them to assign a label: * **Candidate pool**: labelled changepoints on data items in the same project (linked through their physical samples), restricted to the same data modality and property as the new changepoint. * **Ranking**: candidates are ranked by how close they sit in the run to the new changepoint (the distance between their window centers). Labels confirmed by a person are preferred over unconfirmed automatic suggestions, which keeps earlier guesses from compounding into the taxonomy. Up to five of the closest candidates are used. * **Label and confidence**: the new changepoint is assigned a label, favoring labels already used on those references, along with a confidence score. Confidence is capped at 90%, so an automatic label never shows as certain. * **Confidence gate**: results below 50% confidence are discarded, and the changepoint stays unlabelled rather than guessed. * **Cold start**: if the project has no labelled reference for that modality and property, no label is applied. The API can also propagate labels across a chosen scope: a single data item, all data items of a physical sample, an entire project, or every data item of the workflow. The app has no control for this. ## Severity Assignment For RHEED, severity comes from the transition table rather than the size of a shift. The table is built in, and organization settings don't change it. ## Configuration The Changepoint Detection workflow's **Saved Configurations** holds one configuration for your organization, with a single setting: **Cooldown Period** (in ms), the minimum time between consecutive detections. RHEED detection uses a built-in configuration instead, so the saved configuration doesn't affect RHEED changepoints. When detection is enabled, RHEED detection runs whether or not a saved configuration exists. # Workflows Source: https://docs.atomscale.ai/platform/reference/workflows/index Composable analysis pipelines that process your data Workflows are the analysis pipelines that Atomscale runs on your data. Each workflow processes raw instrument data through a sequence of steps to extract metrics, detect patterns, or produce derived outputs. They run automatically when data arrives and store their results for visualization, comparison, and export. ## Characterization Workflows Each characterization method on the platform is a workflow. When you upload or stream data from an instrument, the corresponding characterization workflow runs automatically to extract metrics and features specific to that technique. Surface structure analysis from diffraction patterns and intensity oscillations. Elemental composition from photoelectron spectra. Optical property timeseries from spectroscopic ellipsometry. Video segmentation and morphology tracking. Particle segmentation and morphological analysis. Spectral analysis of photoluminescence emission. Vibrational spectroscopy for material composition. Depth profiling of elemental composition. See the [Characterization overview](/platform/characterization) for details on each technique. ## Cross-Cutting Workflows These workflows operate across characterization data to provide additional analysis. Embed characterization timeseries into 2D for visual comparison, and track similarity to reference items over time. Detect changepoints on RHEED streams in real time, labelled from reference changepoints in a project. Parse, clean, and analyze instrument log files from synthesis tools. # Similarity Source: https://docs.atomscale.ai/platform/reference/workflows/similarity Embed characterization timeseries for visual comparison and track similarity to reference items over time The Similarity workflow computes embeddings from characterization timeseries data and uses them for two purposes: projecting data items into a 2D map for visual comparison, and tracking how similar a live item is to selected reference items over time. Similarity is scoped per data type and per organization. Each data type has its own embedding space. Supported data types are RHEED, RHEED Radial Scan, Tool State, and OES. ## Embedding Projection The embedding pipeline takes characterization timeseries, encodes them into high-dimensional embeddings, and projects those embeddings down to 2D coordinates. The result is a scatter plot where data items with similar characterization profiles appear near each other. When enough data items are available, the pipeline can use a landmark-based projection that scales better to large datasets. The projection refreshes on demand and tracks its status (fresh, updating, stale, or error) so the frontend knows when to show updated coordinates. ## Similarity Trajectory The trajectory pipeline computes how similar a tracked item is to one or more reference items over time. It slides a window across the tracked item's timeseries, encodes each window into an embedding, and compares it against precomputed reference prototypes. Two execution modes are available: * **Stream**: A single incremental update, typically triggered when new data arrives from the RHEED pipeline. Appends one new similarity score to the existing trajectory. * **Historical**: Recomputes the full trajectory by iterating through the entire timeseries. Used when a user initiates a new comparison. When data items have angle tags (common with RHEED), the pipeline groups comparisons by matching angles so that, for example, a \[100] azimuth item is only compared against \[100] references. ## Embedding Pipeline The embedding pipeline loads characterization embeddings for a given data type and organization, then projects them to 2D coordinates using dimensionality reduction. When enough data is available, the pipeline can use a landmark-based projection that scales better to large datasets. Projection state is persisted for incremental updates. ## Trajectory Pipeline The trajectory pipeline computes a similarity timeseries between a tracked item and one or more reference items. Reference items are encoded into prototype vectors (computed once and cached). The pipeline then slides a window across the tracked item's timeseries, encodes each window, and compares it against the reference prototypes to produce a similarity score at each time step. In stream mode, new scores are appended incrementally. In historical mode, the full trajectory is recomputed from scratch. # Tool State Source: https://docs.atomscale.ai/platform/reference/workflows/tool-state Parse, clean, and analyze instrument log files from synthesis tools The Tool State workflow processes instrument log files from synthesis tools, extracts timeseries data for each parameter, and runs signal analysis to characterize oscillation behavior and decay. This page covers log files uploaded as complete files. Logs streamed live from the desktop app's **Tool Log Streamer** integration are processed differently. See [Live Tool Logs From the Desktop App](#live-tool-logs-from-the-desktop-app). ## What It Does The tool state pipeline takes raw log files uploaded from an instrument and produces structured timeseries that you can plot, compare across runs, and monitor for anomalies. The pipeline handles format parsing, data cleaning, signal analysis, and anomaly detection in a single pass. ## Supported Formats The pipeline supports three input formats: | Format | Description | | - | - | | **Tool logs** | CSV log files from synthesis tools. Extracts Ratio Pyrometer and SC Pyrometer temperature readings, then runs signal analysis. | | **Generic CSV** | Any delimited file (CSV, TSV, or other). The pipeline auto-detects the delimiter and header offset, coerces columns to numeric types, and extracts time axes automatically. | | **MOCVD recipe XML** | Recipe definition files. The pipeline expands compact recipe steps into a dense timeseries with interpolation. | ## Outputs For **tool logs**, the pipeline produces: | Output | Description | | - | - | | `ratio_pyrometer` | Raw Ratio Pyrometer temperature timeseries | | `sc_pyrometer` | Raw SC Pyrometer temperature timeseries | | `median_period_seconds` | Median oscillation period extracted from peak analysis, per segment | | `decay_constant_minutes` | Exponential decay time constant fitted to peak envelopes | | `decay_fit` | Interpolated decay fit curve | | `{property}_anomaly_score` | Embedding-based anomaly score (0 to 1) for each property | | `{property}_anomaly_class` | Classification label for detected anomalies | | `{property}_forecast` | Predicted value based on current trajectory | | `{property}_z_score` | Z-score deviation metric for each property (legacy) | | `{property}_ema_z_score` | EMA residual z-score for each property (legacy) | For **generic CSV** files, the pipeline produces a timeseries for each numeric column in the file, plus the same anomaly detection metrics. ## Tool Log Pipeline The tool log pipeline processes MOCVD temperature logs through the following stages: 1. **Data ingestion**: downloads and concatenates CSV log files into a single dataset 2. **Cleaning**: extracts pyrometer temperature columns, filters invalid readings, and computes elapsed time 3. **Segmentation**: breaks the timeseries into segments based on time gaps, discarding segments that are too short or too low in amplitude 4. **Signal analysis**: detects oscillation peaks within each segment, computes the median oscillation period, and fits a decay model to the peak envelope 5. **Changepoint detection**: runs the [Changepoint Detection workflow](/platform/reference/workflows/changepoint-detection) on each property to flag changepoints ## Generic CSV Pipeline The generic CSV pipeline auto-detects the delimiter and header structure of arbitrary delimited files, identifies time columns, and stores each numeric column as an independent timeseries with anomaly detection metrics. ## Live Tool Logs From the Desktop App The **Tool Log Streamer** [integration](/platform/reference/connecting-data/integrations) watches a folder on your tool's control computer and streams each log file to Atomscale while the tool is still writing it. Streamed logs do not use the pipelines described above: | | Uploaded log files | Tool Log Streamer | | - | - | - | | **When data arrives** | After the complete file is uploaded | Continuously, as rows are appended to the file | | **How columns are read** | Built-in parsers for the supported formats, or automatic detection for generic CSV | A parsing schema you provide, which maps each column to a channel name and unit | | **Resulting timeseries** | The outputs listed in the Overview tab, such as `ratio_pyrometer` | One channel per schema column, named exactly as you define it | | **Analysis** | Oscillation period and decay fitting for tool logs, plus anomaly detection | No oscillation or decay fitting. Anomaly detection, if configured for your organization, runs periodically while the file grows. | Atomscale keeps the original bytes of each streamed file, so the raw log is preserved even if some rows fail to parse. For help writing a parsing schema for your tool's logs, contact [support@atomscale.ai](mailto:support@atomscale.ai). # Solutions Source: https://docs.atomscale.ai/platform/solutions Atomscale delivers value for organizational leadership, manufacturing operations, and research teams Atomscale's ***integrated process environment*** accelerates how organizations operate by replacing delayed, fragmented feedback with real-time visibility and data-driven decision making across advanced materials manufacturing. ## For Organizational Leaders De-risk development timelines and accelerate the path from lab to production. The path from lab-scale demonstration to production is where most materials programs stall. For executives and program managers, the value of Atomscale is strategic: faster time-to-market with reduced development costs and lower risk of late-stage surprises. Atomscale compresses the R\&D-to-manufacturing timeline by making process transfer systematic rather than ad hoc: * Quantify operating windows so transfer targets are explicit, not inferred from tribal knowledge * Identify scale-up risks early through cross-reactor comparison and equipment fingerprinting * Preserve development knowledge so teams don't re-learn lessons when personnel change Traditional thin film development relies heavily on individual expertise and tacit knowledge. Key personnel leaving can set programs back months or years. Atomscale mitigates this by capturing process knowledge in searchable, transferable form: * Document the "why" behind recipe decisions, beyond the "what" * Every run, successful or not, becomes valuable institutional memory * New team members build on predecessors' work immediately rather than starting from scratch Every hurdle in process optimization can be accelerated when analysis setup takes minutes instead of days. Atomscale increases the analytical power available to your team: * Use 100% of collected data signal, instead of the fraction that's manually tractable * Rapidly identify relationships between process variables and material quality from small datasets * Cross-project learnings are discoverable and reusable; negative results are as informative as positive ones *** ## For Manufacturing Teams Catch problems during growth, not weeks later at characterization. Process engineers and module owners are accountable for yield, uptime, and product variance. The IPE gives them real-time visibility into the material itself, beyond tool controller state, so they can detect drift and intervene before defects occur. Most process data collected during growth goes unused because it isn't actionable in real time. Atomscale changes this: * Live dashboards with synchronized data from all connected instruments * Quantitative process fingerprints that surface what's happening in the material, beyond what the tool reports * Predictive quality models that forecast final film properties from in-situ measurements, creating real-time proxies for expensive ex-situ characterization When something goes wrong, correlate data across all instruments to pinpoint the source. Hours of investigation become minutes. * Automated comparison of affected runs against known-good references with anomaly detection * Equipment fingerprinting to detect and quantify performance differences between nominally identical reactors * Cross-reference timing against maintenance logs, chamber opens, source changes, or other logged events Link in-situ measurements to final device performance. Identify the process conditions that separate good wafers from scrap. * Detect out-of-distribution runs and trends toward process drift before they impact product * Bridge the gap between metrology and process engineering. Map external measurements to real-time feedback * Build toward adaptive process control that optimizes for consistency of output, rather than consistency of tool state *** ## For Research Teams Accelerate discovery and maximize the value of every experiment. Research in thin film materials is inherently exploratory, but that doesn't mean it has to be inefficient. Atomscale helps research teams move faster by using 100% of their data, surfacing patterns across experiments, and preserving hard-won knowledge. Identify optimal growth conditions faster through systematic parameter exploration and ML-assisted optimization. * Rapidly identify robust relationships between process variables and material quality, even from small labeled datasets * Predict likelihood of trial success from initial conditions, avoiding doomed experiments before they waste a full run * Teams typically reduce the experiments needed to hit targets by 40-60% Every growth run becomes searchable organizational memory. When students graduate or staff move on, their insights stay. * Query years of growth history by any parameter, outcome, or metadata * Identify trends and correlations across dozens or hundreds of experiments * New researchers reach independent productivity in weeks rather than months *** ## Platform Capabilities All solutions are powered by the IPE, a unified platform with deep integration into thin film workflows. | Capability | What It Enables | | - | - | | **Instrument Integration** | Connect MBE, MOCVD, ALD, PVD, and sputtering systems, plus RHEED, XPS, XRD, AFM, ellipsometry, Raman, and more | | **Real-Time Data Ingestion** | Sub-second acquisition via visual streaming, file watcher, or programmatic client | | **Information Extraction** | Tool-specific adapter models transform raw signals into quantitative fingerprints, unlocking large-scale pattern recognition from data that was previously too abstract to use | | **Process Intelligence** | Anomaly detection, predictive modeling, and relationship discovery using generalized and custom models | | **Access Methods** | Web UX, Python API, and on-premises deployment | *** ## Next Steps See specific workflows and tasks Atomscale enables. Read how organizations have implemented Atomscale successfully. # Use Cases Source: https://docs.atomscale.ai/platform/use-cases Specific tasks and workflows that Atomscale enables These are the jobs Atomscale helps you do: concrete tasks that researchers and engineers face regularly in thin film development and manufacturing. *** ## Predict Material Properties During Growth You need to know composition, surface roughness, or other material properties, but feedback only comes after you pull the sample and run ex-situ characterization hours or days later. By then, it's too late to adjust. **Without Atomscale**: Deposit the full film blind to material state. Wait for ex-situ XPS, AFM, or other measurements. Discover problems after the fact. Iterate by running another full growth cycle. **With Atomscale**: Atomscale correlates your existing in-situ monitoring data (RHEED, ellipsometry, tool sensors) with ex-situ characterization results across past runs. Even small labeled datasets can produce practical predictive relationships. Once trained, proxy models estimate material properties (composition, roughness, phase regime) from in-situ data as layers form. Signal that was previously too noisy or abstract to use becomes actionable. Adjust process parameters in response to predicted material state. Correct drift toward an undesired composition or terminate a run heading for failure before wasting a full cycle. **Example**: Correlating diffraction features with ex-situ composition measurements enables real-time composition predictions on future runs, replacing a measurement that previously required pulling the sample and running XPS. *** ## Detect and Respond to Process Drift In production, processes drift. Sources degrade, chamber conditions shift, calibrations slip. The question is whether you catch it before or after it affects your product. **Without Atomscale**: Discover drift at final characterization, days or weeks after the affected runs. Scrap material. Miss shipments. Investigate retroactively with incomplete data. **With Atomscale**: Atomscale monitors the material itself, beyond tool parameters. Quantitative fingerprints extracted from in-situ characterization (RHEED, ellipsometry, spectroscopy) surface changes in crystal structure, surface reconstruction, and growth dynamics at signal-to-noise levels too low for manual detection. At high rotation speeds needed for wafer uniformity, operators lose visual access to the material state. Atomscale preserves full process visibility by observing material state along multiple angles simultaneously, unlocking higher uniformity and faster deposition rates without sacrificing monitoring. When drift is caught early, you can adjust parameters mid-run or abort before wasting a full cycle. Atomscale provides in-process adjustments and run-to-run insights fed directly back to the operator, closing the gap between detection and action. After stabilizing, use correlation analysis across all connected instruments to identify what caused the drift. Fix the root cause rather than the symptom. **Typical outcome**: Organizations catch drift events before they impact final product, versus discovering them days later at characterization. *** ## Diagnose Why a Growth Run Failed A run didn't produce the expected results. Was it a parameter drift? Contamination? Equipment issue? Something in the substrate prep? **Without Atomscale**: Manually export logs from each instrument. Align timestamps in spreadsheets. Compare against previous runs by memory or scattered notes. Spend hours (or days) hunting for the deviation, and even then miss patterns buried in raw characterization data that require expert intuition to interpret. **With Atomscale**: Select the failed run and one or more known-good references. Atomscale automatically aligns all data streams by timestamp. The platform compares quantitative material fingerprints rather than tool parameter values. Surface transitions, reconstruction changes, and compositional shifts that were previously invisible without manual analysis of raw metrology data are surfaced automatically. Anomaly detection flags statistically significant differences. See if the deviation in one system (e.g., substrate temperature) correlates with observations in another (e.g., RHEED pattern changes or compositional drift). Annotate the run with your diagnosis. The insight becomes searchable for the next time something similar happens. **Typical time savings**: What previously took 4-8 hours now takes 15-30 minutes. *** ## Transfer a Process from R\&D to Production A process that works in the research lab needs to move to a manufacturing environment. This transition is where many materials programs fail. **Without Atomscale**: Transfer a recipe and hope it works. When it doesn't, troubleshoot from scratch. Struggle to distinguish equipment differences from operator differences from environmental differences. **With Atomscale**: Use existing R\&D run data to quantify how much each parameter can vary before outcomes degrade. Know which parameters are critical and which have margin, without dedicated characterization experiments. Run baseline experiments on the production equipment. Compare its fingerprint to the R\&D system to understand exactly where the systems differ. Based on system differences, identify which recipe parameters need modification before the first transfer attempt. Run confirmation experiments with real-time monitoring. Verify that production results fall within the established operating window. **Typical outcome**: Process transfers that previously took 6-12 months complete in 2-4 months with higher first-pass success rates. *** ## Optimize a New Recipe You have a baseline process that works, but you need to improve a specific property, whether uniformity, growth rate, interface sharpness, or something else. **Without Atomscale**: Run experiments based on intuition. Track results in notebooks or spreadsheets. Struggle to remember what you already tried. Miss subtle parameter interactions. Spend weeks running characterization to understand the effect of each change. **With Atomscale**: Rapidly identify robust relationships between process variables (temperature, flux ratio, timing) and material quality (roughness, composition, uniformity), even from small labeled datasets. Atomscale's information extraction captures signals that manual analysis misses. Build surrogate models that predict outcomes across the parameter space. Understand which variables matter, which interact, and where the boundaries of your process window lie, without running every combination. Each run is automatically logged with complete process and characterization data. No manual data entry. No forgotten conditions. Every experiment builds on the last. Use process-property models to guide your next experiments toward the target. Reach your goal in fewer runs by letting the data direct exploration rather than intuition alone. **Typical outcome**: Teams report reaching optimization targets in 40-60% fewer experimental runs compared to traditional approaches. *** ## Next Steps See detailed examples of these workflows in practice. Start using Atomscale with your own data. # Python SDK Source: https://docs.atomscale.ai/sdk/index Programmatic access to Atomscale for automation and custom analysis The `atomscale` Python SDK provides programmatic access to the Atomscale platform, enabling you to upload data, search the catalogue, retrieve analysis results, and stream RHEED video in real time. ## Features * **Upload files** - Send RHEED videos, images, XPS, XRD, photoluminescence, Raman, ellipsometry, and tool-state data for automated analysis * **Search the catalogue** - Query your data with filters for type, status, date ranges, and keywords * **Retrieve results** - Access time series data, extracted frames, pattern graphs, and processed files * **Query RHEED features and masks** - Pull the full low-level feature set and per-frame segmentation masks * **Compare growths** - Run similarity queries over embedding vectors and track similarity trajectories * **Organize by sample and project** - Group data across runs and align timeseries from multiple instruments * **Stream data** - Push RHEED frames and instrument readings for live analysis * **Poll updates** - Monitor streaming analysis with synchronous or async polling utilities ## Quick example ```python theme={null} from atomscale import Client client = Client() # Uses AS_API_KEY environment variable # Search for recent uploads results = client.search(keywords="GaN", status="success") print(results[["Data ID", "Status", "Sample Name"]]) # Get analysis for the first result data_id = results["Data ID"].iloc[0] analysed = client.get(data_id) # Access time series data timeseries = analysed[0].timeseries_data print(timeseries.tail()) ``` ## Installation ```bash theme={null} pip install atomscale ``` Requires Python 3.10 through 3.12. ## Next steps Install the SDK and create your first client. Send files to Atomscale for analysis. Find items in your data catalogue. Work with analysis outputs. Query low-level features and segmentation masks. Compare growths with embedding vectors. Group data and align timeseries across instruments. Push frames from your instrument in real time. Push instrument readings for real-time tracking. Monitor streaming analysis updates. ## API reference Full Client class documentation. RHEEDStreamer and TimeseriesStreamer documentation. # Inspect Results Source: https://docs.atomscale.ai/sdk/inspect-results Work with analysis outputs, time series data, and extracted frames Use `client.get()` to fetch analysis results for items in the catalogue. The method returns result objects with time series data, extracted frames, pattern graphs, and more. ## Fetch analysed items ```python theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") # Search for completed analyses search_results = client.search(keywords="demo", data_type="rheed") # Get analysis results analysed = client.get(search_results["Data ID"].to_list()) ``` Each item in `analysed` is a result object depending on the source data type: `RHEEDVideoResult`, `RHEEDImageResult`, `XPSResult`, `XRDResult`, `PhotoluminescenceResult`, `RamanResult`, `ToolStateResult`, `OpticalResult`, `RecipeResult`, or `EllipsometryResult`. Anything the SDK does not recognize comes back as `UnknownResult` with the raw catalogue entry attached. ## Inspect time series data For video results, access the `timeseries_data` attribute to get a DataFrame with per-frame metrics: ```python theme={null} video_item = analysed[0] timeseries = video_item.timeseries_data print(timeseries.columns) print(timeseries.tail()) ``` The timeseries frame contains specular intensity, strain metrics, cluster IDs, and other summary features for every frame in the video. `get()` returns the standard feature set. For the full low-level feature space, per-frame segmentation masks, or a time window, use [`get_rheed_timeseries()`](/sdk/rheed-features) instead. ## Work with extracted frames Video results include snapshot images at key moments: ```python theme={null} snapshot = video_item.snapshot_image_data[0] # Get a matplotlib figure figure = snapshot.get_plot() # Access the detected diffraction pattern as a NetworkX graph fingerprint = snapshot.pattern_graph # Get a tidy table describing each spot df = snapshot.get_pattern_dataframe() ``` The `pattern_graph` exposes the detected diffraction network, while `get_pattern_dataframe()` returns positions, intensities, and labels for each spot. ## Inspect spectroscopy results For XPS, XRD, photoluminescence, and Raman results, access the spectral data and generate plots: ```python theme={null} # XPS xps_result = analysed[0] print(xps_result.predicted_composition) # dict of element → fraction print(xps_result.detected_peaks) figure = xps_result.get_plot() # XRD xrd_result = analysed[0] print(xrd_result.two_theta) print(xrd_result.intensities) print(xrd_result.wavelength_angstrom) # Cu Kα by default figure = xrd_result.get_plot() # Photoluminescence pl_result = analysed[0] print(pl_result.energies) print(pl_result.intensities) figure = pl_result.get_plot() # Raman raman_result = analysed[0] print(raman_result.raman_shift) print(raman_result.intensities) figure = raman_result.get_plot() ``` XRD peaks are dicts with `two_theta`, `intensity`, `d_spacing_angstrom`, `prominence`, and `fwhm_degrees`. ## Inspect tool-state, optical, and ellipsometry results These result types carry timeseries data from instrument sensors, optical imaging, and ellipsometry fits: ```python theme={null} # Tool state (pyrometer, pressure, etc.) tool_state_result = analysed[0] print(tool_state_result.timeseries_data.columns) print(tool_state_result.timeseries_data.tail()) # Optical imaging optical_result = analysed[0] print(optical_result.timeseries_data.columns) # Edge Perimeter, Circularity, etc. # Access optical snapshots if optical_result.snapshot_image_data: snapshot = optical_result.snapshot_image_data[0] snapshot.processed_image.show() # PIL Image # Ellipsometry ellipsometry_result = analysed[0] df = ellipsometry_result.timeseries_data print(df.filter(like="psi_").columns) # per-wavelength channels print(df["thickness"].tail()) # scalar fits ``` ## Fetch detected changepoints `get_changepoints()` returns the changepoints the platform detected in a run, so you can flag transitions without re-analysing the timeseries yourself: ```python theme={null} changepoints = client.get_changepoints( data_ids=search_results["Data ID"].to_list(), detection_method="intensity_profile", severity="critical", ) print(changepoints[["data_id", "property_name", "score", "window_start_elapsed"]]) ``` By default this returns only `critical` changepoints from the latest `intensity_profile` run. Pass `None` to either filter to widen the search, `latest_only=False` for every historical run, or `as_dataframe=False` for `ChangepointResult` objects. See [Changepoint Detection](/platform/reference/workflows/changepoint-detection) for how the detection methods differ. ## Download processed files Save processed output to disk. `download()` works for every data type the platform stores, not only video: ```python theme={null} client.download( data_ids=search_results["Data ID"].to_list(), dest_dir="processed/", ) ``` The files mirror what you see in the web UI. ## Download raw files To download the original uploaded file instead of processed output: ```python theme={null} client.download( data_ids=search_results["Data ID"].to_list(), dest_dir="raw/", data_type="raw", ) ``` `download_videos()` is a deprecated alias for `download()`. It still works, but use `download()` in new code. ## Next steps Query low-level features and segmentation masks. Compare growths with embedding vectors. Push frames from your instrument in real time. Monitor streaming analysis updates. # Poll Time Series Updates Source: https://docs.atomscale.ai/sdk/poll-timeseries Monitor streaming RHEED analysis with sync and async polling When streaming RHEED data, use the polling utilities in `atomscale.timeseries` to receive analysis updates as they arrive. These helpers always read the RHEED timeseries for `data_id`, so they work only with RHEED videos. Each poll returns a DataFrame indexed by `["View ID", "Frame Number"]`, the same shape as [`get_rheed_timeseries()`](/sdk/rheed-features). Four entry points cover different concurrency needs: | Function | Use case | | - | - | | `iter_poll` | Synchronous loop that blocks between polls | | `start_polling_thread` | Background thread for GUI or acquisition loops | | `aiter_poll` | Async iterator for `asyncio` applications | | `start_polling_task` | Fire-and-forget asyncio task | ## Setup ```python theme={null} from atomscale import Client from atomscale.timeseries import ( iter_poll, aiter_poll, start_polling_thread, start_polling_task, ) client = Client(api_key="YOUR_API_KEY") data_id = "YOUR_STREAMING_DATA_ID" def latest_frame(df): """Return the newest frame number in the result, for deduplication.""" if df.empty or "Frame Number" not in df.index.names: return None return df.index.get_level_values("Frame Number").max() ``` ## Synchronous polling Loop over `iter_poll` to fetch fresh rows on a fixed cadence. The helper waits `interval` seconds between polls. ```python theme={null} for idx, result in enumerate( iter_poll( client, data_id=data_id, interval=5.0, last_n=10, distinct_by=latest_frame, max_polls=3, ), start=1, ): print(f"Poll {idx}: latest frame -> {latest_frame(result)}") print(result.tail()) ``` ### Parameters | Parameter | Description | | - | - | | `interval` | Seconds between polls (default: 1.0) | | `last_n` | Only fetch the last N rows (None for all) | | `distinct_by` | Function to extract a key for deduplication | | `max_polls` | Stop after this many polls | | `fire_immediately` | Poll immediately or wait one interval first (default: True) | | `jitter` | Random delay (0 to jitter) added to each sleep | | `until` | Predicate function; stop when it returns True | | `on_error` | Handler called with the exception when a poll fails | Polling errors are never raised. A failed poll is skipped and polling continues on the next tick, so pass an `on_error` handler to log failures, or re-raise inside it to stop polling. ## Background thread polling Use `start_polling_thread` when you can't block the main thread (GUI, acquisition loop): ```python theme={null} collected = [] def on_result(result): print(f"Thread received {len(result)} rows") collected.append(result) stop_event = start_polling_thread( client, data_id=data_id, interval=10.0, last_n=10, max_polls=5, distinct_by=latest_frame, on_result=on_result, ) # Later, to stop polling: # stop_event.set() ``` The function returns a `threading.Event` that you can set to stop polling gracefully. ## Async polling Two helpers integrate with `asyncio`: ### Async iterator `aiter_poll` yields results without blocking the event loop: ```python theme={null} import asyncio async def stream_updates(): async for result in aiter_poll( client, data_id=data_id, interval=5.0, last_n=10, distinct_by=latest_frame, max_polls=3, ): print(f"Async poll received {len(result)} rows") print(result.tail()) asyncio.run(stream_updates()) ``` ### Background task `start_polling_task` creates a background task that invokes an optional async handler: ```python theme={null} async def handle_async(result): print(f"Task handler received {len(result)} rows") async def main(): task = start_polling_task( client, data_id=data_id, interval=5.0, last_n=5, max_polls=3, distinct_by=latest_frame, on_result=handle_async, ) await task asyncio.run(main()) ``` ## Deduplication The `distinct_by` parameter accepts a function that extracts a hashable key from each result. Only results with a new key are yielded, so you won't process the same data twice when polling faster than updates arrive. Key on the newest `Frame Number` in the index. Frame numbers are absolute and present on every row, so the maximum grows each time new frames are analyzed. Avoid keying on the last row (`df.iloc[-1]`): rows are grouped by view, so the last row is not necessarily the newest frame in a rotating video. `UNIX Timestamp` can also be `0` for every row when the recording has no absolute start time. ```python theme={null} def latest_frame(df): if df.empty or "Frame Number" not in df.index.names: return None return df.index.get_level_values("Frame Number").max() ``` ## Drift correction All polling utilities use drift-corrected scheduling to maintain the requested cadence even if individual polls are slow. Missed ticks are skipped rather than accumulated. ## Next steps Set up RHEED streaming from your instrument. Full Client class documentation. # Poll Similarity Trajectory Source: https://docs.atomscale.ai/sdk/poll-trajectory Monitor similarity trajectory updates from streaming analysis Similarity trajectory polling tracks how the RHEED pattern evolves over time relative to reference frames. It follows the same patterns as [time series polling](/sdk/poll-timeseries) but uses dedicated functions from `atomscale.similarity`. | Function | Use case | | - | - | | `iter_poll_trajectory` | Synchronous loop that blocks between polls | | `start_polling_trajectory_thread` | Background thread for GUI or acquisition loops | | `aiter_poll_trajectory` | Async iterator for `asyncio` applications | | `start_polling_trajectory_task` | Fire-and-forget asyncio task | ## When to use trajectory polling Use trajectory polling when you need to: * Track how the diffraction pattern changes during growth * Detect when the pattern stabilizes or diverges from a reference * Implement automatic stop conditions based on similarity thresholds ## Setup ```python theme={null} from atomscale import Client from atomscale.similarity import ( iter_poll_trajectory, start_polling_trajectory_thread, aiter_poll_trajectory, start_polling_trajectory_task, ) client = Client(api_key="YOUR_API_KEY") source_id = "YOUR_DATA_OR_SAMPLE_ID" ``` The `source_id` parameter accepts either a data ID or a physical sample ID. The `Client` also exposes these four functions as methods, so you can poll without the extra imports. `client.iter_poll_similarity_trajectory(source_id, ...)` is equivalent to `iter_poll_trajectory(client, source_id, ...)`, and the same holds for the async, thread, and task variants. For a single snapshot rather than a poll loop, use [`get_similarity_trajectory()`](/sdk/similarity). ## Synchronous polling ```python theme={null} def latest_similarity(df): """Extract the latest similarity score for deduplication.""" if df.empty or "Similarity" not in df.columns: return None return df.iloc[-1]["Similarity"] for result in iter_poll_trajectory( client, source_id=source_id, interval=5.0, last_n=10, distinct_by=latest_similarity, max_polls=20, ): if not result.empty and "Similarity" in result.columns: latest = result.iloc[-1]["Similarity"] print(f"Current similarity: {latest:.3f}") ``` ## Auto-stop on threshold Use the `until` parameter to stop polling when a condition is met. By default, trajectory polling stops automatically when no trajectory is active. ```python theme={null} def similarity_stabilized(df): """Stop when similarity exceeds 0.95.""" if df.empty or "Similarity" not in df.columns: return False return df.iloc[-1]["Similarity"] > 0.95 for result in iter_poll_trajectory( client, source_id=source_id, interval=5.0, distinct_by=latest_similarity, until=similarity_stabilized, ): print(f"Similarity: {result.iloc[-1]['Similarity']:.3f}") print("Growth stabilized!") ``` ## Background thread polling For integration with instrument control software: ```python theme={null} def on_trajectory_update(result): if not result.empty and "Similarity" in result.columns: similarity = result.iloc[-1]["Similarity"] if similarity > 0.95: print("Pattern stabilized - consider stopping growth") stop_event = start_polling_trajectory_thread( client, source_id=source_id, interval=5.0, last_n=10, on_result=on_trajectory_update, ) # Stop polling when done: # stop_event.set() ``` ## Async polling ```python theme={null} import asyncio async def monitor_trajectory(): async for result in aiter_poll_trajectory( client, source_id=source_id, interval=5.0, last_n=10, max_polls=20, ): if not result.empty and "Similarity" in result.columns: print(f"Similarity: {result.iloc[-1]['Similarity']:.3f}") asyncio.run(monitor_trajectory()) ``` ## Error handling Pass an `on_error` handler to continue polling through transient failures: ```python theme={null} def handle_error(exc): print(f"Poll failed: {exc}") for result in iter_poll_trajectory( client, source_id=source_id, interval=5.0, max_polls=10, on_error=handle_error, ): print(f"Received {len(result)} rows") ``` ## Result structure Each poll yields a DataFrame with a multi-level index and the following columns: | Column | Description | | - | - | | `Reference ID` (index) | ID of the reference trajectory | | `Time` (index) | Real time in seconds | | `Similarity` | Similarity score (float) | | `Reference Name` | Display name of the reference | | `UNIX Timestamp` | Unix epoch timestamp | | `Active` | Whether the trajectory is currently active | | `Averaged Count` | Number of data points averaged | ## Next steps Monitor raw time series updates. Set up RHEED streaming from your instrument. # Quickstart Source: https://docs.atomscale.ai/sdk/quickstart Install the SDK and create your first client This quickstart walks through the essentials for calling the Atomscale API with the Python SDK. ## Prerequisites * Python 3.10 through 3.12 * An active Atomscale account * An API key from the [Atomscale Dashboard](https://app.atomscale.ai) (found in **User** > **Manage Account** > **Access**) ## Install the client ```bash theme={null} pip install atomscale ``` ## Create a client The `Client` reads `AS_API_KEY` and `AS_API_ENDPOINT` from the environment. Export the variables or pass values explicitly. ```python theme={null} import os from atomscale import Client os.environ["AS_API_KEY"] = "YOUR_API_KEY" client = Client() ``` Never commit your API key to version control. Use environment variables or a secrets manager. Override the endpoint when pointing at staging or a private deployment: ```python theme={null} client = Client( api_key="YOUR_API_KEY", endpoint="https://api.atomscale.ai/", ) ``` ## Verify the connection Run a simple search to confirm the client is working: ```python theme={null} results = client.search() print(f"Found {len(results)} items in the catalogue") ``` ## Mute progress bars For non-interactive environments like CI pipelines, pass `mute_bars=True`: ```python theme={null} client = Client(api_key="YOUR_API_KEY", mute_bars=True) ``` ## Next steps Send files to Atomscale for analysis. Find items in your data catalogue. Explore analysis outputs and plots. Push frames from your instrument in real time. # Client Source: https://docs.atomscale.ai/sdk/reference/client Complete API reference for the atomscale Client class The `Client` class is your entry point to the Atomscale API. It provides methods for searching the catalogue, fetching analysis results, querying RHEED features and masks, running similarity queries, uploading files, and downloading raw or processed data. [View source on GitHub](https://github.com/atomscale-ai/sdk/blob/main/src/atomscale/client.py) ## Constructor ```python theme={null} from atomscale import Client client = Client( api_key="YOUR_API_KEY", endpoint="https://api.atomscale.ai/", mute_bars=False, ) ``` API key for authentication. If not provided, reads from the `AS_API_KEY` environment variable. Root API endpoint. Can be overridden with the `AS_API_ENDPOINT` environment variable. Whether to suppress progress bars. Set to `True` for non-interactive environments like CI pipelines. *** ## search() Search and filter items in the data catalogue. ```python theme={null} results = client.search( keywords="GaN", include_organization_data=True, data_type="rheed", status="success", ) ``` ### Parameters Keyword or list of keywords to search all catalogue fields. Applied after other explicit filters. Whether to include catalogue entries from other users in your organization. Filter to specific data IDs. Filter by physical sample IDs. Filter by project IDs. Filter by data type. | Value | Description | | - | - | | `rheed_image` | Single RHEED image | | `rheed` | RHEED video, stationary or rotating | | `rheed_stationary`, `rheed_rotating` | Legacy aliases for `rheed`. Both match all RHEED videos, rotating or not | | `xps` | XPS spectrum | | `xrd` | XRD pattern | | `photoluminescence` | Photoluminescence spectrum | | `pl` | Alias for photoluminescence | | `raman` | Raman spectrum | | `ellipsometry` | Ellipsometry measurement | | `optical` | Optical imaging | | `tool_state` | Tool-state log (instrument sensor readbacks) | | `metrology` | Deprecated alias for `tool_state` | | `recipe` | Growth recipe | | `all` | All types | Filter by pipeline status. | Value | Description | | - | - | | `success` | Analysis completed | | `pending` | Queued for processing | | `running` | Currently processing | | `error` | Analysis failed | | `stream_active` | Streaming in progress | | `stream_interrupted` | Stream was interrupted | | `stream_finalizing` | Stream finishing | | `stream_error` | Streaming failed | | `all` | All statuses | Filter by growth length in seconds. Tuple of `(min, max)` with `None` for open bounds. Filter by upload timestamp. Tuple of `(min, max)` with `None` for open bounds. Filter by last updated timestamp. Tuple of `(min, max)` with `None` for open bounds. Deprecated alias for `last_updated`. Passing it emits a warning and will stop working in a future release. ### Returns DataFrame with columns: | Column | Description | | - | - | | `Data ID` | Unique identifier | | `File Name` | Original filename | | `Type` | Data type (rheed, rheed\_image, etc.) | | `Status` | Pipeline status | | `File Type` | File extension/format | | `File Metadata` | Format-specific metadata captured at ingest | | `Instrument Source` | Source instrument | | `Sample Name` | Associated sample name | | `Growth Length` | Duration in seconds (for videos) | | `Physical Sample ID` | Linked physical sample | | `Physical Sample Name` | Physical sample name | | `Sample Notes` | Associated notes | | `Sample Notes Last Updated` | When the notes last changed | | `Project ID` | Linked project | | `Project Name` | Project name | | `Tags` | Tags attached to the item | | `Collected Datetime` | When the data was collected | | `Upload Datetime` | When the file was uploaded | | `Last Updated` | When the entry last changed | | `Has Instrument Logs` | Whether instrument logs are linked | | `Owner` | Uploader's name | | `Workspaces` | Associated workspaces | ### Example ```python theme={null} from datetime import datetime results = client.search( keywords="GaN", data_type="rheed", status="success", upload_datetime=(datetime(2025, 1, 1), None), growth_length=(3000, None), ) print(results[["Data ID", "Status", "Sample Name"]]) ``` *** ## get() Fetch analysis results for one or more data IDs. ```python theme={null} analysed = client.get(data_ids=["44fa63b0-74da-4d25-a362-2276c80a670a"]) ``` ### Parameters Data ID or list of data IDs from the catalogue. ### Returns List of result objects. The type depends on the source data: **RHEEDVideoResult** (for rheed): * `timeseries_data` - DataFrame with per-frame metrics, indexed by `["View ID", "Frame Number"]` * `snapshot_image_data` - List of extracted frame snapshots * `rotating` - Whether the video was taken of a rotating stage, read from the stored rotation rate For the full low-level feature set, per-frame masks, or a time window, use [`get_rheed_timeseries()`](#get-rheed-timeseries) instead. **RHEEDImageResult** (for rheed\_image): * `processed_image` - Processed image as a PIL Image * `mask` - Binary segmentation mask (numpy array) * `pattern_graph` - NetworkX graph of the diffraction pattern * `get_pattern_dataframe()` - Tidy table of spot positions (params: `extra_data`, `symmetrize`, `return_as_features`) * `get_laue_zero_radius()` - Best-fit radius and center of the zeroth order Laue zone * `get_plot()` - PIL Image with overlays (params: `show_mask`, `show_spot_nodes`, `symmetrize`, `alpha`) **XPSResult** (for xps): * `binding_energies` - Array of binding energy values * `intensities` - Array of intensity values * `predicted_composition` - Dict mapping element symbols to fractional composition * `detected_peaks` - Detected peak positions * `get_plot()` - Matplotlib figure **XRDResult** (for xrd): * `two_theta` - 2-theta angle values, in the unit given by `two_theta_unit` * `intensities` - Intensity values aligned with `two_theta` * `detected_peaks` - Peak dicts with `two_theta`, `intensity`, `d_spacing_angstrom`, `prominence`, and `fwhm_degrees` * `wavelength_angstrom` - Source wavelength (default Cu Kα, 1.5406) * `get_plot()` - Matplotlib figure **PhotoluminescenceResult** (for photoluminescence): * `energies` - Energy axis values * `intensities` - Intensity values * `detected_peaks` - Peak labels and positions * `get_plot()` - Matplotlib figure **RamanResult** (for raman): * `raman_shift` - Raman shift axis values * `intensities` - Intensity values * `detected_peaks` - Peak labels and positions * `get_plot()` - Matplotlib figure **ToolStateResult** (for tool\_state): * `timeseries_data` - DataFrame with instrument readings (pyrometer, pressure, etc.) **OpticalResult** (for optical imaging): * `timeseries_data` - DataFrame with per-frame metrics (edge perimeter, circularity, etc.) * `snapshot_image_data` - List of `OpticalImageResult` snapshots, each with a `processed_image` **EllipsometryResult** (for ellipsometry): * `timeseries_data` - DataFrame with per-wavelength channels (`psi_<λ>`, `delta_<λ>`, `depol_<λ>`, `incidentI_<λ>`) and scalar fits such as `thickness` **UnknownResult** (fallback for unsupported types): * `data_type` - Type string * `catalogue_entry` - Raw catalogue metadata ### Example ```python theme={null} # Search and get results search_results = client.search(keywords="demo", status="success") analysed = client.get(search_results["Data ID"].to_list()) # Access time series data video = analysed[0] print(video.timeseries_data.columns) print(video.timeseries_data.tail()) ``` *** ## get\_rheed\_timeseries() Fetch the RHEED feature timeseries for a data entry. Unlike `get()`, which returns only the standard feature set, this method can also return the full set of low-level features, attach per-frame segmentation masks, and filter which points come back. ```python theme={null} df = client.get_rheed_timeseries( data_id, include_low_level_features=True, include_masks=True, last_n=500, ) ``` ### Parameters Data ID of the RHEED video. Restrict the result to these feature names. These are the underlying property names (for example `specular_intensity`, `referenced_strain`), which differ from the display column names in the returned DataFrame. `None` returns the standard set. Include the full set of low-level per-point features as additional columns. These keep their raw backend names, and nested values are flattened into dotted column paths. Fetch the per-frame segmentation masks (see [`get_frame_masks()`](#get-frame-masks)) and attach them as `mask_rle`, `mask_height`, and `mask_width` columns, joined on the `Frame Number` axis. Only masks for the frames the returned series spans are fetched, so this respects any `last_n` or `elapsed_seconds` window. Only return the last N points. Only return points within the last `elapsed_seconds` of the recording. ### Returns The RHEED timeseries, indexed by `["View ID", "Frame Number"]`. `Angle`, `Interval ID`, and `Azimuth Label` are regular columns. Low-level feature columns appear when `include_low_level_features=True`, and mask columns when `include_masks=True`. Mask coverage is sparse: masks exist only for featurized frames, so rows without a mask contain NA in the mask columns, as do all rows when the video has no mask artifact. ### Example ```python theme={null} from atomscale.results import decode_mask_rle df = client.get_rheed_timeseries(data_id, include_masks=True, elapsed_seconds=60.0) row = df.dropna(subset=["mask_rle"]).iloc[0] mask = decode_mask_rle(row["mask_rle"], row["mask_height"], row["mask_width"]) print(mask.shape) # (H, W) uint8, values 0/1 ``` *** ## get\_frame() Fetch a single extracted RHEED frame as a `RHEEDImageResult`. ```python theme={null} frame = client.get_frame(data_id, frame_index=0) ``` ### Parameters Data ID of the RHEED video. Index into the video's extracted-frame list. Negative indexing is supported, so `-1` returns the last extracted frame. ### Returns The frame's image result, or `None` when the video has no extracted frames, `frame_index` is out of range, or the selected frame has no image. *** ## get\_frame\_masks() Fetch per-frame RHEED segmentation masks for a processed video. Each featurized frame carries a binary mask of the diffraction pattern, encoded as a COCO run-length-encoding (RLE) counts string. ```python theme={null} masks = client.get_frame_masks(data_id, from_frame=0, to_frame=500, decode=True) ``` ### Parameters Data ID of the RHEED video, the same ID used for the video and timeseries. First absolute frame number to fetch, inclusive. Must be `>= 0`. Last absolute frame number to fetch, inclusive. `None` fetches every featurized frame from `from_frame` to the end of the video. When `True`, decode each RLE mask into an `(H, W)` uint8 array and return a dict keyed by frame number. When `False`, return the raw rows with the RLE string intact. ### Returns With `decode=False`, a list of row dicts each containing `data_id`, `processed_data_id`, `frame_number`, `mask_rle`, `mask_height`, and `mask_width`. With `decode=True`, a dict of `{frame_number: ndarray}` holding `(H, W)` uint8 masks with values 0 or 1. Returns an empty list or dict when the video has no per-frame mask artifact. `frame_number` is the absolute frame index, keyed identically to the processed video frames and the `Frame Number` axis of the timeseries. Coverage is sparse: for stationary videos every frame is featurized, but for rotating and per-azimuth videos only a sampled subset is, so returned frame numbers are not necessarily contiguous. ### Example ```python theme={null} masks = client.get_frame_masks(data_id, decode=True) frame_numbers = sorted(masks) print(f"{len(masks)} masks, frames {frame_numbers[0]}-{frame_numbers[-1]}") ``` *** ## get\_changepoints() Get changepoint detection records for one or more data IDs. ```python theme={null} changepoints = client.get_changepoints( data_ids=["44fa63b0-74da-4d25-a362-2276c80a670a"], severity="critical", ) ``` ### Parameters Data ID or list of data IDs from the catalogue. Return only changepoints from the most recently completed detection run for each `(data_id, detection_method)` pair. Pass `False` for every historical run. Filter to a single detection method: `forecasting`, `clustering`, or `intensity_profile`. Pass `None` to include all methods. Filter to a single severity level: `info`, `warning`, or `critical`. Pass `None` to include all severities. Return a DataFrame. Pass `False` for a list of `ChangepointResult` objects. ### Returns Changepoint records matching the filters. Each record carries: * `id` - Unique ID of the changepoint record * `data_id` - Data ID the changepoint was detected on * `data_modality` - Modality it was detected in (for example `rheed`) * `property_name` - Property or channel the changepoint was detected on * `severity` - `info`, `warning`, or `critical` * `score` - Normalized changepoint score in `[0, 1]` * `window_start_elapsed` / `window_end_elapsed` - Window bounds in seconds from the start of the timeseries * `detection_method` - `forecasting`, `clustering`, or `intensity_profile` * `detail` - Method-specific detail dict * `label` - Applied category label, if any See [Changepoint Detection](/platform/reference/workflows/changepoint-detection) for how the methods differ and when each fires. *** ## get\_similarity\_trajectory() Fetch a one-shot similarity trajectory for a data ID or physical sample ID. ```python theme={null} result = client.get_similarity_trajectory(source_id, last_n=200) print(result.timeseries_data.tail()) ``` ### Parameters Data ID or physical sample ID the trajectory is computed against. Similarity workflow name. Only fetch the last N points of the trajectory. Window span in seconds, forwarded to the provider. Reference data IDs to compare against. Softmax mode forwarded to the provider. Number of reference values forwarded to the provider. ### Returns Result object with attributes: * `source_id` - The queried source ID * `workflow` - Workflow name used * `window_span` - Window length in seconds * `source_data_ids` - Source data IDs included in the trajectory * `timeseries_data` - DataFrame indexed by `("Reference ID", "Time")` with columns `Similarity`, `Reference Name`, `UNIX Timestamp`, `Active`, and `Averaged Count` *** ## get\_embeddings() Fetch similarity embedding vectors for a data entry. ```python theme={null} emb = client.get_embeddings(data_id, window_span=60.0, kind="window") print(emb.vectors.shape) ``` ### Parameters Data ID to fetch embeddings for. Similarity workflow name. Window span in seconds. `window` for one time-resolved vector per window, or `prototype` for a small set of representative vectors. Number of leading vectors to skip. Maximum number of vectors to return. `None` returns all available vectors. ### Returns Result object with attributes: * `vectors` - `(n_returned, dimension)` array of embedding vectors * `dimension` - Length of each vector, `0` when the result is empty * `count` - Total vectors available before `offset` and `limit` * `truncated` - `True` when more vectors are available than were returned * `offset` - Number of leading vectors skipped * `real_times` / `unix_times_ms` - Timing arrays, for `kind="window"` * `cluster_sizes` - Windows summarized per vector, for `kind="prototype"` Metadata arrays that do not apply to the returned kind are `None`. When no embeddings exist for the requested workflow and window span, the SDK emits a `UserWarning` and returns an empty result rather than raising, so loops over many IDs keep running. *** ## query\_rheed\_embeddings() Find RHEED data items whose embeddings are most similar to a given item. Runs k-nearest-neighbour search over the embedding index using the item's own vectors. ```python theme={null} neighbours = client.query_rheed_embeddings(data_id, top_k=10) print(neighbours[["data_id", "similarity"]]) ``` ### Parameters Data ID whose vectors seed the query. Similarity workflow name. Embedding window span in seconds. Must match a span the data was embedded at. `prototype` for a coarse query, or `window` for a finer query with more lookups. Maximum neighbours to return. The backend caps this at 30. ### Returns Columns `data_id` and `similarity` (1 = identical), plus the locus columns `source_index`, `neighbor_index`, `real_time_seconds`, and `unix_time_ms`, sorted by descending similarity. Empty when the item has no embeddings for the given workflow and window span. *** ## get\_similarity\_matches() Fetch the top stored similarity matches for a source entry. ```python theme={null} matches = client.get_similarity_matches(source_id, window_span=60.0) ``` ### Parameters Data ID or physical sample ID to find matches for. Similarity workflow name. Window span in seconds. Also include the source entry's most recent, still-streaming data in the comparison. Maximum number of matches to return. `None` uses the server default. ### Returns Columns `data_id`, `item_name`, and `similarity`, one row per match. Empty with those columns when there are no matches or the source is not found. *** ## Trajectory polling wrappers Four `Client` methods wrap the polling functions in `atomscale.similarity` so you can poll a similarity trajectory without importing them separately. | Method | Style | Returns | | - | - | - | | `iter_poll_similarity_trajectory()` | Synchronous loop | `Iterator[DataFrame]` | | `aiter_poll_similarity_trajectory()` | Async iterator | `AsyncIterator[DataFrame]` | | `start_polling_similarity_trajectory_thread()` | Background daemon thread | `threading.Event` | | `start_polling_similarity_trajectory_task()` | Asyncio task | `asyncio.Task[None]` | Each takes `source_id`, `interval` (default `1.0`), and `last_n`. The thread and task variants also take an `on_result` callback. Extra keyword arguments (`distinct_by`, `until`, `max_polls`, `fire_immediately`, `jitter`, `on_error`) are forwarded to the underlying function. ```python theme={null} for frame in client.iter_poll_similarity_trajectory(source_id, interval=5.0, last_n=10): print(frame["Similarity"].iloc[-1]) if not frame["Active"].any(): break ``` See [Poll Similarity Trajectory](/sdk/poll-trajectory) for the full walkthrough. *** ## upload() Upload files for analysis. ```python theme={null} client.upload(files=[ "/path/to/rheed.mp4", "/path/to/xps.vms", ]) ``` ### Parameters List of file paths (strings) or open file handles (BinaryIO objects). Physical sample name or UUID to link the uploads to. If a name is given and no matching sample exists, one is created automatically. Project name or UUID to associate the uploads with. The project must already exist, since the SDK does not auto-create projects. When provided, `physical_sample` is required so the sample can be added to the project's tracking list. ### Returns Data IDs assigned to the uploaded files. Analysis runs asynchronously, so use `search()` or the web UI to monitor progress. ### Example ```python theme={null} # Upload by path data_ids = client.upload(files=[ "/data/growths/2025-02-10/RHEED-stationary.mp4", "/data/growths/2025-02-10/RHEED-rotating.imm", ]) # Upload into a sample and project data_ids = client.upload( files=["/data/growths/2025-02-10/RHEED-stationary.mp4"], physical_sample="GaN-2025-001", project="Nitride Buffer Study", ) # Upload from file handles with open("/data/rheed.mp4", "rb") as f: client.upload(files=[f]) ``` *** ## download() Download raw or processed files to disk. Works for every data type the platform stores, including RHEED video, XPS, XRD, PL, Raman, optical, tool state, and ellipsometry, returning whatever file format the backend has on record. ```python theme={null} client.download( data_ids=["44fa63b0-74da-4d25-a362-2276c80a670a"], dest_dir="processed/", data_type="processed", ) ``` ### Parameters One or more data IDs from the catalogue. Directory to write files to. Defaults to current working directory. Whether to download `raw` or `processed` data. ### Returns None. Files are saved to the destination directory. ### Example ```python theme={null} search_results = client.search(data_type="rheed", status="success") # Download processed videos client.download( data_ids=search_results["Data ID"].to_list(), dest_dir="processed/", ) # Download raw files client.download( data_ids=search_results["Data ID"].to_list(), dest_dir="raw/", data_type="raw", ) ``` *** ## download\_videos() Deprecated alias for [`download()`](#download), kept for backwards compatibility. It takes the same parameters and behaves identically. Use `download()` in new code. *** ## list\_physical\_samples() List all physical samples accessible to your account. ```python theme={null} samples = client.list_physical_samples() ``` ### Returns DataFrame of physical sample records with columns including Physical Sample ID, Physical Sample Name, Project ID, Project Name, Target Material, and Owner. *** ## list\_projects() List all projects accessible to your account. ```python theme={null} projects = client.list_projects() ``` ### Returns DataFrame of project records with columns including Project ID, Project Name, Physical Sample Count, Project Notes, and Owner. *** ## get\_physical\_sample() Get all data associated with a physical sample. ```python theme={null} result = client.get_physical_sample( physical_sample_id="sample-uuid", align=True, ) ``` ### Parameters The physical sample ID. Whether to include data from other users in your organization. Whether to time-align data from multiple sources. Pass `True` for outer join, or a string like `"inner"` to control the join strategy. ### Returns Result object with attributes: * `physical_sample_id` - Sample identifier * `physical_sample_name` - Sample name * `data_results` - List of all result objects for this sample * `aligned_timeseries` - Aligned DataFrame if `align` was set, otherwise `None` *** ## get\_project() Get all data associated with a project. ```python theme={null} result = client.get_project( project_id="project-uuid", align=True, ) ``` ### Parameters The project ID. Whether to include data from other users in your organization. Whether to time-align data from multiple sources. Pass `True` for outer join, or a string like `"inner"` to control the join strategy. ### Returns Result object with attributes: * `project_id` - Project identifier * `project_name` - Project name * `samples` - List of `PhysicalSampleResult` objects for each sample in the project * `aligned_timeseries` - Project-level aligned DataFrame if `align` was set, otherwise `None` *** ## list\_growth\_instruments() List all growth instruments accessible to your account. ```python theme={null} instruments = client.list_growth_instruments() ``` ### Returns List of instrument records. Each dict contains: * `synth_source_id` - Unique instrument ID (int) * `source_name` - Display name * `synth_source_type` - Instrument type (mbe, cvd, etc.) * `source_manufacturer` - Manufacturer name * `source_model` - Model name *** ## create\_growth\_instrument() Register a new growth instrument. ```python theme={null} instrument_id = client.create_growth_instrument( label="Main MBE", name="Veeco GEN10", instrument_type="mbe", serial_id="SN-12345", ) ``` ### Parameters Display name for the instrument (e.g., "Main MBE"). Manufacturer and model (e.g., "Veeco GEN10"). Type of growth instrument. | Value | Description | | - | - | | `mbe` | Molecular beam epitaxy | | `cvd` | Chemical vapor deposition | | `pvd` | Physical vapor deposition | | `sputter` | Sputtering | | `ald` | Atomic layer deposition | | `pld` | Pulsed laser deposition | Optional serial number or identifier. ### Returns The ID of the newly created instrument. *** ## delete\_growth\_instrument() Delete a growth instrument. ```python theme={null} client.delete_growth_instrument(synth_source_id=42) ``` ### Parameters ID of the instrument to delete. ### Returns None. # Streaming Source: https://docs.atomscale.ai/sdk/reference/streaming Complete API reference for RHEEDStreamer and TimeseriesStreamer The `RHEEDStreamer` class provides high-performance streaming of RHEED frames to the Atomscale platform. It bridges Python to a Rust/PyO3 backend for efficient, concurrent packaging and upload of grayscale frames. [View source on GitHub](https://github.com/atomscale-ai/sdk/blob/main/src/atomscale/streaming/rheed_stream.pyi) ## Constructor ```python theme={null} from atomscale.streaming import RHEEDStreamer streamer = RHEEDStreamer( api_key="YOUR_API_KEY", endpoint="https://api.atomscale.ai/", verbosity=2, ) ``` API key for authentication. Root API endpoint. If not provided, uses the default production endpoint or reads from `AS_API_ENDPOINT` environment variable. Logging verbosity level. Set to `4` for detailed progress output. *** ## initialize() Start a new streaming session and return a data ID for subsequent operations. ```python theme={null} data_id = streamer.initialize( fps=120.0, rotations_per_min=15.0, chunk_size=240, stream_name="Growth run 42", physical_sample="GaN-001", ) ``` ### Parameters Frame rate of the capture in frames per second. Sample rotation speed in rotations per minute. Set to `0.0` for stationary samples. Number of frames per upload chunk. Must be at least `2 * fps`, so each chunk covers 2 seconds or more of video. Human-readable name for this stream. Helps identify runs in the UI. Name of the physical sample to link this stream to. Names are matched case-insensitively. If no match is found, a new sample record is created. UUID of the project to associate with this stream. When provided along with `physical_sample`, the sample is added to the project's tracking list and marked as the project's active tracking sample for growth monitoring. Tag names or UUIDs to attach to the data item. Names are matched case-insensitively against existing organization tags, and unknown names are created. UUIDs must reference existing tags. ### Returns Unique identifier for this streaming session. Use this ID with `push()`, `run()`, and `finalize()`. ### Example ```python theme={null} data_id = streamer.initialize( fps=120.0, rotations_per_min=15.0, chunk_size=240, stream_name="MBE growth - GaN on sapphire", physical_sample="GaN-001", ) print(f"Started stream: {data_id}") ``` *** ## run() Stream frames using generator/pull mode. The streamer consumes chunks from an iterator and handles pacing and retry logic. ```python theme={null} streamer.run(data_id, frame_iterator) ``` ### Parameters The data ID returned by `initialize()`. Iterator yielding frame chunks. Each chunk should be a numpy array of shape `(N, H, W)` with dtype `uint8`, where N is the chunk size. A single `(H, W)` frame is treated as `N = 1`. A yielded item may instead be a `(frames, capture_start_ms_utc)` tuple, pairing the array with an explicit capture-start timestamp in milliseconds since the UNIX epoch (UTC). Use this when you have hardware or OS-level timestamps for each chunk. ### Returns None. Frames are uploaded as they are yielded by the iterator. The call blocks until every spawned upload task completes. ### Example ```python theme={null} import numpy as np import time def frame_chunks(frames, *, chunk_size=240, fps=120.0): """Yield frame chunks at the capture cadence.""" seconds_per_chunk = chunk_size / fps for start in range(0, len(frames), chunk_size): yield frames[start : start + chunk_size] time.sleep(seconds_per_chunk) # Buffered frames (e.g., loaded from disk) frames = np.random.randint(0, 256, size=(1200, 300, 500), dtype=np.uint8) data_id = streamer.initialize(fps=10.0, rotations_per_min=0.0, chunk_size=20) streamer.run(data_id, frame_chunks(frames, chunk_size=20, fps=10.0)) streamer.finalize(data_id) ``` *** ## push() Upload a single chunk of frames in callback/push mode. Use this when frames arrive live from the instrument. ```python theme={null} streamer.push(data_id, chunk_idx, frames) ``` ### Parameters The data ID returned by `initialize()`. Zero-based index of this chunk. Chunks must be pushed in order. Numpy array of frames with shape `(N, H, W)` or `(H, W)` for single frames. Must have dtype `uint8`. Capture-start timestamp for the first frame in the chunk, in milliseconds since the UNIX epoch (UTC). When omitted, the streamer samples the current time on entry to `push()`. ### Returns None. The chunk is queued for upload. ### Timestamps Without an explicit `capture_start_ms_utc`, each chunk is stamped with the wall clock sampled the moment `push()` is entered, before any packaging work. The chunk's end timestamp is then `start + (n / fps) * 1000`. Inter-chunk gaps therefore reflect real arrival jitter, while the span within a chunk follows the declared FPS. Pass an explicit timestamp when you have a hardware clock, such as a camera trigger or an OS monotonic-to-UTC conversion. ### Example ```python theme={null} import numpy as np import time fps = 120.0 chunk_size = 240 seconds_per_chunk = chunk_size / fps data_id = streamer.initialize( fps=fps, rotations_per_min=15.0, chunk_size=chunk_size, ) for chunk_idx in range(10): # Capture frames from instrument frames = np.random.randint(0, 256, size=(chunk_size, 300, 500), dtype=np.uint8) streamer.push(data_id, chunk_idx, frames) time.sleep(seconds_per_chunk) time.sleep(1.0) # Let in-flight uploads finish streamer.finalize(data_id) ``` *** ## finalize() Complete the streaming session and signal the server to begin final processing. ```python theme={null} streamer.finalize(data_id) ``` ### Parameters The data ID returned by `initialize()`. ### Returns None. The stream is marked as complete and queued for final analysis. Always call `finalize()` even if the upload fails part-way. This signals the server to clean up resources and mark the stream appropriately. ### Example ```python theme={null} try: data_id = streamer.initialize(fps=120.0, rotations_per_min=15.0, chunk_size=240) # ... push or run frames ... finally: streamer.finalize(data_id) ``` *** ## Frame Format Requirements RHEED frames must meet these requirements: | Property | Requirement | | - | - | | Data type | `numpy.uint8` (8-bit unsigned integer) | | Shape | `(N, H, W)` for chunks or `(H, W)` for single frames | | Color | Grayscale only | | Values | 0-255 intensity range | ### Example frame preparation ```python theme={null} import numpy as np # From a color image color_frame = capture_from_camera() # Shape: (H, W, 3) gray_frame = np.mean(color_frame, axis=2).astype(np.uint8) # From 16-bit data raw_frame = capture_raw() # Shape: (H, W), dtype: uint16 normalized = ((raw_frame / raw_frame.max()) * 255).astype(np.uint8) # Batch into chunks chunk_size = 240 frames = np.stack([capture_frame() for _ in range(chunk_size)]) # (240, H, W) ``` *** ## Best Practices | Recommendation | Rationale | | - | - | | Match the capture cadence | The server expects frames at the declared FPS | | Use chunk sizes ≥ 2 seconds | Reduces network overhead and improves throughput | | Always call `finalize()` | Allows graceful cleanup even on failure | | Use distinct stream names | Makes runs easier to find later | | Link to physical samples | Enables cross-run analysis and tracking | *** ## Error Handling The streamer raises exceptions for connection failures and invalid parameters. Wrap streaming code in try/finally to ensure `finalize()` is called: ```python theme={null} data_id = None try: data_id = streamer.initialize( fps=120.0, rotations_per_min=15.0, chunk_size=240, ) for chunk_idx, frames in enumerate(capture_frames()): streamer.push(data_id, chunk_idx, frames) except Exception as e: print(f"Streaming failed: {e}") finally: if data_id: streamer.finalize(data_id) ``` *** # TimeseriesStreamer The `TimeseriesStreamer` class streams scalar instrument data (temperature, pressure, growth rate, etc.) to the Atomscale platform. Like `RHEEDStreamer`, it uses a Rust/PyO3 backend. [View source on GitHub](https://github.com/atomscale-ai/sdk/blob/main/src/atomscale/streaming/rheed_stream.pyi) ## Constructor ```python theme={null} from atomscale.streaming import TimeseriesStreamer streamer = TimeseriesStreamer( api_key="YOUR_API_KEY", points_per_chunk=100, verbosity=2, ) ``` API key for authentication. Root API endpoint. If not provided, uses the default production endpoint or reads from `AS_API_ENDPOINT` environment variable. Expected number of data points per chunk. Used for array positioning. Logging verbosity level. Set to `4` for detailed progress output. *** ## initialize() Start a new timeseries streaming session. ```python theme={null} data_id = streamer.initialize( stream_name="MBE growth - temperature log", synth_source_id=42, physical_sample="GaN-001", project_id="project-uuid", ) ``` ### Parameters Human-readable name for this stream. Growth instrument ID to link. Must belong to your organization. Name or UUID of a physical sample to associate with the stream. Names are matched case-insensitively, or a new sample is created if no match is found. UUID of the project to associate with this stream. When provided along with `physical_sample`, the sample is added to the project's tracking list and marked as the project's active tracking sample for growth monitoring. Tag names or UUIDs to attach to the data item. Names are matched case-insensitively against existing organization tags, and unknown names are created. UUIDs must reference existing tags. ### Returns Unique identifier for this streaming session. *** ## push() Upload a single chunk of data for one channel. Spawns an async upload task and returns immediately. ```python theme={null} streamer.push( data_id, chunk_index=0, channel_name="temperature", timestamps=[1700000000.0, 1700000000.1], values=[25.0, 25.1], units="C", ) ``` ### Parameters The data ID returned by `initialize()`. Zero-based index of this chunk. Chunks must be pushed in order. Name of the data channel (e.g., "temperature", "pressure"). Unix epoch timestamps in seconds. Measured values corresponding to each timestamp. Optional units for the values (e.g., "C", "mbar"). ### Returns None. The chunk is queued for upload. *** ## push\_multi() Upload data for multiple channels in a single call. ```python theme={null} streamer.push_multi(data_id, chunk_index=0, channels={ "temperature": { "timestamps": [1700000000.0, 1700000000.1], "values": [25.0, 25.1], "units": "C", }, "pressure": { "timestamps": [1700000000.0, 1700000000.1], "values": [1.0, 1.1], "units": "mbar", }, }) ``` ### Parameters The data ID returned by `initialize()`. Zero-based index of this chunk. Mapping of channel names to channel data. Each value dict should contain `timestamps` (list of floats), `values` (list of floats), and optionally `units` (string). ### Returns None. *** ## run() Stream data from an iterator. Blocks until all uploads complete. ```python theme={null} def data_chunks(): for i in range(10): timestamps = [time.time() + j * 0.1 for j in range(100)] values = [25.0 + j * 0.01 for j in range(100)] yield (timestamps, values) streamer.run( data_id, channel_name="temperature", data_iter=data_chunks(), units="C", ) ``` ### Parameters The data ID returned by `initialize()`. Name of the data channel. Iterator yielding `(timestamps, values)` tuples for each chunk. Optional units for the values. ### Returns None. *** ## finalize() Complete the streaming session and signal the server to begin processing. ```python theme={null} streamer.finalize(data_id) ``` ### Parameters The data ID returned by `initialize()`. ### Returns None. Always call `finalize()` even if the upload fails part-way. This signals the server to clean up resources and mark the stream appropriately. # RHEED Features and Masks Source: https://docs.atomscale.ai/sdk/rheed-features Query low-level RHEED features and per-frame segmentation masks `client.get()` returns the standard RHEED feature set. When you need the full per-region feature space, a filtered subset of properties, a time window, or the segmentation masks behind the features, use `get_rheed_timeseries()` and `get_frame_masks()`. | Method | Use case | | - | - | | `get_rheed_timeseries()` | Feature timeseries with low-level features, masks, and windowing | | `get_frame_masks()` | Per-frame segmentation masks on their own, optionally decoded | | `get_frame()` | A single extracted frame as a `RHEEDImageResult` | | `decode_mask_rle()` | Turn an RLE string into an `(H, W)` binary array | ## Fetch the feature timeseries `get_rheed_timeseries()` returns a DataFrame indexed by `["View ID", "Frame Number"]`. Each view of the video has its own `View ID`: a rotating video has one per sampled azimuth, and a stationary video has one for each azimuth it was held at. `Angle`, `Interval ID`, and `Azimuth Label` are regular columns, not index levels. ```python theme={null} from atomscale import Client client = Client() data_id = "44fa63b0-74da-4d25-a362-2276c80a670a" df = client.get_rheed_timeseries(data_id) print(df.index.names) # ['View ID', 'Frame Number'] print(df.columns) ``` Standard columns use display names such as `Specular Intensity`, `Strain`, `Cumulative Strain`, `Oscillation Period`, `Diffraction Spot Count`, and `Lattice Spacing`. ## Include low-level features Set `include_low_level_features=True` to add the full set of per-point, per-region features (spot areas, eccentricities, FWHM values, and similar) as extra columns. ```python theme={null} df = client.get_rheed_timeseries(data_id, include_low_level_features=True) print(df.filter(like="area").columns) ``` Low-level columns keep their raw backend names, so they are not renamed to display labels the way standard metrics are. Nested values are flattened into dotted column paths. Where a low-level feature shares a name with an existing standard column, the standard column wins and the low-level value is dropped, so known metrics are never overwritten. Low-level features are per-point: a frame that was not featurized contributes NA to those columns. ## Restrict to specific properties Pass `property_names` to fetch only the features you need. These are the underlying property names, which differ from the display column names in the returned DataFrame. ```python theme={null} df = client.get_rheed_timeseries( data_id, property_names=["specular_intensity", "referenced_strain"], ) ``` | Property name | Display column | | - | - | | `specular_intensity` | Specular Intensity | | `referenced_strain` | Strain | | `nearest_neighbor_strain` | Cumulative Strain | | `spot_count` | Diffraction Spot Count | | `oscillation_period` | Oscillation Period | | `lattice_spacing` | Lattice Spacing | | `cluster_id` | Cluster ID | | `first_order_intensity` | First Order Intensity | | `half_order_intensity` | Half Order Intensity | | `specular_fwhm_1` | Specular FWHM | ## Limit the time window Two parameters narrow which points come back, which matters for long growths and for live streams where you only want the tail: ```python theme={null} # Last 500 points recent = client.get_rheed_timeseries(data_id, last_n=500) # Last 60 seconds of the recording tail = client.get_rheed_timeseries(data_id, elapsed_seconds=60.0) ``` ## Attach segmentation masks Every featurized frame of a processed RHEED video carries a binary segmentation mask of the diffraction pattern. Pass `include_masks=True` to join those masks onto the timeseries on the `Frame Number` axis: ```python theme={null} from atomscale.results import decode_mask_rle df = client.get_rheed_timeseries( data_id, include_low_level_features=True, include_masks=True, ) row = df.dropna(subset=["mask_rle"]).iloc[0] mask = decode_mask_rle(row["mask_rle"], row["mask_height"], row["mask_width"]) print(mask.shape) # (H, W), uint8 with values 0 or 1 ``` Three columns are added: | Column | Description | | - | - | | `mask_rle` | COCO run-length-encoded mask string | | `mask_height` | Mask height in pixels | | `mask_width` | Mask width in pixels | Mask coverage is sparse. Masks exist only for featurized frames, so rows whose frame has no mask contain NA in these columns, as do all rows when the video has no mask artifact at all. Drop the NA rows before decoding. The mask fetch is scoped to the frame range the returned series actually spans, so combining `include_masks=True` with `last_n` or `elapsed_seconds` pulls only the masks for that window rather than the whole video. ## Fetch masks on their own When you want masks without the feature columns, call `get_frame_masks()` directly. Frame numbers are absolute and keyed identically to the processed video frames and to the `Frame Number` axis of the timeseries, so a decoded mask overlays the matching frame of the video you get from `download()`. ```python theme={null} # Raw rows with the RLE string intact rows = client.get_frame_masks(data_id) print(rows[0].keys()) # dict_keys(['data_id', 'processed_data_id', 'frame_number', # 'mask_rle', 'mask_height', 'mask_width']) # Decoded arrays keyed by absolute frame number masks = client.get_frame_masks(data_id, decode=True) print(masks[0].shape) # (H, W) uint8 ``` Bound the request with `from_frame` and `to_frame`, both inclusive: ```python theme={null} masks = client.get_frame_masks( data_id, from_frame=1000, to_frame=1200, decode=True, ) ``` For stationary videos every frame is featurized, so frame numbers are contiguous. For rotating and per-azimuth videos only a sampled subset is featurized, and frames without a mask are simply absent from the result. An empty list (or empty dict with `decode=True`) means the video has no per-frame mask artifact, either because the item is not RHEED or because it was processed before per-frame masks were persisted. ## Overlay a mask on a frame Combine `get_frame()` with a decoded mask to inspect a specific frame: ```python theme={null} import numpy as np frame = client.get_frame(data_id, frame_index=0) masks = client.get_frame_masks(data_id, from_frame=0, to_frame=0, decode=True) image = np.array(frame.processed_image) mask = masks[0] highlighted = np.where(mask[..., None] == 1, image, image // 3) ``` `get_frame()` accepts negative indices, so `frame_index=-1` returns the last extracted frame. It returns `None` when the video has no extracted frames, the index is out of range, or the selected frame has no image. For a single RHEED image item (rather than a video), the mask is already on the result object as `result.mask`, and `get_plot(show_mask=True)` renders it as an overlay. See [Inspect Results](/sdk/inspect-results). ## Decode masks yourself `decode_mask_rle()` handles the COCO RLE format used by every Atomscale mask endpoint. Use it when working with raw rows or with API responses fetched outside the SDK: ```python theme={null} from atomscale.results import decode_mask_rle rows = client.get_frame_masks(data_id, from_frame=0, to_frame=99) decoded = { row["frame_number"]: decode_mask_rle( row["mask_rle"], row["mask_height"], row["mask_width"] ) for row in rows } ``` The counts string is column-major (Fortran order), matching `pycocotools`. ## Next steps Compare growths with embedding vectors. Full parameter reference for every method. # Samples and Projects Source: https://docs.atomscale.ai/sdk/samples-projects Work with physical samples, projects, and aligned timeseries Physical samples and projects let you group related data items for cross-run analysis. Use the SDK to list, fetch, and align data across multiple uploads. ## List physical samples ```python theme={null} from atomscale import Client client = Client() samples = client.list_physical_samples() print(samples[["Physical Sample ID", "Physical Sample Name"]]) ``` The returned DataFrame includes columns for sample metadata, linked projects, and ownership. ## List projects ```python theme={null} projects = client.list_projects() print(projects[["Project ID", "Project Name", "Physical Sample Count"]]) ``` ## Fetch a physical sample Use `get_physical_sample()` to retrieve all analysis results linked to a sample: ```python theme={null} result = client.get_physical_sample(physical_sample_id="sample-uuid") # Iterate over all result objects for item in result.data_results: print(type(item).__name__, item.data_id) ``` The returned `PhysicalSampleResult` contains: * `physical_sample_id` and `physical_sample_name` for identification * `data_results` with all result objects (RHEED, XPS, PL, tool state, etc.) ## Fetch a project Use `get_project()` to retrieve all data grouped by physical sample: ```python theme={null} result = client.get_project(project_id="project-uuid") for sample in result.samples: print(f"Sample: {sample.physical_sample_name}") for item in sample.data_results: print(f" {type(item).__name__}: {item.data_id}") ``` ## Align timeseries across sources Pass `align=True` to time-align data from multiple sources onto a common time index: ```python theme={null} result = client.get_physical_sample( physical_sample_id="sample-uuid", align=True, ) # Access the aligned DataFrame aligned = result.aligned_timeseries print(aligned.columns) print(aligned.tail()) ``` This merges RHEED, tool-state, optical, and other timeseries data so you can correlate signals across instruments. By default, alignment uses an outer join (union of all timestamps). Pass a join strategy string for different behavior: ```python theme={null} # Inner join: only timestamps present in all sources result = client.get_physical_sample( physical_sample_id="sample-uuid", align="inner", ) ``` Project-level alignment works the same way: ```python theme={null} result = client.get_project(project_id="project-uuid", align=True) print(result.aligned_timeseries.tail()) ``` ## Next steps Work with individual analysis outputs. Full Client class documentation. # Search the Catalogue Source: https://docs.atomscale.ai/sdk/search-data Find uploaded data with filters and keywords Use `client.search()` to locate items in the data catalogue. The method returns a pandas DataFrame with metadata for each match. ## Basic search ```python theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") results = client.search(keywords=["WSe2"]) print(results[["Data ID", "Status", "Sample Name"]]) ``` ## Limit to your uploads By default, search includes data from other users in your organization. To see only your own uploads: ```python theme={null} personal_only = client.search( keywords="demo", include_organization_data=False, ) ``` ## Filter by IDs or type Fetch specific items by their data IDs: ```python theme={null} exact = client.search(data_ids=["44fa63b0-74da-4d25-a362-2276c80a670a"]) ``` Filter by data type: ```python theme={null} videos = client.search(data_type="rheed") ``` RHEED videos share the single `rheed` type whether or not the stage was rotating. To tell them apart, fetch the results with `client.get()` and check the `rotating` property of each `RHEEDVideoResult`, which is read from the stored rotation rate. ### Available data types | Value | Description | | - | - | | `rheed_image` | Single RHEED image | | `rheed` | RHEED video, stationary or rotating | | `rheed_stationary`, `rheed_rotating` | Legacy aliases for `rheed`. Both match all RHEED videos, rotating or not | | `xps` | XPS spectrum | | `xrd` | XRD pattern | | `photoluminescence` | Photoluminescence spectrum | | `pl` | Alias for photoluminescence | | `raman` | Raman spectrum | | `ellipsometry` | Ellipsometry measurement | | `optical` | Optical imaging | | `tool_state` | Tool-state log (instrument sensor readbacks) | | `metrology` | Deprecated alias for `tool_state` | | `recipe` | Growth recipe | | `all` | All types (default) | ## Filter by lifecycle state The `status` parameter filters by pipeline state: ```python theme={null} completed = client.search(status="success") ``` ### Available status values | Value | Description | | - | - | | `success` | Analysis completed successfully | | `pending` | Queued for processing | | `running` | Currently processing | | `error` | Analysis failed | | `stream_active` | Streaming in progress | | `stream_interrupted` | Stream was interrupted | | `stream_finalizing` | Stream finishing up | | `stream_error` | Streaming failed | | `all` | All statuses (default) | ## Apply numeric or datetime bounds Pass `(min, max)` tuples for growth length (seconds), upload timestamp, or last-updated timestamp. Use `None` for an open bound. ```python theme={null} from datetime import datetime recent = client.search( upload_datetime=(datetime(2025, 1, 1), None), last_updated=(datetime(2025, 6, 1), None), growth_length=(3000, None), ) ``` `last_accessed_datetime` is a deprecated alias for `last_updated`. It still works but emits a warning, and it will be removed in a future release. ## Combine filters All filter parameters can be combined: ```python theme={null} results = client.search( keywords="GaN", data_type="rheed", status="success", upload_datetime=(datetime(2025, 1, 1), None), ) ``` ## Result columns Each row describes one catalogue entry: | Column | Description | | - | - | | `Data ID` | Unique identifier, the input to `client.get()` | | `File Name`, `File Type`, `File Metadata` | Source file and format details | | `Type`, `Status` | Data type and pipeline state | | `Instrument Source`, `Has Instrument Logs` | Originating instrument and log availability | | `Sample Name`, `Physical Sample ID/Name` | Linked sample | | `Sample Notes`, `Sample Notes Last Updated` | Sample annotations | | `Project ID`, `Project Name` | Linked project | | `Tags` | Tags attached to the item | | `Growth Length` | Duration in seconds, for videos | | `Collected Datetime`, `Upload Datetime`, `Last Updated` | Lifecycle timestamps | | `Owner`, `Workspaces` | Ownership and visibility | ## Next steps Pass the `Data ID` column to `client.get()` to fetch analysis artefacts. See [Inspect Results](/sdk/inspect-results) for a hands-on tour. # Similarity and Embeddings Source: https://docs.atomscale.ai/sdk/similarity Compare growths with embedding vectors, matches, and trajectories The similarity pipeline embeds RHEED data as vectors, then uses those vectors to answer two different questions: which other growths look like this one, and how this growth is evolving relative to a reference. | Method | Answers | | - | - | | `get_embeddings()` | What are the raw vectors for this item? | | `query_rheed_embeddings()` | Which other items are most similar to this one? | | `get_similarity_matches()` | What are the stored top matches for this item? | | `get_similarity_trajectory()` | How has similarity to a reference changed over time? | All four accept a `workflow` name that defaults to `rheed`. See [Similarity](/platform/reference/workflows/similarity) for how the workflow is computed. ## Find similar growths `query_rheed_embeddings()` runs a k-nearest-neighbour query over the embedding index using an item's own vectors: ```python theme={null} from atomscale import Client client = Client() data_id = "44fa63b0-74da-4d25-a362-2276c80a670a" neighbours = client.query_rheed_embeddings(data_id, top_k=10) print(neighbours[["data_id", "similarity"]]) ``` The result is sorted by descending similarity, where `1.0` means identical. Alongside `data_id` and `similarity`, each row carries locus columns that pin down where in each recording the match occurred: `source_index`, `neighbor_index`, `real_time_seconds`, and `unix_time_ms`. ### Coarse and fine queries The `kind` parameter trades precision for query count: ```python theme={null} # Coarse: a few representative vectors per item (default) coarse = client.query_rheed_embeddings(data_id, kind="prototype") # Fine: one vector per window, more queries fine = client.query_rheed_embeddings(data_id, kind="window", window_span=60.0) ``` `window_span` must match a span the data was actually embedded at. The backend caps `top_k` at 30. An empty DataFrame means this item has no embeddings for the given workflow and window span. ## Fetch embedding vectors Use `get_embeddings()` when you want the vectors themselves, for clustering, dimensionality reduction, or a custom distance metric: ```python theme={null} emb = client.get_embeddings(data_id, window_span=60.0, kind="window") print(emb.vectors.shape) # (n_returned, dimension) print(emb.count) # total available before offset/limit print(emb.truncated) # True when more remain ``` The two kinds carry different metadata: | Kind | Vectors | Metadata | | - | - | - | | `window` | One time-resolved vector per window | `real_times`, `unix_times_ms` | | `prototype` | A small representative set | `cluster_sizes` | Metadata arrays that do not apply to the returned kind are `None`. Page through large results with `offset` and `limit`: ```python theme={null} first_page = client.get_embeddings(data_id, kind="window", limit=500) second_page = client.get_embeddings(data_id, kind="window", offset=500, limit=500) ``` When no embeddings exist for the requested workflow and window span, the SDK emits a `UserWarning` and returns an empty result instead of raising, so loops over many data IDs keep running. Check `len(result.vectors)` before using the array. ## Retrieve stored matches `get_similarity_matches()` returns the top matches the platform has already computed, which is the same ranking shown in the web app: ```python theme={null} matches = client.get_similarity_matches(data_id, window_span=60.0) print(matches) # columns: data_id, item_name, similarity ``` `source_id` accepts either a data ID or a physical sample ID. Set `live_comparison=True` to include the source entry's still-streaming data in the comparison, and `limit` to cap the number of rows. ## Fetch a similarity trajectory `get_similarity_trajectory()` returns similarity against reference growths over time in a single call, without polling: ```python theme={null} result = client.get_similarity_trajectory( source_id=data_id, workflow="rheed", last_n=200, ) print(result.window_span) print(result.timeseries_data.tail()) ``` The `timeseries_data` DataFrame is indexed by `("Reference ID", "Time")` with columns `Similarity`, `Reference Name`, `UNIX Timestamp`, `Active`, and `Averaged Count`. Restrict the comparison to specific references with `reference_ids`: ```python theme={null} result = client.get_similarity_trajectory( source_id=data_id, reference_ids=["reference-uuid-1", "reference-uuid-2"], ) ``` ## Poll a trajectory during a growth For a run in progress, poll instead of fetching once. The `Client` exposes wrappers for the four polling styles: ```python theme={null} for frame in client.iter_poll_similarity_trajectory(source_id=data_id, interval=5.0): if not frame["Active"].any(): print("Trajectory complete") break print(frame["Similarity"].iloc[-1]) ``` | Client method | Style | | - | - | | `iter_poll_similarity_trajectory()` | Synchronous loop | | `aiter_poll_similarity_trajectory()` | Async iterator | | `start_polling_similarity_trajectory_thread()` | Background daemon thread | | `start_polling_similarity_trajectory_task()` | Asyncio task | Each forwards extra keyword arguments (`distinct_by`, `until`, `max_polls`, `fire_immediately`, `jitter`, `on_error`) to the underlying function in `atomscale.similarity`. See [Poll Similarity Trajectory](/sdk/poll-trajectory) for the full polling walkthrough. ## Next steps Query low-level features and segmentation masks. Monitor trajectories during a live growth. # Stream Tool State Source: https://docs.atomscale.ai/sdk/stream-metrology Push instrument readings for real-time analysis The `TimeseriesStreamer` class lets you push scalar instrument data (temperature, pressure, growth rate, etc.) to Atomscale as tool-state data for live tracking and analysis. Like `RHEEDStreamer`, it uses a Rust/PyO3 backend for efficient uploads. ## Prerequisites * Instrument readings as lists of timestamps and values * Timestamps as Unix epoch seconds (float) ## Create a streamer ```python theme={null} from atomscale.streaming import TimeseriesStreamer streamer = TimeseriesStreamer( api_key="YOUR_API_KEY", points_per_chunk=100, ) ``` Set `verbosity=4` for detailed progress output. ## Initialize a stream ```python theme={null} data_id = streamer.initialize( stream_name="MBE growth - temperature log", physical_sample="GaN-001", synth_source_id=42, # from client.list_growth_instruments() project_id="project-uuid", tags=["calibration"], ) ``` The `synth_source_id` links the stream to a registered growth instrument. Use `client.list_growth_instruments()` to find available instruments. `tags` accepts tag names or UUIDs. Names are matched case-insensitively against your organization's tags, and unknown names create new tags. ## Push single-channel data Use `push()` to send one channel at a time. Chunks are uploaded asynchronously. ```python theme={null} import time timestamps = [time.time() + i * 0.1 for i in range(100)] values = [25.0 + i * 0.01 for i in range(100)] streamer.push( data_id, chunk_index=0, channel_name="temperature", timestamps=timestamps, values=values, units="C", ) ``` ## Push multi-channel data Use `push_multi()` to send multiple channels in a single call: ```python theme={null} streamer.push_multi(data_id, chunk_index=0, channels={ "temperature": { "timestamps": timestamps, "values": temp_values, "units": "C", }, "pressure": { "timestamps": timestamps, "values": pressure_values, "units": "mbar", }, }) ``` ## Generator mode Use `run()` when data is already buffered. It iterates over chunks and handles uploads: ```python theme={null} def data_chunks(timestamps, values, chunk_size=100): for start in range(0, len(timestamps), chunk_size): yield ( timestamps[start : start + chunk_size], values[start : start + chunk_size], ) data_id = streamer.initialize(stream_name="Pyrometer log") streamer.run( data_id, channel_name="temperature", data_iter=data_chunks(all_timestamps, all_values), units="C", ) streamer.finalize(data_id) ``` ## Finalize Always call `finalize()` when the stream is complete: ```python theme={null} streamer.finalize(data_id) ``` Always call `finalize()` even if the upload fails part-way. This signals the server to clean up resources and mark the stream appropriately. ## Best practices | Recommendation | Rationale | | - | - | | Always call `finalize()` | Allows graceful cleanup even on failure | | Use `push_multi()` for correlated channels | Keeps channels in sync within each chunk | | Link to growth instruments | Enables cross-run analysis and instrument tracking | | Use Unix epoch timestamps | The platform expects seconds since epoch | ## Next steps Read tool-state data back with `client.get()`. Full streaming API documentation. # Stream RHEED Video Source: https://docs.atomscale.ai/sdk/stream-rheed Push frames from your instrument for real-time analysis The `RHEEDStreamer` class lets you push RHEED frames directly from your instrument to Atomscale for live analysis. Choose between two streaming modes: * **Callback / push mode** - The camera or SDK hands you fresh frames and you upload each chunk immediately * **Generator / pull mode** - You already have frames buffered and want the helper to pace the upload for you ## Prerequisites * `numpy` installed * RHEED frames as `uint8` arrays shaped `(N, H, W)` or `(H, W)` * A stable clock so you can honor the capture cadence ## Create a streamer ```python theme={null} from atomscale.streaming import RHEEDStreamer streamer = RHEEDStreamer(api_key="YOUR_API_KEY") ``` Optional keyword arguments tune chunking and logging. Set `verbosity=4` for detailed progress output. ## Callback / push mode Use this variant when frames arrive live from the instrument. The outer loop is your acquisition callback: once a chunk is ready, send it to the API and wait just long enough to match the capture cadence. ```python theme={null} import numpy as np import time fps = 120.0 chunk_size = 240 # 2 seconds of frames at 120 fps seconds_per_chunk = chunk_size / fps data_id = streamer.initialize( fps=fps, rotations_per_min=15.0, # set to 0.0 for stationary chunk_size=chunk_size, stream_name="Demo (callback mode)", physical_sample="Demo wafer", ) for chunk_idx in range(5): # Replace with actual frame capture frames = np.random.randint(0, 256, size=(chunk_size, 300, 500), dtype=np.uint8) streamer.push(data_id, chunk_idx, frames) time.sleep(seconds_per_chunk) time.sleep(1.0) # let in-flight uploads finish streamer.finalize(data_id) ``` ## Generator / pull mode Use this form when frames are already buffered (from disk, memory, or a simulated source). Provide an iterator that yields chunks and the helper handles pacing and retry logic. ```python theme={null} import numpy as np import time def frame_chunks(frames, *, chunk_size=240, fps=120.0): seconds_per_chunk = chunk_size / fps for start in range(0, len(frames), chunk_size): yield frames[start : start + chunk_size] time.sleep(seconds_per_chunk) # Simulated buffered frames frames = np.random.randint(0, 256, size=(1200, 300, 500), dtype=np.uint8) data_id = streamer.initialize( fps=10.0, rotations_per_min=0.0, chunk_size=20, stream_name="Demo (generator mode)", physical_sample="Demo wafer", ) streamer.run(data_id, frame_chunks(frames, chunk_size=20, fps=10.0)) streamer.finalize(data_id) ``` ## Best practices | Recommendation | Why | | - | - | | Maintain original capture cadence | The server expects frames at the declared FPS | | Use chunk sizes of at least 2 seconds | Reduces overhead and improves throughput | | Always call `finalize()` | Lets the pipeline clean up gracefully, even on failure | | Use distinct stream names | Makes it easier to find runs later | Always call `finalize()` even if the upload fails part-way. This signals the server to clean up resources and mark the stream appropriately. ## Link to samples, projects, and tags Pass `physical_sample` to `initialize()` to link the stream to an existing sample. Names are matched case-insensitively, or a new sample is created if no match is found. You can also pass `project_id` to associate the stream with a project, which adds the sample to that project's tracking list and marks it as the active tracking sample for growth monitoring. `tags` accepts tag names or UUIDs. Names are matched case-insensitively against your organization's tags, and unknown names create new tags. ```python theme={null} data_id = streamer.initialize( fps=120.0, rotations_per_min=15.0, chunk_size=240, stream_name="Growth run 42", physical_sample="GaN-001", project_id="your-project-uuid", tags=["calibration", "buffer-layer"], ) ``` ## Next steps Monitor streaming analysis in real time. Full RHEEDStreamer API documentation. # Upload Data Source: https://docs.atomscale.ai/sdk/upload-data Send instrument files for automated analysis Use the `upload()` method to send files to Atomscale. Analysis starts as soon as data arrives, and results appear in the catalogue once the pipeline finishes. ## Choose files Collect the local file paths you want to upload. Mixing file types is fine. ```python theme={null} files = [ "/data/growths/2025-02-10/RHEED-stationary.mp4", "/data/growths/2025-02-10/RHEED-rotating.imm", ] ``` ## Supported formats | Data Type | Extensions | | - | - | | RHEED video | `.mp4`, `.avi`, `.mov`, `.webm`, `.h5`, `.imm` | | RHEED image | `.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.img`, `.pdf` | | XPS | `.vms`, `.spe`, `.spc`, `.dat`, `.txt` | | SEM | `.tif`, `.tiff`, `.png`, `.jpg`, `.jpeg`, `.bmp` | | Ellipsometry | `.txt`, `.csv`, `.dat` | | Optical microscopy | `.png`, `.jpg`, `.jpeg`, `.tiff`, `.tif`, `.gif`, `.bmp`, `.img`, `.pdf` | | Raman | `.txt`, `.csv` | | Photoluminescence | `.txt`, `.csv` | | SIMS | `.xlsx`, `.xls` | | OES | `.txt`, `.csv` | | Tool state and logs | `.csv`, `.txt`, `.log`, `.dat`, `.xml` | | Archives | `.zip`, `.tar`, `.gz`, `.lzma`, `.7z`, `.rar` | ## Start the upload ```python theme={null} from atomscale import Client client = Client(api_key="YOUR_API_KEY") data_ids = client.upload(files=files) print(data_ids) ``` Each file streams to the API in chunks. The method displays a progress bar for each file and returns the data IDs assigned to the uploads, which you can pass straight to `client.get()` once analysis finishes. ## Link uploads to a sample Pass `physical_sample` to group the files under a sample. Names are matched case-insensitively, and a new sample is created when nothing matches: ```python theme={null} data_ids = client.upload( files=files, physical_sample="GaN-2025-001", ) ``` ## Link uploads to a project Pass `project` to associate the uploads with an existing project. The project must already exist, since the SDK does not create projects, and `physical_sample` is required so the sample can be added to the project's tracking list: ```python theme={null} data_ids = client.upload( files=files, physical_sample="GaN-2025-001", project="Nitride Buffer Study", ) ``` Both parameters accept a name or a UUID. ## Upload from file objects You can also pass open file handles instead of paths: ```python theme={null} with open("/data/rheed.mp4", "rb") as f: client.upload(files=[f]) ``` ## Mute progress bars For non-interactive environments, pass `mute_bars=True` when constructing the client: ```python theme={null} client = Client(api_key="YOUR_API_KEY", mute_bars=True) client.upload(files=files) ``` ## Check status in the web app Uploads immediately appear in the Atomscale UI. Analysis runs in the background, and results land in the catalogue once the pipeline finishes. ## Next steps Find your uploaded items in the catalogue. Work with analysis outputs and plots. Group data across runs and align timeseries. Query low-level features and segmentation masks.