Skip to main content

Simulation Pipeline

This page describes how a simulation flows from configuration to output.

Pipeline Stages

config.json


┌─────────┐
│ Config │ load_config() parses JSON
└────┬─────┘


┌──────────┐
│ Geometry │ Layer placement, r0 profile, wind profile
└────┬─────┘


┌─────────┐
│ Layers │ Phase screen initialization (Von Karman, autoregressive)
└────┬────┘


┌────────────┐
│ Propagator │ Precompute transfer functions for angular spectrum method
└────┬───────┘


┌───────────┐
│ Time Loop │ Propagate → Record → Extrude (repeat for N steps)
└────┬──────┘


┌────────┐
│ Output │ (steps, 2, nx_size, nx_size) frames
└────────┘

Stage Details

1. Configuration

load_config() reads config.json and returns a nested dictionary. All leaf parameters use the {"value": ..., "description": ...} format.

2. Geometry

The Geometry dataclass (core/geometry/geometry.py) constructs the atmospheric model:

  • Cn2 profile: Hufnagel-Valley model with parameter A solved from the user-specified total r0 via solve_for_A() (uses scipy.optimize.fsolve)
  • Layer placement: Heights are chosen so each layer contributes equally to the fractional scintillation index (integrates Cn2(h) * h^(5/6))
  • r0 profile: Per-layer Fried parameters computed from the Cn2 profile
  • Wind profile: Bufton wind model; satellite slew rate contributes to effective wind at each layer height

3. Layers

Each layer wraps a PhaseScreenVonKarman -- an infinite-length phase screen generated via autoregressive row extrusion:

  • Initial screen: Created using ft_sh_phase_screen() (FFT-based with subharmonic compensation)
  • Autoregressive model: Uses pre-computed A_mat and B_mat matrices. New rows are generated as:
    new_row = A_mat @ stencil_data + B_mat @ random_noise
  • GPU conversion: GpuLayer wraps the NumPy arrays as tf.Variable and tf.constant for use in @tf.function

4. Propagator

The Propagator class (core/propagation/propagator.py) implements the angular spectrum method for Fresnel propagation:

  • Precomputes quadratic phase factors Q1, Q2, Q3 and magnification factors per layer
  • All NumPy arrays are pre-converted to TF tensors at init time (tf_Q1, tf_Q2, tf_Q3, tf_m, tf_deltaf, tf_delta, tf_sg) to avoid baking Python/NumPy constants into the graph during @tf.function tracing
  • Applies a super-Gaussian window (exp(-r^16)) to suppress edge artifacts
  • Downlink geometry: Reverses the layer order and recalculates propagation distances to model satellite-to-ground direction

5. Time Loop

At each time step:

  1. Propagate: A flat unit-amplitude wavefront passes through all layers via the angular spectrum method
  2. Record: The output complex field is decomposed into phase (angle(Uout)) and amplitude (abs(Uout))
  3. Extrude: Each layer advances by v_wind * dt / pixel_scale pixel rows, generating new turbulence via the autoregressive model

6. Output

The recorded frames are collected into a (steps, 2, nx_size, nx_size) array and saved to disk. See Output Format for details.

The simulator models satellite-to-ground propagation:

  • The beam originates at the satellite (highest layer) and propagates downward
  • Layer order is reversed internally so propagation proceeds from high altitude to ground
  • Propagation distances account for the zenith angle: distance = height / cos(zenith)
  • The satellite orbit altitude determines the starting range and slew rate contribution to wind