Skip to content

synthe_py

Pure-Python SYNTHE spectrum synthesis engine. synthe_py computes atomic and molecular line opacity and solves the LTE radiative-transfer equation to produce a synthetic spectrum from a converged model atmosphere.

The top-level entry point is the opacity engine, which orchestrates wavelength grid construction, line selection, profile accumulation, and the JOSH radiative transfer solver. Most users invoke it indirectly through pykurucz.run_synthe_py().

See also:


Configuration

Configuration models for the Python SYNTHE pipeline.

Attributes

DEFAULT_WAVELENGTH module-attribute

DEFAULT_WAVELENGTH: Tuple[float, float, float] = (300.0, 1800.0, 300000.0)

Default wavelength grid (start, end, resolving power).

Classes

WavelengthGrid dataclass

WavelengthGrid(start: float, end: float, resolution: float, velocity_microturb: float = 0.0, vacuum: bool = True)

Defines the spectral sampling to synthesise.

LineDataConfig dataclass

LineDataConfig(atomic_catalog: Path, molecular_catalogs: List[Path] = list(), include_predicted: bool = False, cache_directory: Optional[Path] = None, allow_tfort_runtime: bool = False, molecular_line_dirs: List[Path] = list(), include_tio: bool = True, include_h2o: bool = False, tio_bin_path: Optional[Path] = None, h2o_bin_path: Optional[Path] = None)

Controls which line lists are included in the synthesis.

Note: Populations are computed from Saha-Boltzmann equations (no fort.10 dependency). Line opacity is computed from first principles using the atomic catalog.

Attributes
molecular_line_dirs class-attribute instance-attribute
molecular_line_dirs: List[Path] = field(default_factory=list)

Directories containing Kurucz ASCII molecular .dat/.asc files.

include_tio class-attribute instance-attribute
include_tio: bool = True

Include Schwenke TiO binary line list (rschwenk) when the binary is found.

include_h2o class-attribute instance-attribute
include_h2o: bool = False

Include Partridge-Schwenke H2O binary line list (rh2ofast) when the binary is found. Disabled by default: the Fortran reference tfort.12 has all 12.6M H2O records with out-of-range NBUFF values, so Fortran synthe.for effectively skips every H2O line. Enable with --h2o if using a corrected H2O compilation.

tio_bin_path class-attribute instance-attribute
tio_bin_path: Optional[Path] = None

Explicit path to schwenke.bin (or eschwenke.bin). Auto-located if None.

h2o_bin_path class-attribute instance-attribute
h2o_bin_path: Optional[Path] = None

Explicit path to h2ofastfix.bin. Auto-located if None.

AtmosphereInput dataclass

AtmosphereInput(model_path: Path, format: str = 'atlas12', npz_path: Optional[Path] = None)

Describes the input model atmosphere.

OutputConfig dataclass

OutputConfig(spec_path: Path, diagnostics_path: Optional[Path] = None)

Specifies the artefacts produced by the pipeline.

SynthesisConfig dataclass

SynthesisConfig(wavelength_grid: WavelengthGrid, line_data: LineDataConfig, atmosphere: AtmosphereInput, output: OutputConfig, cutoff: float = 0.001, linout: int = 30, nlte: bool = False, scattering_iterations: int = 8, scattering_tolerance: float = 0.001, rhoxj_scale: float = 0.0, log_level: str = 'INFO', enable_helium_wings: bool = True, skip_hydrogen_wings: bool = False, line_filter: bool = True, wavelength_subsample: int = 1, n_workers: Optional[int] = None)

Global settings for a SYNTHE run.

Methods:
from_cli classmethod
from_cli(spec_path: Path, diagnostics_path: Optional[Path], atmosphere_path: Path, atomic_catalog: Path, wl_start: float, wl_end: float, resolution: float, velocity_microturb: float = 0.0, vacuum: bool = True, cutoff: float = 0.001, linout: int = 30, nlte: bool = False, scattering_iterations: int = 8, scattering_tolerance: float = 0.001, rhoxj_scale: float = 0.0, enable_helium_wings: bool = True, skip_hydrogen_wings: bool = False, line_filter: bool = True, wavelength_subsample: int = 1, npz_path: Optional[Path] = None, n_workers: Optional[int] = None, allow_tfort_runtime: bool = False, molecular_line_dirs: Optional[List[Path]] = None, include_tio: bool = True, include_h2o: bool = True, tio_bin_path: Optional[Path] = None, h2o_bin_path: Optional[Path] = None) -> 'SynthesisConfig'

