Skip to main content
This tutorial walks through the complete path for a Monte Carlo orbit propagation study: submitting a dispersed batch, running it through lynx-runner with the Basilisk engine, streaming telemetry, and retrieving per-job trajectory artifacts. The scenario used here is scenario_basic_orbit_lynx.py.

What Lynx handles for you

You bring the simulation. Lynx handles everything around it. Lynx invokes your scenario as a subprocess. Your scenario reads its parameters, runs, writes results, and exits — that’s the full contract.

How the runner launches your scenario

For each job, the Basilisk engine plugin in lynx-runner does the following:
  1. Creates a per-job output directory
  2. Starts a gRPC telemetry server on a dynamic local port
  3. Spawns your Python entrypoint as a subprocess with these environment variables injected:
Where the runner places the run configuration file on disk is an internal detail. Your scenario reads the path from TETRYX_RUN_CONFIG and does not need to know or hardcode the backing location.
  1. Waits for the process to exit
  2. If an early termination condition fires, sends SIGKILL to the subprocess immediately
  3. Collects output files from TETRYX_OUTPUT_DIR and attaches them to the job result
Your scenario does not need to communicate with the Lynx API directly. Everything flows through the injected environment.

The run configuration

TETRYX_RUN_CONFIG points to a JSON file with this shape:
The parameters keys and values come directly from the dispersions you defined in your batch config. Lynx samples them using the method you specified (Latin Hypercube Sampling in this tutorial).

The scenario

scenarios/aerospace/python/scenario_basic_orbit_lynx.py reads the run config, builds a headless Basilisk simulation, steps through the orbit, and emits scalar telemetry at each time step. Reading the run config:
Streaming telemetry:
Telemetry is optional — if TETRYX_TELEMETRY_URL is not set the client silently no-ops. Your scenario will still run and produce artifacts. Writing artifacts: The scenario writes three files to TETRYX_OUTPUT_DIR before exiting: These become the output_files attached to the job result, retrievable via GET /jobs/{job_id}/results.

Dispersed parameters

The tutorial config (scenarios/aerospace/configs/basic_orbit_tutorial.yaml) disperses these orbital elements using uniform distributions: Each job in the batch receives a unique point sampled from this 7-dimensional space via Latin Hypercube Sampling. With LHS, 50 runs give better coverage than 50 purely random draws.

Runner configuration

The runner needs to know which Python executable to use, where the scenario entrypoint lives, and where to write per-job output. These are set in the runner’s YAML config file. A minimal config for this tutorial (configs/lynx-runner-basilisk.yaml):
Connection credentials (the platform endpoint) are set via environment variables provided during onboarding — they are not stored in this file. All config fields can also be set via environment variables, which is the preferred approach in containerised deployments:

Start a Basilisk runner

Verify it registered and is advertising the right capabilities:
Expected:

Submit the batch

Response:

Monitor progress

Example mid-run output:

Retrieve results

Once the batch status is Completed, fetch all job results:
Each job result looks like:
The output_files paths are on the runner’s local filesystem. Artifact upload to S3 (and retrieval via the API) is a planned feature.

Early termination

Early termination lets you kill a simulation mid-run when a condition is met, without waiting for the job timeout. The runner’s watchdog monitors telemetry and sends SIGKILL to the subprocess if a condition fires. To enable it in the batch config:
The condition evaluates against the telemetry variables your scenario is emitting. If orbit.radius_km drops below 6371 km (Earth’s mean radius), the runner kills the subprocess and marks the job failed.

Adapting this scenario to your simulation

The integration contract your scenario must satisfy:
  1. Read TETRYX_RUN_CONFIG at startup — this is the path to your sampled parameter set
  2. Write artifacts to TETRYX_OUTPUT_DIR before exiting
  3. Exit 0 on success, non-zero on failure
  4. Optionally stream scalar telemetry to TETRYX_TELEMETRY_URL during execution
That’s the complete surface area. Your scenario can use any language, framework, or physics engine — as long as it respects those three required points, Lynx will orchestrate it.

What comes next

  • Connect a GitHub Actions workflow that submits a batch on push and fails CI if jobs fail
  • Expand to 500+ runs with "sampling_method": "latin_hypercube" and a fixed seed for reproducible studies
  • Add a second runner type (e.g. px4_sitl) using the same control plane and API
  • Enable artifact upload to S3 for cross-machine artifact retrieval (planned)