# 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 highlightedComparison to target values and reference baselinesAny 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.