Helper for the default CLI entry point.

Functions:

discover_default_molecular_line_directories

discover_default_molecular_line_directories() -> List[Path]

Return data/molecules inside the pykurucz repo if it exists.

The data/ directory is populated by running scripts/download_data.py once. If the directory is absent or empty, returns an empty list (atomic-line synthesis only).

Engine — Opacity

Core synthesis loop.

Classes

Functions:

run_synthesis

run_synthesis(cfg: SynthesisConfig, *, synthe_policy=None) -> SynthResult

Execute the high-level synthesis pipeline.

Engine — Radiative Transfer

LTE radiative transfer helpers using the Kurucz JOSH solver.

Functions:

solve_lte_spectrum

solve_lte_spectrum(wavelength_nm: ndarray, temperature: ndarray, column_mass: ndarray, cont_abs: ndarray, cont_scat: ndarray, line_opacity: ndarray, line_scattering: ndarray, line_source: Optional[ndarray] = None, n_workers: Optional[int] = None) -> Tuple[np.ndarray, np.ndarray]

Solve LTE radiative transfer for a spectrum.

Parameters

wavelength_nm: Wavelength array (nm) temperature: Temperature array (K) for each depth column_mass: Column mass array (g/cm²) for each depth cont_abs: Continuum absorption opacity (n_depths, n_wavelengths) cont_scat: Continuum scattering opacity (n_depths, n_wavelengths) line_opacity: Line absorption opacity (n_depths, n_wavelengths) line_scattering: Line scattering opacity (n_depths, n_wavelengths) line_source: Optional line source function (n_depths, n_wavelengths) n_workers: Number of parallel workers. If None or 1, uses sequential processing. If > 1, uses multiprocessing.

Returns

flux_total: Total flux (HNU) for each wavelength flux_cont: Continuum flux (HNU) for each wavelength

I/O — Atmosphere

Atmosphere file handling for the Python SYNTHE pipeline.

Classes

AtmosphereModel dataclass

AtmosphereModel(depth: ndarray, temperature: ndarray, gas_pressure: ndarray, electron_density: ndarray, mass_density: ndarray, turbulent_velocity: ndarray, metadata: Dict[str, str], tkev: Optional[ndarray] = None, tk: Optional[ndarray] = None, tlog: Optional[ndarray] = None, hkt: Optional[ndarray] = None, continuum_tables: Optional[ContinuumTables] = None, continuum_frequency: Optional[ndarray] = None, continuum_wledge: Optional[ndarray] = None, continuum_half_edge: Optional[ndarray] = None, continuum_delta_edge: Optional[ndarray] = None, continuum_abs_coeff: Optional[ndarray] = None, continuum_scat_coeff: Optional[ndarray] = None, continuum_coeff_log10: bool = False, hckt: Optional[ndarray] = None, txnxn: Optional[ndarray] = None, xnf_h: Optional[ndarray] = None, xnf_he1: Optional[ndarray] = None, xnf_he2: Optional[ndarray] = None, xnf_h2: Optional[ndarray] = None, xnfph: Optional[ndarray] = None, dopph: Optional[ndarray] = None, cont_absorption: Optional[ndarray] = None, cont_scattering: Optional[ndarray] = None, line_source_lower: Optional[ndarray] = None, line_source_upper: Optional[ndarray] = None, population_per_ion: Optional[ndarray] = None, doppler_per_ion: Optional[ndarray] = None, xabund: Optional[ndarray] = None, bhyd: Optional[ndarray] = None, bc1: Optional[ndarray] = None, bc2: Optional[ndarray] = None, bsi1: Optional[ndarray] = None, bsi2: Optional[ndarray] = None, xnatm: Optional[ndarray] = None, atlas_tables: Optional[Dict[str, ndarray]] = None, xnfpc: Optional[ndarray] = None, xnfpmg: Optional[ndarray] = None, xnfpsi: Optional[ndarray] = None, xnfpal: Optional[ndarray] = None, xnfpfe: Optional[ndarray] = None, xnfpch: Optional[ndarray] = None, xnfpoh: Optional[ndarray] = None)

Structured representation of a 1D stellar atmosphere.

