Skip to content

Track outgassing condensates in the helpfile and plot their saturation - #1037

Open
nichollsh wants to merge 17 commits into
mainfrom
atmodeller-graphite-tracking
Open

nichollsh wants to merge 17 commits into
mainfrom
atmodeller-graphite-tracking

Conversation

@nichollsh

@nichollsh nichollsh commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Description

Tracks the saturated (condensed) phases from the atmodeller outgassing chemistry in the helpfile, keeps them in their own reservoir rather than the solid mantle, plots them, and adds an option to stop condensates from re-vapourising. Closes #1036.

Condensates in the chemistry

  • The atmodeller wrapper adds each supported condensate (graphite, sulfur, water, sulfuric acid; condensate_list in utils/constants.py) when its elements have budgets and T_magma lies inside its thermodynamic data range.

Helpfile columns

  • {X}_cd_activity (1 = saturated) and {X}_cd_kg for each condensate, written on every solve, zero when it is not solved for. Registered as diagnostics, so older helpfiles still resume.
  • {e}_kg_condensed for H, O, C, N, S: the element mass held in condensates. The outgassing wrapper zeroes them before every solve; only atmodeller with include_condensates fills them.

New key - outgas.prevent_desaturation (default false)

  • false: each solve re-equilibrates the whole inventory, so a condensate can re-vapourise.
  • true: the mass already in each condensed reservoir is withheld from the solve and added back on top of what the solve newly condenses, together with {X}_cd_kg, so a condensed layer only grows.

New plot - plot_saturated

  • Activity of each condensate; condensed column density; estimated layer thickness from condensate_density; T_magma and melt fraction.

Other

  • Config and output references regenerated; prevent_desaturation added to input/all_options.toml and classified neutral for the v2 to v3 migration.
  • Golden trajectory re-recorded: only the 13 new columns are added (constant 0, since the golden run uses the dummy outgassing backend); all 926 existing columns reproduce unchanged over the 56 rows.
  • Reduced log spam from SPIDER and Atmodeller wrappers on INFO level.

Validation of changes

Linux (Fedora 44, kernel 7.2), Python 3.12, pandas 3.1.0, atmodeller 1.0.2 (local checkout at 18eabab), AGNI 1.13.2 on Julia 1.13.1.

  • Closure with real solves. A real atmodeller solve (Mercury-like, IW-4, C/H = 10, 2000 K) puts graphite in C_kg_condensed (7.57e20 kg, equal to C_cd_kg) with C_kg_solid = 0, and atm + liquid + solid + condensed equals C_kg_total within 5e-6, the solver tolerance.
  • prevent_desaturation end to end. Two real atmodeller steps at IW-5 with 1000 ppmw C, 1800 K then 2400 K: with false the 4.29e19 kg layer from 1800 K re-vapourises at 2400 K (activity 0.30); with true it stays at 4.29e19 kg (activity of the remaining inventory 0.12). Total carbon is conserved and the carbon reservoirs close within 3e-5 in both.
  • Coupled runs. Mercury-like grids (fO2 from IW-7 to IW-3 against the carbon budget; dummy, SPIDER and Aragog/Zalmoxis interiors) wrote the new columns and plot_saturated on every step, with graphite saturation matching fixed-temperature atmodeller sweeps through the same wrapper.
  • Tests. New unit tests cover condensate gating by elements and data range, empty-budget solves under both fO2 sources, the condensed columns staying zero without condensates, desiccation keeping graphite, prevent_desaturation (withheld from the solve, only growing, restored when the solve raises, invisible to escape and to the desiccation gate), and plot_saturated (column and thickness against mass, area and density, floor masking, held layers, skip cases, entry point). Changed lines in src/ are fully covered in a local coverage run of the affected suites.
  • Local suites. The affected unit suites pass, the golden run passes, ruff check and format pass, and the config and output reference checks pass. Failures on this machine are the environment-dependent ones (installer, doctor, docs-figure tooling) that also fail on main here; the branch adds none. The test-quality check reports the same 80 violations as main.
  • CI. All 16 checks passed on ca634be; CI is re-running on c90b5e2, which adds prevent_desaturation.

Checklist

  • I have followed the contributing guidelines
  • My code follows the style guidelines of this project (ruff check and format via pre-commit)
  • I have performed a self-review of my code
  • My changes generate no new warnings or errors
  • I have checked that the tests still pass on my computer (affected suites pass; environment-dependent failures shared with main remain)
  • I have updated the docs, as appropriate (generated config and output references, all_options.toml)
  • I have added tests for these changes, as appropriate
  • I have checked that all dependencies have been updated, as required (no dependency changes)

The atmodeller outgassing module now adds each supported condensate
(graphite, sulfur, water, sulfuric acid) when its elements have budgets
and T_magma lies inside its thermodynamic data range, instead of graphite
alone. A condensate that cannot be built is logged as a warning rather
than dropped silently.

Each solve writes {X}_cd_activity and {X}_cd_kg to the helpfile (zero
when the condensate was not solved for) and logs them. The columns are
registered as diagnostics, so helpfiles written before them still resume.
The new plot_saturated shows each condensate's activity, its condensed
mass while saturated, and the magma temperature and melt fraction.

The golden trajectory reference is not yet re-recorded for the eight new
columns, so test_the_committed_reference_covers_every_column_the_helpfile_writes
fails until it is recorded again with --record-golden.
The condensed mass panel now shows each saturated condensate per unit
surface area (kg m-2, over 4 pi R_int^2), and a new panel estimates the
thickness of a uniform layer of it from condensate_density. The densities
are approximate ambient values with no temperature, pressure or porosity
dependence, so the thickness is an estimate only.
atmodeller's condensed mass per element (e.g. graphite carbon) was added
to {e}_kg_solid, a column described as mass in the solid mantle and also
shared with volatile trapping. It now goes to a separate {e}_kg_condensed
reservoir for each volatile element.

