Coupling to PROTEUS
This page is the practical recipe for using CALLIOPE inside a PROTEUS coupled run: which TOML blocks matter, which knobs are documented where, and which combinations are known to be safe.
For the underlying control flow (per-iteration sequence diagram, what the wrapper passes to equilibrium_atmosphere(), how warm-starts are constructed, what gets written back into hf_row), see the theory page.
Minimal [outgas] block
[outgas]
module = "calliope" # or "atmodeller" or "dummy"
fO2_shift_IW = 0.0 # log10 shift relative to IW buffer
mass_thresh = 1e16 # kg; doubles as the absolute mass-balance
# tolerance and the per-element zero threshold
T_floor = 700.0 # K, magma temperatures below this are clipped
solver_rtol = 1e-4 # relative tolerance on mass balance
solver_atol = 1e-6 # fsolve step tolerance (mapped to xtol, not atol)
h2_binodal = false # apply Rogers+2025 H2-MgSiO3 binodal override
[outgas.calliope]
include_H2O = true
include_CO2 = true
include_N2 = true
include_S2 = true
include_SO2 = true
include_H2S = true
include_NH3 = true
include_H2 = true
include_CH4 = true
include_CO = true
solubility = true # if false, force Phi_global = 0 (no melt dissolution)
The four primary species (H2O, CO2, N2, S2) must be included; the wrapper will refuse to run with any of them set to false. The seven secondary species can be disabled individually if you want a constrained sub-system (e.g. include_NH3 = false to suppress ammonia chemistry).
Choosing the elemental-budget mode
CALLIOPE needs total H, C, N, S inventories in kilograms. PROTEUS gives you two ways to specify them via [planet]:
[planet]
volatile_mode = "elements" # or "gas_prs"
volatile_reservoir = "mantle" # or "mantle+core"; sets the reservoir for ppmw
volatile_mode = "elements" (most common)
[planet.elements]
use_metallicity = false # if true, scales C/N/S to H from solar abundances
H_mode = "oceans" # "oceans" | "ppmw" | "kg"
H_budget = 1.0 # interpreted per H_mode
C_mode = "C/H" # "C/H" | "ppmw" | "kg"
C_budget = 0.1
N_mode = "ppmw"
N_budget = 2.0
S_mode = "ppmw"
S_budget = 200.0
The wrapper translates these into the four budget fields CALLIOPE expects:
H_mode = "oceans"βhydrogen_earth_oceans = H_budgetH_mode = "ppmw"βH_kg = H_budget * 1e-6 * M_reservoirH_mode = "kg"βH_kg = H_budget
C, N, S follow the same pattern; C/H is interpreted as a mass ratio relative to H.
volatile_mode = "gas_prs" (initial-pressure mode)
[planet]
volatile_mode = "gas_prs"
[planet.gas_prs]
H2O = 220.0 # bar
CO2 = 100.0
N2 = 1.0
S2 = 0.01
# secondary species default to 0.0 unless explicitly set
When volatile_mode = "gas_prs", the wrapper calls get_target_from_pressures(ddict) to back out the elemental inventory implied by the prescribed initial atmosphere; on every subsequent iteration the same elemental inventory is preserved.
Selecting the fO2 dispatch
The PROTEUS schema field [planet].fO2_source selects which CALLIOPE entry point the wrapper calls. The two paths share the same physics; they differ only in which quantity is supplied as input and which is solved for.
fO2_source |
Input | Solved for | Entry point |
|---|---|---|---|
user_constant |
\(\Delta\mathrm{IW}\) | atmospheric + dissolved O | equilibrium_atmosphere |
from_O_budget |
total O mass | \(\Delta\mathrm{IW}\) | equilibrium_atmosphere_authoritative_O |
Under user_constant (the default) CALLIOPE buffers the redox state to the configured outgas.fO2_shift_IW and solves the four-equation H/C/N/S mass balance. The resulting O mass is whatever the equilibrium chemistry requires at that buffer, and is written into hf_row['O_kg_total'] so the rest of PROTEUS can read it. Under from_O_budget the wrapper passes the running whole-planet O total (maintained by the PROTEUS element-budget bookkeeping) as a fifth elemental target, uses outgas.fO2_shift_IW only as an initial-guess hint, and solves a five-equation system that returns the derived \(\Delta\mathrm{IW}\) in hf_row['fO2_shift_IW_derived'].
See Coupling to PROTEUS (theory) for the per-iteration control flow and Authoritative-oxygen mode for the augmented five-residual mass balance.
Worked example: buffered fO2 mode (user_constant)
In this mode the user fixes \(\Delta\mathrm{IW}\) and the chemistry returns the O budget. Set O_mode = "ic_chemistry" so the wrapper does not pre-populate an O target: the first outgas call writes one in, and PROTEUS carries it from there.
config_version = "3.0"
[orbit]
semimajoraxis = 1.0 # [AU]
[planet]
mass_tot = 1.0 # [M_earth]
volatile_mode = "elements"
fO2_source = "user_constant" # buffered mode (the default)
[planet.elements]
H_mode = "oceans"
H_budget = 1.0 # [Earth oceans]
C_mode = "C/H"
C_budget = 0.1 # [C/H mass ratio]
N_mode = "ppmw"
N_budget = 2.0
S_mode = "ppmw"
S_budget = 200.0
O_mode = "ic_chemistry" # O is derived from the chemistry
O_budget = 0.0 # ignored under ic_chemistry
[outgas]
module = "calliope"
fO2_shift_IW = 4.0 # [log10] redox buffer offset (input)
[outgas.calliope]
include_H2O = true
include_CO2 = true
include_N2 = true
include_S2 = true
include_H2 = true
include_CH4 = true
include_CO = true
include_SO2 = true
include_H2S = true
include_NH3 = true
solubility = true
What the wrapper does on the first call: builds the four-element target \((m_\mathrm{H}, m_\mathrm{C}, m_\mathrm{N}, m_\mathrm{S})\) from H_budget etc.; calls equilibrium_atmosphere with fO2_shift_IW = 4.0; reads back the four primary partial pressures, the seven secondary species, the derived O_kg_total, and writes them all into hf_row. Subsequent iterations warm-start from the previous-iteration <s>_bar values and follow the same path.
Worked example: authoritative-O mode (from_O_budget)
In this mode the user fixes the total O mass and the chemistry returns the derived \(\Delta\mathrm{IW}\). The O_mode is one of "ppmw", "kg", or "FeO_mantle_wt_pct" (never "ic_chemistry", which the config-level validator rejects when fO2_source = "from_O_budget" because the chemistry needs a target to invert against). The outgas.fO2_shift_IW value becomes the initial-guess hint for the solver, not the buffered redox state; pick a value near the expected derived \(\Delta\mathrm{IW}\) for fast convergence, but the solver tolerates a poor hint at the cost of more Monte-Carlo restarts.
config_version = "3.0"
[orbit]
semimajoraxis = 1.0 # [AU]
[planet]
mass_tot = 1.0 # [M_earth]
volatile_mode = "elements"
fO2_source = "from_O_budget" # authoritative-O mode
[planet.elements]
H_mode = "oceans"
H_budget = 1.0 # [Earth oceans]
C_mode = "C/H"
C_budget = 0.1 # [C/H mass ratio]
N_mode = "ppmw"
N_budget = 2.0
S_mode = "ppmw"
S_budget = 200.0
O_mode = "FeO_mantle_wt_pct" # interpret O_budget as mantle FeO wt%
O_budget = 8.0 # 8.0 wt% FeO ~ Earth's modern mantle
[outgas]
module = "calliope"
fO2_shift_IW = 4.0 # [log10] initial-guess HINT, not buffer
[outgas.calliope]
include_H2O = true
include_CO2 = true
include_N2 = true
include_S2 = true
include_H2 = true
include_CH4 = true
include_CO = true
include_SO2 = true
include_H2S = true
include_NH3 = true
solubility = true
What the wrapper does on the first call: builds the five-element target \((m_\mathrm{H}, m_\mathrm{C}, m_\mathrm{N}, m_\mathrm{S}, m_\mathrm{O})\), with \(m_\mathrm{O}\) derived from O_mode = "FeO_mantle_wt_pct" and O_budget = 8.0 via \(m_\mathrm{O} = M_\mathrm{O}/M_\mathrm{FeO} \times \mathrm{wt\%}/100 \times M_\mathrm{mantle} \approx 0.2227 \times 0.08 \times M_\mathrm{mantle}\); calls equilibrium_atmosphere_authoritative_O with fO2_hint = 4.0; reads back the four primary partial pressures, the seven secondary species, AND fO2_shift_derived (the redox state implied by the supplied O budget), writes them all into hf_row. The derived \(\Delta\mathrm{IW}\) appears in hf_row['fO2_shift_IW_derived']. Subsequent iterations carry the same authoritative O total (modulated by PROTEUS escape bookkeeping) and the redox state can drift across the trajectory.
Choosing between the two modes
Use user_constant when |
Use from_O_budget when |
|---|---|
| You want a fixed redox state for a parameter sweep (Nicholls et al. 2024 3 explored seven \(\Delta\mathrm{IW}\) values this way) | You want whole-planet O accounting where escape, ingassing, and the mantle FeO inventory all debit the same O reservoir |
| You don't have an independent constraint on the planet's O budget | You have an O constraint from an FeO-content estimate, a chondritic O/Si ratio, or an observational retrieval |
| Buffered chemistry is good enough for your scientific question | The mantle redox state is itself the unknown you're trying to infer |
The two modes give bit-identical results in the cases where they should: for any \(\Delta\mathrm{IW}\) accepted by user_constant, the chemistry returns an O budget; feeding that O budget back through from_O_budget recovers the same \(\Delta\mathrm{IW}\) to within ~0.05 dex (this is the round-trip property pinned by tests/test_authoritative_O.py::TestRoundTrip and documented on the authoritative-oxygen page).
Redox state
fO2_shift_IW is in \(\log_{10}\) units relative to the O'Neill & Eggins (2002) 4 IW buffer. Under fO2_source = "user_constant" this value is the buffer offset; under fO2_source = "from_O_budget" it is only the initial-guess seed. Common reference values:
| \(\Delta\mathrm{IW}\) | Description |
|---|---|
| \(-5\) | Highly reduced (Mercury-like; sulphur-derived estimate IW-5.4, Cartier & Wood 2019 1) |
| \(-3\) | Reduced (e.g. enstatite-chondrite-like; Mercury Fe-based estimate IW-2.8 to IW-4.5, Cartier & Wood 2019 1) |
| \(-1\) | Moderately reduced; near the Mars-mantle source range (Wadhwa 2001 6 places the shergottite-source mantle at \(\approx\) IW) |
| \(0\) | At iron-wΓΌstite buffer (core formation equilibrium at depth) |
| \(+3.5\) | Sossi et al. (2020) 5 preferred Earth's mantle \(f_{\mathrm{O}_2}\) |
| \(+4\) | CALLIOPE PROTEUS-side default; near-modern Earth upper mantle (within FMQ\(\,\pm\,2\) per Frost & McCammon 2008 2) |
Sossi et al. (2020) 5 place Earth's modern upper mantle at \(\Delta\mathrm{IW} \approx +3.5\); Frost & McCammon (2008) 2 report a broader FMQ\(\,\pm\,2\) range across mantle settings (approximately IW+1.5 to IW+5.5). CALLIOPE defaults sit at \(\Delta\mathrm{IW} = 4.0\), consistent with a modern terrestrial composition.
Solver tolerances
The PROTEUS wrapper hard-codes a few solver knobs that override the CALLIOPE library defaults:
| Wrapper override | Value | Reason |
|---|---|---|
nguess |
\(10^3\) | The PROTEUS warm-start almost always lands on the right basin in the first attempt; \(10^3\) Monte-Carlo restarts is plenty of margin without spending wall-time on degenerate cases. |
nsolve |
\(3 \times 10^3\) | Per-restart fsolve iteration cap. |
opt_solver |
False |
Skip the alternating-solver fallback; if fsolve cannot converge from a warm start, it almost always means an upstream bug rather than a basin-of-attraction problem. |
print_result |
False |
Suppress the per-call INFO log; the PROTEUS main loop already prints the partial pressures. |
The TOML field names map to equilibrium_atmosphere keyword arguments as follows:
| TOML field | Solver argument | Role |
|---|---|---|
solver_rtol |
rtol |
Relative tolerance on the elemental mass-balance residual. |
solver_atol |
xtol |
Step tolerance on the inner fsolve Powell-hybrid iteration. The name is historical; it does not set the absolute mass-balance tolerance. |
mass_thresh |
atol |
Absolute tolerance on mass balance, in kg. Also the per-element threshold below which a species' inventory is treated as zero. |
Warm-start behaviour
For Time > 1 yr, the wrapper builds p_guess from the previous-iteration H2O_bar, CO2_bar, N2_bar, S2_bar rows. For Time = 0 (first call) the wrapper passes p_guess = None and lets CALLIOPE's Monte-Carlo initial-guess generator find the basin. If an element's target inventory drops below mass_thresh, the corresponding p_guess is forced to zero so that the solver does not waste iterations chasing a vanishing species.
Common pitfalls
Missing required volatileat startup: one of H2O / CO2 / N2 / S2 is set toinclude = false. Fix: re-enable it (it can still be set to a tiny initial pressure if you want to suppress its mass).Could not find solution for volatile abundancesmid-run: CALLIOPE exhausted itsnguessMonte-Carlo restarts. Almost always caused by an upstream NaN or unphysical state inhf_row(e.g.T_magma < T_floorafter AGNI failed,M_mantle = 0after a structure-solve failure). Inspectproteus_00.logfor the iteration immediately before the CALLIOPE failure; do not bumpnguessblindly.- Atmosphere mass collapses to zero without escape accounting it: the
check_desiccationgate inproteus.outgas.wrapperwill refuse to mark the planet desiccated if the loss is unexplained by cumulative escape. This is by design: a real desiccation event must be supported by tracked atmospheric escape, so the gate firing means an upstream module silently zeroed the atmosphere and the failure is upstream of CALLIOPE. Hydrogen inventory must be > 0:H_budget = 0orH_modemistyped. CALLIOPE refuses to solve a zero-H system because the speciation is degenerate.- Solubility off but
Phi_global > 0: not an error, but the wrapper will silently forcePhi_global = 0when[outgas.calliope].solubility = false, so dissolved masses will all be zero regardless of the live melt fraction.
Next step
For what the wrapper actually does on each iteration (sequence diagram, mapping table, hf_row keys), read Coupling to PROTEUS (theory).
-
C. Cartier, B. J. Wood, The role of reducing conditions in building Mercury, Elements, 15(1), 39β45, 2019. SciX. ↩↩
-
D. J. Frost, C. A. McCammon, The redox state of Earth's mantle, Annual Review of Earth and Planetary Sciences, 36, 389β420, 2008. SciX. ↩↩
-
H. Nicholls, T. Lichtenberg, D. J. Bower, R. Pierrehumbert, Magma ocean evolution at arbitrary redox state, Journal of Geophysical Research: Planets, 129, e2024JE008576, 2024. SciX. ↩
-
H. St. C. O'Neill, S. M. Eggins, The effect of melt composition on trace element partitioning: an experimental investigation of the activity coefficients of FeO, NiO, CoO, MoO\(_2\) and MoO\(_3\) in silicate melts, Chemical Geology, 186, 151β181, 2002. SciX. ↩
-
P. A. Sossi, A. D. Burnham, J. Badro, A. Lanzirotti, M. Newville, H. St. C. O'Neill, Redox state of Earth's magma ocean and its Venus-like early atmosphere, Science Advances, 6, eabd1387, 2020. SciX. ↩↩
-
M. Wadhwa, Redox state of Mars' upper mantle and crust from Eu anomalies in shergottite pyroxenes, Science, 291, 1527β1530, 2001. SciX. ↩