Functions:

load_cached

load_cached(path: Path) -> AtmosphereModel

Load a cached numpy .npz representation of the atmosphere.

save_cached

save_cached(model: AtmosphereModel, path: Path) -> None

Persist the atmosphere to an .npz archive.

iterate_layers

iterate_layers(model: AtmosphereModel) -> Iterable[int]

Yield layer indices in order of increasing optical depth.

I/O — Export

Export utilities for SYNTHE outputs.

Classes

Functions:

write_spec_file

write_spec_file(result: SynthResult, destination: Path) -> None

Write the synthesized spectrum to a .spec file.

The legacy Fortran code emitted three columns per row: wavelength, emergent flux, and continuum flux. The Python reimplementation produces the same layout for drop-in comparability.

I/O — spectrv

Helpers for parsing spectrv input cards.

Classes

SpectrvParams dataclass

SpectrvParams(rhoxj: float, ph1: float, pc1: float, psi1: float, prddop: float, prdpow: float)

Subset of the spectrv control parameters used in the LTE run.

Functions:

load

load(path: Path) -> SpectrvParams

Parse the standard spectrv input deck (e.g. spectrv_std.input).

Physics — Continuum Opacity

Continuum opacity calculations mirroring the legacy SYNTHE algorithm.

Classes

Functions:

interpolate_continuum

interpolate_continuum(tables: ContinuumTables, wavelengths: ndarray) -> np.ndarray

Reproduce the DO 2005 loop filling CONTINUUM(NBUFF).

finalize_continuum

finalize_continuum(log_continuum: ndarray) -> np.ndarray

Apply the final 10** conversion (loop 2006).

build_continuum_grid

build_continuum_grid(atmosphere: 'AtmosphereModel', wavelength_grid: ndarray) -> np.ndarray

Compute continuum opacity for each layer using legacy interpolation.

build_depth_continuum

build_depth_continuum(atmosphere: 'AtmosphereModel', wavelength_grid: ndarray) -> tuple[np.ndarray, np.ndarray, Optional[np.ndarray], Optional[np.ndarray]]

Return per-depth continuum absorption, scattering, and optional line sources.

This function matches Fortran's approach: uses log10 coefficients (CONTABS/CONTSCAT) from fort.10, interpolates them, then converts to linear with 10**. Requires wledge and cont_abs_coeff/cont_scat_coeff to be present in the atmosphere model.

Physics — Populations

Population and state computations for atmosphere layers.

Classes

DepthState dataclass

DepthState(boltzmann_factor: ndarray, doppler_width: ndarray, turbulence_width: float, electron_density: float, temperature: float, continuum_opacity: ndarray, hckt: float, txnxn: float, hydrogen: Optional[HydrogenDepthState] = None)

Derived physical quantities for one atmospheric depth.

Populations dataclass

Populations(layers: Dict[int, DepthState])

Holds precomputed populations for all depths.

Functions:

compute_depth_state

compute_depth_state(atmosphere: AtmosphereModel, line_wavelengths: ndarray, excitation_energy: ndarray, microturb_kms: float, elements: Optional[ndarray] = None) -> Populations

Compute LTE-like populations and Doppler widths per depth.

CRITICAL FIX (Dec 2025): Use element-specific atomic masses for thermal velocity. Fortran xnfpelsyn.for line 488: DOPPLE = SQRT(2*TK/ATMASS(NELEM)/1.660D-24 + ...) Previous Python code used hydrogen mass for all elements, causing: - Fe lines: Doppler widths 7.5x too large (sqrt(56) factor) - This affects wing profiles and total line opacity

Physics — Line Broadening

Line broadening utilities.

Classes

Functions:

damping_parameter

damping_parameter(depth_state: DepthState, gamma_rad: ndarray, gamma_stark: ndarray, gamma_vdw: ndarray) -> np.ndarray

Compute the damping parameter a for the Voigt profile.

Physics — JOSH Solver

Functions:

solve_josh_flux

solve_josh_flux(acont: ndarray, scont: ndarray, aline: ndarray, sline: ndarray, sigmac: ndarray, sigmal: ndarray, column_mass: ndarray) -> float

Compute the emergent flux for a single frequency using the JOSH solver.

Option 2: Use higher precision arithmetic when alpha (scattering) is large to reduce numerical errors. This is especially important when alpha > 0.1.