Skip to content

Usage

This page shows the two canonical calling patterns for CALLIOPE: solving from an elemental inventory, and solving from a prescribed initial-pressure atmosphere. Both use the same equilibrium_atmosphere() entry point.

For an end-to-end runnable example with explanations, see the First run tutorial. For the schema of each input field, see Configuration.

Pattern 1 - solve from elemental inventory

This is the workflow PROTEUS uses. You specify the total mass of H, C, N, S in the planet, plus the magma ocean state, and CALLIOPE returns the equilibrium speciation.

from calliope.solve import equilibrium_atmosphere, get_target_from_params
from calliope.constants import volatile_species

# 1. Build ddict (planet + magma + inclusion flags)
ddict = {
    'M_mantle': 4.03e24,
    'gravity': 9.81,
    'radius': 6.371e6,
    'Phi_global': 1.0,
    'T_magma': 2500.0,
    'fO2_shift_IW': 0.0,
    # elemental-budget inputs for get_target_from_params
    'hydrogen_earth_oceans': 1.0,    # 1 Earth ocean of H
    'CH_ratio': 0.1,
    'nitrogen_ppmw': 2.0,            # primitive Earth mantle estimate
    'sulfur_ppmw': 200.0,
}
for sp in volatile_species:
    ddict[f'{sp}_included'] = 1
    ddict[f'{sp}_initial_bar'] = 0.0

# 2. Build target from elemental budget
target = get_target_from_params(ddict)

# 3. Solve
result = equilibrium_atmosphere(target, ddict, print_result=True)

# 4. Read out
print(f"Surface pressure  : {result['P_surf']:.2f} bar")
print(f"Atmosphere mass   : {result['M_atm']:.2e} kg")
print(f"H2O bar           : {result['H2O_bar']:.2f}")
print(f"H2O VMR           : {result['H2O_vmr']:.3f}")
print(f"CO2 bar           : {result['CO2_bar']:.2f}")
print(f"H2  VMR           : {result['H2_vmr']:.3f}")

The result dictionary contains, for every species s in volatile_species:

  • s_bar: surface partial pressure, bar
  • s_vmr: volume mixing ratio (== mole fraction)
  • s_kg_atm: atmospheric column mass, kg
  • s_kg_liquid: dissolved mass, kg
  • s_kg_solid: 0.0 (solid partitioning is handled by the caller, not CALLIOPE)
  • s_kg_total: sum of atmosphere + liquid

and per-element totals H_kg_atm, C_kg_atm, ..., S_res (mass-balance residual in kg).

Pattern 2 - solve from initial partial pressures

Use this when you know the initial composition of the atmosphere and want to back out the consistent total inventory plus the equilibrium update.

from calliope.solve import equilibrium_atmosphere, get_target_from_pressures
from calliope.constants import volatile_species

ddict = {
    'M_mantle': 4.03e24,
    'gravity': 9.81,
    'radius': 6.371e6,
    'Phi_global': 1.0,
    'T_magma': 2500.0,
    'fO2_shift_IW': 0.0,
}
for sp in volatile_species:
    ddict[f'{sp}_included'] = 1
    ddict[f'{sp}_initial_bar'] = 0.0

# Specify the initial atmosphere by its primary-species partial pressures
ddict['H2O_initial_bar'] = 220.0    # bar
ddict['CO2_initial_bar'] = 100.0
ddict['N2_initial_bar']  = 1.0
ddict['S2_initial_bar']  = 0.01

target = get_target_from_pressures(ddict)
result = equilibrium_atmosphere(target, ddict, print_result=True)

get_target_from_pressures() computes the implied total elemental masses by summing atmospheric column mass (Bower et al. (2019) Eq. 2) and dissolved mass (Henry's law) across every included species at the prescribed initial pressures.

Warm-starting from a previous solve

For repeated calls (e.g. inside a time-evolution loop), pass the previous solution as the initial guess to dramatically reduce the number of fsolve iterations:

p_guess = {
    'H2O': result['H2O_bar'],
    'CO2': result['CO2_bar'],
    'N2':  result['N2_bar'],
    'S2':  result['S2_bar'],
}
result_new = equilibrium_atmosphere(target_new, ddict_new, p_guess=p_guess, print_result=False)

This is exactly what the PROTEUS wrapper does. With a good warm start, convergence typically takes 1–3 fsolve iterations; without one, CALLIOPE may need 20+ Monte-Carlo restarts before it lands on a plausible basin.

Choosing solubility laws

To override the default solubility law for a species, instantiate the law explicitly and pass it through your own wrapper rather than calling equilibrium_atmosphere() directly. The defaults are:

Species Default class Default composition Source
H\(_2\)O SolubilityH2O peridotite Sossi et al. (2023)
CO\(_2\) SolubilityCO2 basalt_dixon Dixon et al. (1995)
CO SolubilityCO mafic_armstrong Armstrong et al. (2015)
CH\(_4\) SolubilityCH4 basalt_ardia Ardia et al. (2013)
N\(_2\) SolubilityN2 dasgupta (in dissolved_mass) Dasgupta et al. (2022)
S\(_2\) SolubilityS2 gaillard Gaillard et al. (2022)

Alternative compositions (e.g. SolubilityH2O('basalt_dixon'), SolubilityH2O('lunar_glass')) are documented in the API reference and discussed in Solubility laws.

Next step

For the science behind these laws and the equilibrium constants, head to Equilibrium chemistry and Solubility laws. For the PROTEUS-side TOML recipe, head to Coupling to PROTEUS.