A modular chirped-pulse amplification (CPA) simulation package.
cpa-sim is a physics-oriented pipeline for modeling a canonical CPA chain:
pulse initialization → stretcher → fiber propagation → amplification → compressor → metrics.
The project is structured to keep stage interfaces stable while allowing backend implementations to evolve (for example, from placeholder deterministic transforms to solver-backed physics models).
CPA experiments and design studies need repeatable simulation workflows with:
- consistent units and sign conventions,
- deterministic runs (same config + seed → same summary outputs),
- stage-level provenance and metric reporting,
- test tiers that separate fast contract checks from slower physics regression checks.
cpa-sim focuses on those software guarantees first so model fidelity can scale safely over time.
The default v1 chain is shown below.
flowchart LR
A["PulseInitStage<br/>Initialize pulse and grid"] --> B["FreeSpaceStage<br/>Stretcher"]
B --> C["FiberStage<br/>Nonlinear propagation"]
C --> D["AmpStage<br/>Gain model"]
D --> E["FreeSpaceStage<br/>Compressor"]
E --> F["MetricsStage<br/>Compute metrics and plots"]
F --> G["ReportStage (optional)<br/>Summarize provenance and validation"]
The repository currently provides:
- a deterministic sequential pipeline builder and runner,
- typed configuration/state models,
- stage registries/backends for laser generation, free-space, fiber, amplification, and metrics,
- test markers for
unit,integration,physics, andslowworkflows.
See STATUS.md for progress tracking and release-readiness checklists.
pip install -e .pip install -e .[dev]
# optional WUST-FOG backend dependency
pip install -e .[gnlse]pip install -e .For development tooling and optional WUST-FOG gnlse backend support:
pip install -e .[dev]
pip install -e .[gnlse]cpa-sim run configs/examples/treacy_stage_validation.yaml --out out/treacy-stage-validationOther canonical examples:
# optional: these require the WUST-FOG gnlse dependency
cpa-sim run configs/examples/simple_fiber_dispersion.yaml --out out/simple-fiber-dispersion
cpa-sim run configs/examples/wave_breaking_raman.yaml --out out/wave-breaking-raman
cpa-sim run configs/examples/fiber_amp_spm.yaml --out out/fiber-amp-spm
cpa-sim run configs/examples/end_to_end_1560nm.yaml --out out/end-to-end-1560nmEach run writes these CLI artifacts into --out:
metrics.json(overall + per-stage metrics),artifacts.json(artifact path registry),stage_plots/with per-stage SVG time-intensity and spectrum plots,- metrics-stage input/output overlays (
metrics_time_intensity_overlay.svg,metrics_spectrum_overlay.svg), - optional
state_final.npzwhen--dump-state-npzis passed.
Example with NPZ state dump enabled:
cpa-sim run configs/examples/treacy_stage_validation.yaml --out out/treacy-stage-validation --dump-state-npzUse a fixed runtime.seed in your YAML config to keep summary outputs deterministic for a given config + backend setup. The checked-in configs under configs/examples/ are intended as stable starting points for reproducible runs.
from cpa_sim.models import PipelineConfig
from cpa_sim.pipeline import run_pipeline
cfg = PipelineConfig() # start from deterministic defaults
result = run_pipeline(cfg)
print(result.metrics)
print(result.state.meta)| Stage type | kind values available now |
Notes |
|---|---|---|
laser_gen |
analytic |
Deterministic pulse/grid initialization backend. |
free_space |
treacy_grating_pair, phase_only_dispersion |
Used for stretcher/compressor roles depending on stage placement. |
fiber |
fiber |
Physics/numerics split with numerics.kind (toy_phase or wust_gnlse). |
amp |
simple_gain, fiber_amp_wrap |
Simple gain and a fiber amplifier wrapper backend. |
metrics |
standard |
Canonical metrics/artifact recording backend (always final stage in pipeline). |
Related docs:
- Examples:
docs/examples/simple-fiber-dispersion.md,docs/examples/wave-breaking-raman.md,docs/examples/fiber-amp-spm.md,docs/examples/treacy-stage-validation.md,docs/examples/end-to-end-1560nm.md - Validation:
docs/validation/auto_window.md - ADRs:
docs/adr/ADR-0001-conventions-units.md,docs/adr/ADR-0002-result-schema-contract.md,docs/adr/ADR-0003-validation-tiers-ci-policy.md,docs/adr/ADR-0008-canonical-output-layout.md
Use AutoWindow policy flags from the CLI to auto-rerun eligible free-space stages with a larger time window when edge wraparound is detected:
cpa-sim run configs/examples/treacy_stage_validation.yaml --out out/treacy-stage-validation --auto-window --auto-window-printWith --auto-window and no --auto-window-stages, pipeline policy defaults target configured
free-space stages only. Include fiber stage names explicitly in --auto-window-stages (or
cpa.auto_window.stages in Python policy) when you want fiber auto-reruns.
See docs/validation/auto_window.md for limits (including why this does not fix Nyquist aliasing).
The top-level PipelineConfig includes these sections:
runtime(seed and run controls),laser_gen(initial pulse/beam specification),stretcher/compressordefaults (free-space configs used whenstagesis not set),fiber(FiberStageCfgwith stablephysicsplus backend-specificnumerics),amp(amplifier backend config; supportssimple_gainandfiber_amp_wrapwithpower_out_wcontrol),stages(optional arbitrary ordered list offree_space,fiber, andampstage configs),metrics(summary metric backend; always appended at pipeline end).
This keeps public configuration stable while backend selection happens per stage via kind.
runtimeis run-level metadata/control (for example seed) and is not a processing stage.- stage configs (
laser_gen,stretcher,fiber,amp,compressor,metrics) define model parameters and backendkindper stage. policyis a pipeline-wide override bag passed at execution time for cross-cutting controls (debug/tolerances/instrumentation) without changing stage config shape.
In short: runtime and policy are global execution concerns, while stage configs define the physical chain itself.
PipelineConfig.stages allows arbitrary permutations of free-space, fiber, and amp stages.
When omitted, the legacy default order remains stretcher -> fiber -> amp -> compressor.
laser_gen is always the first stage and metrics is always the final stage, so baseline
runs without stretching/compression can be expressed by omitting free-space entries from stages.
CLI runs now emit per-stage time/spectrum SVG plots by default into <out>/stage_plots/.
If you use the Python API directly, pass runtime policy {"cpa.emit_stage_plots": true} and
optionally set "cpa.stage_plot_dir".
For new configs, prefer average-power-driven inputs in lab units:
laser_gen:
spec:
pulse:
shape: gaussian
avg_power_w: 0.5 # average power [W]
rep_rate_mhz: 80.0 # repetition rate [MHz]
width_fs: 120.0 # pulse intensity FWHM [fs]
center_wavelength_nm: 1030.0avg_power_w is in watts and rep_rate_mhz is in MHz.
See docs/config/pulse_spec.md for full PulseSpec behavior, including autocorrelation input rules and legacy compatibility.
Prefer these user-facing pulse normalization fields:
peak_power_w(direct peak power),avg_power_w+rep_rate_mhz(lab-friendly average power), orpulse_energy_j(single-pulse energy workflows).
width_fs is the pulse intensity FWHM. If your source measurement is from an autocorrelator,
provide intensity_autocorr_fwhm_fs with a supported shape (gaussian or sech2) and the
simulator will deconvolve to intensity FWHM internally.
Internally, the envelope still uses sqrt(W) scaling where |E(t)|^2 is instantaneous power in W,
and pulse energy is sum(|E|^2 * dt_fs * 1e-15) joules.
Legacy compatibility (deprecated): amplitude is still accepted but should be migrated to
peak_power_w/avg_power_w/pulse_energy_j in new configs.
For fiber_amp_wrap, power_out_w is the target output average power in watts at the stage output plane.
Runnable example logic lives in src/cpa_sim/examples/* and is invoked via module entrypoints.
python -m cpa_sim.examples.simple_fiber_dispersion
python -m cpa_sim.examples.wave_breaking_raman
python -m cpa_sim.examples.fiber_amp_spm
python -m cpa_sim.examples.treacy_stage_validation
python -m cpa_sim.examples.end_to_end_1560nmSee docs/examples/ for per-example guides.
Small public measurement datasets live under data/public/. Unpublished lab data should stay under the gitignored data/local/ mirror layout.
The initial checked-in showcase dataset is data/public/edfa_spectra/. It includes:
- raw Agilent/HP 86142B exports under
raw/2026-03-05/, - deterministic parsed CSV and metadata JSON outputs under
processed/2026-03-05/, - a stable
manifest.csvfor dataset discovery viadataset_id.
Examples, tests, and future tuning workflows should resolve measurement inputs through manifest.csv plus dataset_id; they should not reconstruct file paths from filenames or date folders.
Rebuild the checked-in processed outputs and manifest from the raw exports with:
python scripts/measurements/rebuild_public_edfa_spectra.pycpa-sim run ... --out <dir> uses this canonical output layout:
metrics.json(schemacpa.metrics.v1)overall: aggregate flat metric mapper_stage: stage-grouped metric map
artifacts.json(schemacpa.artifacts.v1)paths: artifact-name to file-path map
stage_plots/<stage>_time_intensity.svg<stage>_spectrum.svgmetrics_time_intensity_overlay.svgmetrics_spectrum_overlay.svg
state_final.npz(optional via--dump-state-npz)
A run returns a StageResult with deterministic state, metrics, and provenance metadata in state.meta.
The test strategy follows tiered validation:
- Unit tests for schema contracts and invariants,
- Integration tests for end-to-end tiny-chain execution,
- Physics tests for canonical/golden targets (tracked as roadmap work),
- Slow tests for larger or longer-running scenarios.
Common local commands:
python -m ruff format .
python -m pre_commit run -a
# if hooks modify files, rerun pre-commit until it passes
python -m pre_commit run -a
python -m mypy src
python -m pytest -q -m "not slow and not physics" --durations=10This repository includes a MkDocs Material site built from docs/.
pip install -e .[dev]
pip install -r docs/requirements.txt
mkdocs serveThen open the local URL printed by MkDocs (typically http://127.0.0.1:8000).
- On pull requests,
.github/workflows/docs-pr.ymlbuilds the documentation withmkdocs build --strictand uploadssite/as a workflow artifact. - On manual trigger (
workflow_dispatch) and nightly schedule,.github/workflows/docs-publish.ymlbuilds and deploys to GitHub Pages using the official Pages actions.
For manual deployment to work, enable GitHub Pages in repository settings with Build and deployment: GitHub Actions.
src/cpa_sim/
measurements/ # parser and manifest-loader utilities for measurement datasets
models/ # configuration, state, provenance models
stages/ # stage interfaces + backend implementations
pipeline.py # chain assembly and deterministic execution entrypoint
data/
public/ # small publishable measurement datasets tracked in Git
local/ # unpublished lab data (gitignored)
tests/
unit/ # fast contract/invariant tests
integration/ # fast end-to-end smoke tests
physics/ # canonical physics regression tests
Near-term focus areas include:
- maturing physics backends and canonical reference cases,
- expanding artifact/report generation,
- hardening schema/contract documentation,
- introducing a stable CLI workflow.
For concrete status, see STATUS.md and ADRs in docs/adr/.
Before opening a PR, run required quality gates:
python -m ruff format .
python -m pre_commit run -a
# if hooks modify files, rerun pre-commit until it passes
python -m pre_commit run -a
python -m mypy src
python -m pytest -q -m "not slow and not physics" --durations=10Keep docs and status files synchronized when behavior changes.
See LICENSE.