neoVULCAN is a fork of VULCAN — a photochemical kinetics code for exoplanetary atmospheres. This fork tracks work in progress: a JAX-based chemistry kernel, alternative Rosenbrock solvers, and a typed TOML-based configuration system.
The theory papers of VULCAN are Tsai et al. 2017 (thermal chemistry) and Tsai et al. 2021 (with photochemistry). Chemical equilibrium initialisation uses FastChem.
The full documentation can be found here: https://newstrangeworlds.github.io/neoVULCAN/
Requires Python ≥ 3.10. Install dependencies into the environment of your choice:
pip install -r requirements.txtThe required packages are NumPy, SciPy, JAX, pyfastchem, Matplotlib, Pydantic, Pillow (optional),
and Sympy. On Python 3.10 the TOML loader uses the tomli backport; on 3.11+ it uses the
stdlib tomllib. For GPU acceleration, install the CUDA variant of JAX (see the comment in
requirements.txt).
Edit vulcan_cfg.toml (or copy one of the example configs from cfg_examples/), then run:
python vulcan.py -c vulcan_cfg.tomlBoth arguments accept defaults: -c vulcan_cfg.toml is implicit, and the flag -n skips
regeneration of the auto-generated chemistry module (use when the network hasn't changed
since the last run).
Output is written to output/<out_name>.vul (a pickle file) plus a JSON dump of the
fully-resolved configuration alongside it.
To produce plots from a .vul file, see plot_py/.
Runs are configured through TOML files validated against a Pydantic schema (src/neovulcan_config.py). The schema groups fields into ten sections:
| Section | What it controls |
|---|---|
network |
Atom list, path to the reaction network, photo-cross-section directory |
paths |
All filesystem paths for input/output and FastChem |
elements |
Elemental abundances or solar mix; initial mixing-ratio strategy |
atmosphere |
Vertical grid, pressure range, T-P profile source, gravity, stellar geometry |
photochemistry |
Photolysis switches, RT scheme, wavelength binning |
boundary_conditions |
Top/bottom fluxes, fixed mixing ratios, diffusion-limited escape |
condensation |
Condensible species, particle properties, relaxation |
solver |
ODE solver choice, tolerances, step controls, Newton finisher knobs |
output |
What to save to the .vul file |
plotting |
Live plots, movies, species to track |
The schema rejects unknown keys (catches typos), validates enum values (e.g.
atm_type ∈ {"isothermal", "analytical", "file", "vulcan_ini", "table"}), and enforces
cross-field constraints (P_t < P_b, use_ion ⇒ use_photo, etc.). Several fields have
sensible defaults — omit them from the TOML to use the default.
To see every available field with its default value and type, look at vulcan_cfg_defaults.toml. That file is generated from the schema via:
python tests/_gen_defaults_toml.pyExample configurations are in cfg_examples/:
Earth.cfg— Earth-like atmosphere (N2/O2, photochemistry, water + sulfate condensation)Jupiter.cfg— Jupiter (H2 atmosphere, water and ammonia condensation, NCHO low-T network)HD189.cfg— HD 189733b hot Jupiter (SNCHO network with sulfur and helium)
For external models that want to drive neoVULCAN as a chemistry step:
import sys
sys.path.insert(0, '/path/to/VULCAN/neoVULCAN')
from neoVULCAN import VulcanChemistry
chem = VulcanChemistry(
base_dir='/path/to/VULCAN/neoVULCAN',
config_path='vulcan_cfg.toml', # default — pass a different path to switch configs
cfg_overrides={'solver': {'rtol': 0.1}}, # optional nested-dict overrides
)
chem.initialize()
for step in range(n_steps):
T_new, P_new, Kzz_new = atm_model.get_profiles()
chem.set_atmosphere(T=T_new, P=P_new, Kzz=Kzz_new)
chem.run_to_convergence()
ymix = chem.get_mixing_ratios() # (nz, ni)
atm_model.update_chemistry(ymix, chem.species)See vulcan_api.py for the full API.
neoVULCAN/
├── vulcan.py # script entry point
├── vulcan_api.py # library entry point (VulcanChemistry)
├── __init__.py # re-exports VulcanChemistry
├── vulcan_cfg.toml # active configuration
├── vulcan_cfg_defaults.toml # reference: every field, its type, its default
│
├── src/ # library modules
│ ├── neovulcan_config.py Pydantic schema (VulcanConfig)
│ ├── neovulcan_runtime.py process-singleton holding the loaded config
│ ├── make_chemistry_jax.py generator: produces chemistry_jax.py from a network file
│ ├── chemistry_jax.py auto-generated; do not edit by hand
│ ├── integration.py time-stepping loop
│ ├── ros2.py, rodas3.py Rosenbrock ODE solvers
│ ├── ode_solver.py base ODE machinery, banded Jacobian assembly
│ ├── build_atm.py, store.py atmospheric setup, data containers
│ ├── rates.py reaction-rate reader
│ ├── radiative_transfer.py two-stream / DisORT photochemistry
│ ├── condensation.py condensation kinetics
│ ├── output.py .vul file writer, plotting helpers
│ ├── phy_const.py physical constants
│ └── jacobian_jax.py JAX-fused Jacobian kernel
│
├── tests/ # pytest tests + benchmarks + dev tools
├── plot_py/ # plotting scripts for .vul output
├── tools/ # miscellaneous utilities
├── cfg_examples/ # example .cfg files (Earth, Jupiter, HD189)
├── atm/ # T-P-Kzz profiles, stellar fluxes, boundary-condition files
├── thermo/ # reaction networks, NASA-9 thermodata, photolysis cross-sections
├── fastchem_input/ # FastChem equilibrium-chemistry input
├── output/ # default .vul output directory
└── plot/ # default plot directory
pytest tests/ # full suite
pytest tests/ -m "not slow" # skip slow regression testsThe migration-time parity tests in tests/test_config_migration.py
also cover Pydantic schema correctness (typo rejection, alias semantics, cross-field validators).
The radiative-transfer snapshot test (tests/test_rt_refactor.py)
auto-skips if its rt_snapshot.pkl was captured against a different network; regenerate via
python tests/capture_rt_snapshot.py.
GPL v3 — see licence.md.