The per-element closure check sums atm + liquid + solid + condensed,
desiccation keeps the condensed reservoir with the mantle ones so graphite
is not dropped from the budget when the atmosphere goes, and helpfiles
written before the column existed read it as zero on resume.
@nichollsh nichollsh added Enhancement A new feature or request Outgassing Physics - outgassing and vapourisation Software Relating to software and implementation labels Oct 22, 2026
The schema parser and the static helpfile scanner now know
condensate_list, so the new {X}_cd_activity, {X}_cd_kg and
{e}_kg_condensed columns are parsed and attributed to the atmodeller
module. The wrapper and plot_saturated build those keys directly from
the loop variable so the scan can resolve them, the desiccation write to
{e}_kg_condensed is declared as a volatile-element template, and the
output reference page and JSON sidecar are regenerated.

The logged-activity test compares floats with pytest.approx.
…re on

The outgassing wrapper sets every {e}_kg_condensed to 0 before each chemistry solve, and atmodeller fills them only when include_condensates is on. The columns are always in the helpfile, and a backend or run without condensates now reports 0 by construction rather than by leaving them untouched.
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.65%. Comparing base (8849aea) to head (1248492).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1037      +/-   ##
==========================================
+ Coverage   94.44%   94.65%   +0.21%     
==========================================
  Files         139      140       +1     
  Lines       23569    23717     +148     
  Branches     3944     3968      +24     
==========================================
+ Hits        22259    22450     +191     
+ Misses        882      874       -8     
+ Partials      428      393      -35     
Flag Coverage Δ
unit-tests 93.45% <100.00%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

nichollsh and others added 7 commits October 22, 2026 14:04
The 13 new columns ({X}_cd_activity, {X}_cd_kg and {e}_kg_condensed) are added to golden_run.tsv. The run uses the dummy outgassing backend, so each is constant at 0; all 926 existing columns reproduce unchanged over the 56 rows.
…s parser

From pandas 3.1.0 the default read_csv parser reads these values exactly, so the assert that it misses a last bit no longer holds. The test now shows that some of the values do not survive 16 significant digits, which holds whatever pandas does, and keeps the exact round-trip assert.
The check that over a fifth of the values do not survive 16 significant digits sits with the 11-digit check, before the write, as a count; the docstring says a quarter of the values need all 17 digits.
plot_saturated: the plotted column is the condensed mass over 4 pi R_int^2 and the thickness that over the condensate density, the solver floor below saturation plots as zero, the plot is skipped without solved condensates, and the entry point reads the helpfile. atmodeller: a condensate joins only with its elements budgeted and T_magma inside its data range, and a skipped solve resets every condensate column to zero. Desiccation keeps graphite in C_kg_condensed alongside the solid mantle, scaled by the same share when escape took part of the held mass.
…racking

# Conflicts:
#	src/proteus/outgas/atmodeller.py
@nichollsh
nichollsh marked this pull request as ready for review October 23, 2026 07:32
@nichollsh
nichollsh requested a review from a team as a code owner October 23, 2026 07:32
With the flag off (the default) each chemistry solve re-equilibrates the whole
inventory, so a condensate such as graphite can re-vapourise as conditions
change. With it on, the mass already held in each {e}_kg_condensed reservoir
is withheld from the solve and added back on top of what the solve newly
condenses, together with each condensate's {X}_cd_kg, so a condensed layer can
only grow. {X}_cd_activity stays the solver's value for the inventory left in
the solve.

The held mass is also beyond reach elsewhere: unreachable_mass (trapped plus
held condensed) replaces locked_solid_mass in the escape reservoir, the escape
floor and the desiccation gate, so escape cannot strip a held layer and a
planet left with only a held crust registers as desiccated. The config is
passed through escapable_mass, limit_escape_step, calc_new_elements and
escapable_inventory as an optional argument; without it only trapped mass is
excluded, as before.

plot_saturated now draws a condensed mass above 1 kg at any activity, so a
held layer stays visible after the remaining inventory falls below
saturation. The config and output references are regenerated, the field is
documented in all_options.toml, and the v2 to v3 migration lists it as
neutral.
):
raise RuntimeError('solve failed')

assert hf_row['C_kg_total'] == pytest.approx(1.0e20, rel=1e-12)
The per-solve atmodeller summaries (species and active elements, solve
inputs, total pressure, surface pressure and MMW) and SPIDER's choice of
P-S phase boundaries now log at DEBUG, so a coupled run's INFO log carries
one atmodeller line per condensate on each step. That line reads
"Condensate C: activity=0.250, condensed mass=3.00e-06 kg"; a missing or
non-finite value prints as n/a rather than failing the float format.

Tests check the condensate line and the DEBUG level of the summaries, the
n/a line for an unavailable activity and mass, and that a run's derived P-S
curves are passed to SPIDER in place of the melting_dir pair, at DEBUG.
input/planets/mercury.toml: Mercury mass, orbit and core radius fraction,
SPIDER structure and energetics with the Wolf & Bower (2018) EOS and
melting curves, atmodeller outgassing with condensates and
prevent_desaturation, solar noble gases, AGNI and ZEPHYRUS. It is the
reference config for fO2 x C grids, which set outgas.fO2_shift_IW and
planet.elements.C_budget per case.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement A new feature or request Outgassing Physics - outgassing and vapourisation Software Relating to software and implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Saturated phases from outgassing are not tracked in the helpfile

2 participants