ForMoSA is an open-source Python package for modeling exoplanetary atmospheres using a forward modeling approach. It compares observed spectra and photometry against grids of atmospheric models via nested sampling to derive posterior distributions on physical parameters.
📖 Full documentation: https://formosa.readthedocs.io
- Class-based API centred around a single
Analysisentry point - Multi-instrument support (MOSAIC) — fit spectroscopic and photometric data simultaneously from multiple instruments
- Three nested-sampling back-ends — nestle, PyMultiNest, and UltraNest
- High-contrast mode — model stellar speckles and systematics alongside the companion signal
- Photometry filter service — automatic retrieval and caching of filter curves from the SVO Filter Profile Service
- Configurable prior distributions — uniform, log-uniform, Gaussian, and constant priors
- Comprehensive plotting — corner plots, chain diagnostics, radar diagrams, best-fit spectra, CCFs, and RV–v sin i maps
- Flexible configuration — INI-based config files or direct Python dataclass instantiation
pip install ForMoSA
conda install dask netCDF4 bottleneckgit clone https://github.com/exoAtmospheres/ForMoSA.git
cd ForMoSA
pip install -e .
conda install dask netCDF4 bottleneckSee the installation guide for PyMultiNest, GPU/torch, and macOS Apple Silicon instructions.
from ForMoSA import Analysis
from ForMoSA.config.global_config import (
ConfigPath, ConfigAdapt, ConfigInversion, ConfigParameters
)
# 1. Define paths
config_path = ConfigPath(
observation_path=["path/to/observation.fits"],
adapt_store_path="path/to/adapted_grid/",
result_path="path/to/results/",
model_path="path/to/model_grid.nc",
)
# 2. Initialise the analysis
analysis = Analysis(config_path)
# 3. Configure adaptation & inversion
config_adapt = ConfigAdapt(method="linear")
config_inversion = ConfigInversion(ns_algo="pymultinest", npoints=100)
config_parameters = ConfigParameters(
par1=["uniform", "500", "3000"], # e.g. Teff
par2=["uniform", "2.5", "5.5"], # e.g. log(g)
r=["uniform", "0.5", "3.0"], # radius in R_Jup
d=["constant", "50"], # distance in pc
)
# 4. Adapt the model grid to the observations
analysis.adapt(config_adapt, config_inversion)
# 5. Run nested sampling
analysis.nested_sampling(config_parameters, config_adapt, config_inversion)
# 6. Plot results
analysis.plot(analysis.ns.results)ForMoSA/
├── analysis.py # Main Analysis class
├── config/ # Configuration dataclasses & file I/O
├── core/ # Enums, errors, logging, plot configs
├── filter/ # SVO photometry filter interface
├── grid/ # Model grids, subgrids, adaptation
├── nested_sampling/ # NS engine, post-processing, results, plotting
├── observation/ # Observation classes (spectral, photometric, set)
├── parameter/ # Parameters, priors, parameter sets
├── transform/ # Physics & observational effect pipelines
└── utils/ # Spectral tools, likelihood & prior functions
v2.0.0 is a complete rewrite and is not backwards-compatible with v1.x. See the full migration guide in the docs.
| Area | v1.x | v2.0.0 |
|---|---|---|
| Entry point | main.py script + launch_adapt() / launch_nested_sampling() |
Single Analysis class |
| Configuration | config.ini + GlobFile |
Python dataclasses (ConfigPath, ConfigAdapt, …) |
| Observations | Raw arrays from INI file paths | Typed ObservationSet loaded from .fits |
| Model grid handling | adapt/ functions |
grid/ subgrid classes |
| Photometry filters | Bundled .npz files |
Auto-downloaded from SVO and cached |
| CCF / RV–v sin i | ✗ | ✓ new via analysis.plot_ccf() / analysis.plot_rv_vsini_map() |
| Logging | print statements |
Python logging module |
| Error handling | Generic exceptions | ForMoSAError |
Replace the old script-based calls:
# v1.x (old)
from ForMoSA.global_file import GlobFile
from ForMoSA.adapt.adapt_obs_mod import launch_adapt
from ForMoSA.nested_sampling.nested_sampling import launch_nested_sampling
global_params = GlobFile("config.ini")
launch_adapt(global_params, adapt_model=True)
launch_nested_sampling(global_params)with the new class-based API:
# v2.0.0 (new)
from ForMoSA import Analysis
from ForMoSA.config.global_config import ConfigPath, ConfigAdapt, ConfigInversion, ConfigParameters
analysis = Analysis(ConfigPath(
observation_path=["obs.fits"],
adapt_store_path="adapted_grid/",
result_path="results/",
model_path="model_grid.nc",
))
analysis.adapt(ConfigAdapt(), ConfigInversion())
analysis.nested_sampling(ConfigParameters(par1=["uniform","500","3000"], r=["uniform","0.5","3.0"], d=["constant","50"]))
analysis.plot(analysis.ns.results)If you need the old behaviour in the short term, pin to formosa==1.1.6.
If you use ForMoSA in your research, please cite Petrus et al. (2023).
If you encounter any problems, please open an issue on GitHub.
Our sincere thanks to Code/Astro.
