Analysis Results#

Access the data extracted from your RHEED videos and images.

Fetch Results#

from atomscale import Client

client = Client()

# Search for data
search_results = client.search(keywords=["GaN"], status="success")

# Fetch analysis results
analysed = client.get(search_results["Data ID"].to_list())

Each item in analysed is a result object with properties for accessing different types of analysis data.

Timeseries Data#

For RHEED videos, get frame-by-frame analysis:

video = analysed[0]
df = video.timeseries_data

print(df.columns)
print(df.tail())

Common columns:

Column

Description

timestamp

Frame timestamp in seconds

specular_intensity

Specular spot brightness

strain

Computed strain metric

cluster_id

Pattern cluster assignment

Low-Level Features#

RHEED videos expose a larger set of low-level, per-region features (e.g. area_0, eccentricity_0, fwhm_0_3) beyond the standard columns above. Request them with get_rheed_timeseries(), which returns a DataFrame indexed by ["Angle", "Frame Number"]:

df = client.get_rheed_timeseries(data_id, include_low_level_features=True)
print(df.filter(like="area").columns)

The low-level columns keep their raw backend names (they are not renamed).

Segmentation Masks#

Each featurized frame of a processed RHEED video carries a binary segmentation mask of the diffraction pattern. Attach the masks to the timeseries — aligned on the Frame Number axis, alongside any low-level features — with include_masks:

from atomscale.results import decode_mask_rle

df = client.get_rheed_timeseries(
    data_id,
    include_low_level_features=True,
    include_masks=True,
)

# Mask columns: mask_rle (COCO RLE string), mask_height, mask_width. Coverage
# is sparse -- frames without a mask are NA -- so drop those rows first.
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

Fetch masks on their own — optionally decoded and keyed by absolute frame number — with get_frame_masks():

masks = client.get_frame_masks(data_id, decode=True)  # {frame_number: (H, W) array}

Sample-Level Results#

Some results are computed per physical sample rather than per data item — rheed_quality and composition_metric are compiled from a sample’s constituent RHEED videos into one series each. Fetch them with get_physical_sample_timeseries():

ts = client.get_physical_sample_timeseries(
    physical_sample_id, property_names=["rheed_quality"]
)

# Long form: one row per (property, sample-point). Reduce to a scalar yourself.
q = ts.loc[ts.property_name == "rheed_quality", "value"].dropna()
print(q.mean())

The frame is long (not wide) because distinct properties can have different axes, so a wide join on real_time_seconds would mis-align them. value is NaN for gaps; provenance columns (result_id, last_updated, generating_dbos_workflow_id) travel alongside, and per-property constituent_data_ids are in ts.attrs["constituent_data_ids"]. property_names filters client-side; omit it for all properties.

The same metrics are attached to get_physical_sample() results as sample_metrics (pass include_sample_metrics=False to skip the extra request):

sample = client.get_physical_sample(physical_sample_id)
print(sample.sample_metrics)  # None if the sample has no computed metrics

Embedding Vectors#

The similarity pipeline persists Chronos embedding vectors for RHEED data — the inputs to similarity matching, as opposed to the derived similarity-vs-time trajectory. Fetch them with get_embeddings():

emb = client.get_embeddings(data_id, window_span=60.0, kind="window")
print(emb.vectors.shape)  # (n_windows, dimension)

To find the RHEED data items most similar to a given one, run a k-nearest-neighbour query over the embedding index with query_rheed_embeddings():

neighbours = client.query_rheed_embeddings(data_id, top_k=10)
print(neighbours[["data_id", "similarity"]])

Extracted Frames#

Access snapshots extracted during analysis:

snapshot = video.snapshot_image_data[0]

# Get matplotlib figure
fig = snapshot.get_plot()
fig.savefig("snapshot.png")

# Get diffraction pattern as DataFrame
pattern_df = snapshot.get_pattern_dataframe()

# Get pattern as NetworkX graph
graph = snapshot.pattern_graph

Result Types#

The type of result object depends on the source data:

Data Type

Result Class

rheed_stationary, rheed_rotating

RHEEDVideoResult

rheed_image

RHEEDImageResult

xps

XPSResult

Batch Processing#

Process multiple results efficiently:

for item in analysed:
    if hasattr(item, "timeseries_data"):
        df = item.timeseries_data
        avg_intensity = df["specular_intensity"].mean()
        print(f"{item.data_id}: avg intensity = {avg_intensity:.2f}")