Configuration and filters

Instrument and telescope properties (mirrors, optics, coating throughputs, filters, detector tables) are not hard-coded in esc_etc. They come from two sets of TOML files and support data bundled under esc_etc/data/lazuli/: telescope for the Lazuli 3 m telescope and esc for the ESC instrument. Both are snapshots of configuration packages maintained by the University of Arizona Space Astrophysics Laboratory (UASAL), config_um and config_um_esc (“UM” is the UA code name for Lazuli). Because the copies ship inside esc_etc, a plain pip install needs no git access.

The default instrument

Simulation.from_default assembles the telescope, sensor, scene and coronagraph from the bundled files for one optical arm of the ESC. The instrument has two arms, each with its own optical train and filter wheel; the default is "arm_a".

from esc_etc import Simulation

sim = Simulation.from_default(arm="arm_a")
sim = Simulation.from_default(filter="F630N", lifetime="eol")

filter picks the ESC filter whose measured end-to-end throughput curve is the sensor bandpass; the telescope’s design wavelength follows it. The default is esc_etc.DEFAULT_FILTER ("F630N", slot R2), so the two calls above build the same simulation. lifetime selects the "eol" curve set (end of life, the default) or "bol" (beginning of life); any other value raises ValueError. filter=None is the legacy option: the generic box-filter chain assembled from the configuration files, which is wider and a little more transmissive than any real filter and has no lifetime variants.

The ESC filters

A filter is named either by its wheel slot or by its designation; both are keys of esc_etc.FILTERS. The N filters have a 2% bandwidth, the W filters 10%.

Slot

Designation

Arm

Central wavelength

Bandwidth

B1

F400N

blue

400.0 nm

2%

B2

F450N

blue

450.0 nm

2%

B3

F475N

blue

475.0 nm

2%

B4

F450W

blue

450.0 nm

10%

B5

F425N

blue

425.0 nm

2%

B6

F425W

blue

425.0 nm

10%

R1

F620N

red

620.0 nm

2%

R2

F630N

red

630.0 nm

2%

R3

F659W

red

659.0 nm

10%

R4

F685N

red

685.0 nm

2%

R5

F725.9N

red

725.9 nm

2%

R6

F656N

red

656.0 nm

2%

The curves are the Lazuli throughput budget of 2026-03-25: telescope plus ESC, including detector quantum efficiency and a polarization factor of 0.5, and at end of life also contamination and the budget reserves. They exclude the vortex off-axis throughput and the Lyot stop area, which esc_etc applies itself. DATA_SOURCES.md in the repository records the provenance of every bundled file.

Building the components yourself

Each component has its own default constructor, keyed on the same arm and filter. The telescope and the sensor must be built for the same filter; mixing them raises ValueError:

from esc_etc import Telescope, Sensor, Coronagraph, get_scene

telescope   = Telescope.from_default(arm="arm_a", filter="F630N")
sensor      = Sensor.from_default(telescope, arm="arm_a", filter="F630N")
scene       = get_scene(telescope, hoststarmag=-0.353, planetdeltamag=20.0)
coronagraph = Coronagraph.from_default(telescope, raw_dh_contrast=1e-8,
                                       ppgain=10, separation=0.4)

Supplying your own configuration

The from_config constructors take an already-loaded configuration dictionary and a support-data path, so you can point the ETC at a custom instrument that follows the same layout as the bundled snapshot, which is loaded the same way:

from esc_etc import uasal_config

coronagraph = Coronagraph.from_config(
    instrument_config=uasal_config.load_config_values(uasal_config.INSTRUMENT),
    instrument_support_path=uasal_config.get_data_path(uasal_config.INSTRUMENT),
    telescope=telescope,
    raw_dh_contrast=1e-8, ppgain=10, separation=0.4, arm="arm_a",
)

See Sensor.from_config, Telescope.from_config and Coronagraph.from_config for the exact signatures.

Overriding parameters at runtime

You do not need to edit configuration files to explore parameter changes. Every component exposes its tunable parameters through update() with child__key syntax, and reset() reverts the whole simulation:

sim.update(coronagraph__separation=0.6)
sim.update(coronagraph__raw_dh_contrast=5e-9, coronagraph__ppgain=20)
sim.update(sensor__aperture="optimal")
sim.reset()

Call describe() on a component (sim.sensor.describe()) to print its tunable parameters.

What is in the bundled snapshot

Path under esc_etc/data/

Contents

lazuli/telescope/configs/*.toml

Telescope geometry and coatings (M1 to M4), observatory pointing and jitter, zodiacal-light defaults.

lazuli/telescope/support_data/

Zodiacal-light spectral template and the mirror coating reflectance curves.

lazuli/esc/configs/*.toml

The ESC optical train per arm, source and spectrum defaults, and an EXOSIMS parameter file.

lazuli/esc/support_data/

ESC coatings, the ZWO detector tables (gain, read noise, well depth, dark current, quantum efficiency), the polarizer transmission and the vortex off-axis throughput curve.

lazuli/SNAPSHOT.txt

Which UASAL package and revision each directory was copied from, and when.

esc_filters_eol/, esc_filters_bol/

The twelve filter curves above, at end of life and at beginning of life.

lazuli.mplstyle

The matplotlib style the plot methods apply.

Maintainers refresh the snapshot from the installed UASAL packages with pip install -r scripts/requirements-uasal.txt followed by python scripts/sync_uasal_configs.py, then commit esc_etc/data/lazuli/. The mapping from the bundled names to the upstream packages is esc_etc.uasal_config.UPSTREAM, and tests/test_uasal_snapshot.py fails when the installed packages and the snapshot drift apart.