Skip to main content
Similarity trajectory polling tracks how the RHEED pattern evolves over time relative to reference frames. It follows the same patterns as time series polling but uses dedicated functions from atomscale.similarity.
FunctionUse case
iter_poll_trajectorySynchronous loop that blocks between polls
start_polling_trajectory_threadBackground thread for GUI or acquisition loops
aiter_poll_trajectoryAsync iterator for asyncio applications
start_polling_trajectory_taskFire-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

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.

Synchronous polling

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.
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:
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

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:
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:
ColumnDescription
Reference ID (index)ID of the reference trajectory
Time (index)Real time in seconds
SimilaritySimilarity score (float)
Reference NameDisplay name of the reference
UNIX TimestampUnix epoch timestamp
ActiveWhether the trajectory is currently active
Averaged CountNumber of data points averaged

Next steps