proteus.outgas.trapping¶
Burial of dissolved volatiles in the freezing mantle, and their release on a remelt.
For how this part works, see Outgassing, rock vapour and volatile trapping.
trapping
¶
Solid-phase volatile trapping during mantle crystallisation.
As the mantle crystallises, each increment of solidified mass buries volatiles
by two routes: lattice incorporation into the crystals at the crystal/melt
partition coefficient D_Z, and burial of the interstitial melt that the
crystal pile fails to expel, at the trapped melt fraction F_tl. The two
combine into one effective partition coefficient
D_eff = (1 - F_tl) * D_Z + F_tl
so the mass a species loses from the melt over one step is
dm_trap = D_eff * C_Z * dM_RM
with C_Z the species mass fraction in the melt and dM_RM the mass
crystallised over the step. A species at D_Z = 0 still traps
F_tl * C_Z * dM_RM: interstitial melt is buried whatever the crystal
chemistry does, and it is how the noble gases dissolved in the melt are
buried. This is Sim, Hirschmann and Hier-Majumder (2024), JGR Planets 129,
e2024JE008346, Eq. 6.
C_Z is the concentration the chemistry solver last computed, which is one
value for the whole mantle: the dissolved mass it left in the helpfile row over
the melt M_mantle * Phi_global it dissolved that mass into. Trapping runs
before this step's solve, so dividing by the current, smaller melt instead
would overstate the concentration by Phi(t-1) / Phi(t).
Critical melt fraction
The disaggregation melt fraction phi_c is the interior solver's
rheological transition, interior_energetics.rfront_loc: the melt fraction at
which the solver switches the mantle from melt-like to solid-like rheology is
the model's own disaggregation point, and a separate value would let trapping
and the rheology disagree about where the crystal framework locks. It marks the
top of the freezing front and caps the no-drainage bound.
Trapped-melt fraction
outgas.trap_mode selects whether trapping runs.
none
No trapping. The bracket is not evaluated and no mass moves. This is the
default, so enabling trapping is an explicit choice.
front
F_tl is the melt a parcel still holds after crossing the freezing front
the interior solver resolves, from the drainage integral of
:mod:proteus.outgas.compaction, converted from a volume to a mass
fraction. A front the mesh cannot resolve takes the
no-drainage bound, the entry porosity capped at phi_c. A step on which
the interior state is unavailable buries at D_Z alone (F_tl = 0)
and says so.
Remelting
A step on which the global melt fraction rises remelts part of the solid
mantle and releases what trapping buried in it. The solid that remelts first is
the solid that froze last, so the step returns the mass buried while the melt
fraction fell through the interval it now rises back through, read from the
burial ledger of :mod:proteus.outgas.burial_ledger. Freezing and remelting
the same interval therefore cancel, and a mantle that remelts completely
returns everything trapped, whatever the melt has done in between. Trapped mass
the history does not account for, as in a run resumed from a helpfile written
before trapping, returns in proportion to the solid that remelted,
(Phi(t) - Phi(t-1)) / (1 - Phi(t-1)).
Reservoir bookkeeping
Trapped mass moves from {sp}_kg_liquid to {sp}_kg_solid, leaving
{sp}_kg_total unchanged, and is recorded in {sp}_kg_trapped. The same
mass is moved between the per-element reservoirs by stoichiometry, so the
per-element closure total == atm + liquid + solid + condensed holds after
the step. Trapping never touches {e}_kg_condensed, the element mass in
outgassing condensates such as graphite, which only atmodeller with
outgas.atmodeller.include_condensates fills; it is zero otherwise.
_kg_trapped is the part of _kg_solid that trapping owns. The chemistry
backend writes zero to _kg_solid on every solve, CALLIOPE, atmodeller and the
dummy backend alike, and atmodeller writes its condensates to _kg_condensed
instead. _kg_solid therefore holds only trapped mass, and condensed carbon is
never mistaken for trapped melt.
Trapped mass must be excluded anywhere the code assumes the whole-planet
inventory is reachable. :func:locked_solid_mass is the single definition of
that exclusion. :func:unreachable_mass adds the condensed mass held by
outgas.prevent_desaturation (:func:locked_condensed_mass), and the escape
step and the desiccation gate subtract that sum. :func:trapped_mass_withheld
hides the trapped mass from the chemistry for the duration of a solve;
desiccation keeps it with the rest of the solid mantle.
TrappingStep(mode, dm_rm, f_tl, melt_mass, trapped_kg=dict(), remelted=False, supply_capped=list(), branch=BRANCH_NONE, tau_d=float('nan'), tau_s=float('nan'), t_res=float('nan'), n_front=0, l_front=float('nan'), v_front=float('nan'), front_courant=float('nan'), w_matrix_over_vf=float('nan'), guard_reason='')
dataclass
¶
What one trapping step moved, and the guards it tripped.
Attributes:
| Name | Type | Description |
|---|---|---|
mode |
str
|
Value of |
dm_rm |
float
|
Mantle mass crystallised over the step [kg]; negative when it remelted. |
f_tl |
float
|
Trapped melt fraction applied, a mass fraction in [0, 1]. |
melt_mass |
float
|
Melt mass the dissolved volatiles were last partitioned into [kg]. |
trapped_kg |
dict
|
Mass buried per species [kg]; negative on a remelting step. |
remelted |
bool
|
Whether the step released trapped mass to the melt. |
supply_capped |
list
|
Species whose burial was capped at the mass the melt held. |
branch |
int
|
Regime code, one of the |
tau_d, tau_s, t_res |
float
|
Percolation time at the top of the front, matrix deformation time and residence time of a parcel in the front [yr]; NaN when not computed. |
n_front |
int
|
Number of nodes the front spans. |
l_front |
float
|
Front thickness [m]. |
v_front |
float
|
Front speed [m s-1]. |
front_courant |
float
|
Front advance over the step, in front thicknesses [1]. |
w_matrix_over_vf |
float
|
Matrix speed at the top of the front over the front speed [1]. |
guard_reason |
str
|
Why the front could not be integrated; empty when it could. |
total_trapped
property
¶
Net mass buried this step, summed over species [kg].
Negative on a remelting step, which releases trapped mass to the melt.
critical_melt_fraction(config)
¶
Disaggregation melt fraction phi_c [1], the solver's rheological transition.
The single definition of phi_c for the trapping step, so the top of the
drainage front and the no-drainage bound cannot drift apart from each other
or from the interior rheology.
Source code in src/proteus/outgas/trapping.py
343 344 345 346 347 348 349 350 | |
crystallised_mass_from_phi(m_mantle, phi_prev, phi_now)
¶
Mass crystallised over one step [kg], from the melt fraction.
dM_RM = M_mantle * [Phi(t-1) - Phi(t)]. Phi_global is a mass fraction
and therefore intensive, so a Zalmoxis re-solve that changes M_mantle
does not inject a spurious increment the way differencing
M_mantle_solid would. It also matches the melt reservoir CALLIOPE
dissolves into, which is sized from Phi_global rather than from
M_mantle_liquid; the prevent-warming clamp moves Phi_global without
resynchronising those masses, so the two disagree on clamped steps.
A negative increment means the mantle remelted that much mass, which releases trapped volatiles back to the melt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
m_mantle
|
float
|
Mantle mass [kg]. A non-positive or non-finite value gives 0. |
required |
phi_prev
|
float
|
Global melt fraction at the previous and the current step [1]. A non-finite value gives 0. |
required |
phi_now
|
float
|
Global melt fraction at the previous and the current step [1]. A non-finite value gives 0. |
required |
Source code in src/proteus/outgas/trapping.py
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | |
derived_total_elements(config)
¶
Elements whose whole-planet total the chemistry recomputes each step.
Oxygen under planet.fO2_source = 'user_constant' is the only case.
Holding the oxygen fugacity at a fixed buffer offset requires the chemistry
to move oxygen, so O_kg_total is an output of the solve rather than a
conserved budget the escape chain debits; outgas/common.py says as much
where it lists the _kg_total slot as escape-owned for every element
except O. Under 'from_O_budget' the wrapper restores the authoritative
budget after the copy, so oxygen is conserved state there and is absent
from this tuple.
Trapping still treats oxygen like every other element around a solve: the
trapped oxygen is taken out before it and added back to the total the
chemistry writes, so O_kg_total is the whole planet's oxygen under
either source. The per-element closure is not asserted for it, because the
chemistry total closes only to the solver's own convention.
Source code in src/proteus/outgas/trapping.py
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | |
effective_partition(f_tl, d_z)
¶
Effective crystal/melt partition coefficient including trapped melt.
Reduces to f_tl for a perfectly incompatible species (d_z = 0) and
to 1 for a species that does not fractionate (d_z = 1). f_tl may be
a scalar or a per-step array; the algebra is the same either way.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f_tl
|
float or ndarray
|
Trapped melt fraction, a mass fraction in [0, 1]. |
required |
d_z
|
float
|
Crystal/melt partition coefficient [1], non-negative. |
required |
Returns:
| Type | Description |
|---|---|
float or ndarray
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/proteus/outgas/trapping.py
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | |
escapable_inventory(hf_row, element, config=None)
¶
Whole-planet inventory of one element that escape can still reach [kg].
Source code in src/proteus/outgas/trapping.py
424 425 426 427 428 429 | |
locked_condensed_mass(hf_row, element, config=None)
¶
Mass of one element held in condensates that no longer re-vapourise [kg].
The {element}_kg_condensed reservoir with outgas.prevent_desaturation
on, and 0 otherwise (also without a config), since then each solve may
re-vapourise the condensate.
Source code in src/proteus/outgas/trapping.py
399 400 401 402 403 404 405 406 407 408 409 | |
locked_solid_mass(hf_row, element)
¶
Whole-planet mass of one element locked in the solid mantle [kg].
The single definition of what trapping has put beyond reach, read from
{element}_kg_trapped. Outgassing condensates such as atmodeller's graphite
are held in {element}_kg_condensed, not in _kg_solid; see
:func:locked_condensed_mass for when they are locked too.
Source code in src/proteus/outgas/trapping.py
388 389 390 391 392 393 394 395 396 | |
melt_concentration(kg_liquid, melt_mass)
¶
Species mass fraction in the melt [1]; zero wherever no melt remains.
A fully crystallised mantle carries no melt to draw from, so the quotient is replaced by zero instead of dividing by zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kg_liquid
|
float or ndarray
|
Mass of the species dissolved in the melt [kg]; negative values count as 0. |
required |
melt_mass
|
float or ndarray
|
Mass of the melt [kg]. |
required |
Source code in src/proteus/outgas/trapping.py
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
partition_coefficients(config)
¶
Crystal/melt partition coefficient per species, from config.
Water is the only species with appreciable lattice incorporation; the rest default to zero and are buried through the interstitial-melt term alone.
Returns:
| Type | Description |
|---|---|
dict
|
|
Source code in src/proteus/outgas/trapping.py
353 354 355 356 357 358 359 360 361 362 363 364 365 | |
released_mass(hf_row, hf_all, phi_prev, phi_now)
¶
Mass each trapped species and element returns to the melt on a remelt [kg].
The mass the burial ledger holds below phi_now, rebuilt from the
completed rows in hf_all. Trapped mass the ledger does not hold returns
in proportion to the solid that remelted; a complete remelt returns all of
it. Each release is capped at what is trapped.
Source code in src/proteus/outgas/trapping.py
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 | |
remelt_returned_mass(hf_row)
¶
Whether this step's trapping released buried mass back to the melt.
Source code in src/proteus/outgas/trapping.py
432 433 434 435 436 | |
remelted_fraction(phi_prev, phi_now)
¶
Share of the solid mantle that remelted over one step [1].
(Phi(t) - Phi(t-1)) / (1 - Phi(t-1)), clipped to [0, 1]: 1 when the
mantle is fully molten again, 0 when it froze further or did not change.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
phi_prev
|
float
|
Global melt fraction at the previous and the current step [1]. A non-finite value gives 0. |
required |
phi_now
|
float
|
Global melt fraction at the previous and the current step [1]. A non-finite value gives 0. |
required |
Source code in src/proteus/outgas/trapping.py
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | |
restore_trapped_mass(hf_row)
¶
Put the trapped mass back after a chemistry solve.
Adds it to whatever the backend wrote into _kg_solid, and restores each
element's whole-planet _kg_total. A species total is an output of the
solve, the backend's atmosphere plus melt, so for every trapped species it is
rebuilt as atm + liquid + solid, and the solid and total moles follow the
masses. Putting back the species total saved before the solve would instead
overwrite the partition the solve has just produced.
Source code in src/proteus/outgas/trapping.py
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 | |
run_trapping(config, hf_row, hf_all, interior_o=None, *, init_stage=False)
¶
Bury volatiles into the solid mantle over this crystallisation step.
Called once per iteration, after the interior has advanced and any structure
re-solve has finished, and before escape and outgassing read the
inventories. Returns None when trapping is inactive: during the
initialisation stage, whose interior steps do not advance the mantle,
when hf_all holds no earlier row or the time is not above 0, where
there is no previous state to difference, and whenever the mode is
none. The first step after the initialisation stage differences
against the last row of that stage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
Config
|
Model configuration. |
required |
hf_row
|
dict
|
Current helpfile row, modified in place. |
required |
hf_all
|
DataFrame or None
|
Completed rows. The last one supplies the previous melt fraction. |
required |
interior_o
|
Interior_t or None
|
Interior state. The front scheme reads its melt-fraction, density and pressure profiles to locate the freezing front. |
None
|
init_stage
|
bool
|
True during the initialisation stage, which traps nothing. |
False
|
Source code in src/proteus/outgas/trapping.py
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 | |
trapped_mass(d_z, f_tl, c_z, dm_rm, available_kg)
¶
Mass of one species buried over a crystallisation increment [kg].
[(1 - F_tl) D_Z + F_tl] C_Z dM_RM, Sim et al. (2024) Eq. 6, capped at
available_kg, the mass the melt holds. A non-positive increment buries
nothing. Returns the mass and whether the cap bound.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d_z
|
float
|
Crystal/melt partition coefficient [1], non-negative. |
required |
f_tl
|
float
|
Trapped melt fraction, a mass fraction in [0, 1]. |
required |
c_z
|
float
|
Mass fraction of the species in the melt [1]. |
required |
dm_rm
|
float
|
Mantle mass crystallised over the increment [kg]. |
required |
available_kg
|
float
|
Mass of the species the melt holds [kg]. |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
Buried mass [kg], at least 0, and whether the cap at |
Source code in src/proteus/outgas/trapping.py
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | |
trapped_mass_withheld(hf_row)
¶
Hide the trapped mass from the chemistry for the duration of a solve.
Withholds on entry and restores on exit, also when the solve raises, so the row never carries the reachable inventory as the whole planet's.
Source code in src/proteus/outgas/trapping.py
531 532 533 534 535 536 537 538 539 540 541 542 | |
trapping_active(config)
¶
Whether the run traps volatiles; with trap_mode = 'none' nothing
this module owns may change what the run does.
Source code in src/proteus/outgas/trapping.py
439 440 441 442 443 | |
unreachable_mass(hf_row, element, config=None)
¶
Whole-planet mass of one element beyond the reach of escape and outgassing [kg].
The mass trapped in the solid mantle, plus the condensed reservoir when condensates are kept from re-vapourising. The escape step subtracts this mass from the mass available to escape, and the desiccation gate subtracts it from the inventory it tests, so a planet whose atmosphere and melt have both emptied still registers as desiccated.
Source code in src/proteus/outgas/trapping.py
412 413 414 415 416 417 418 419 420 421 | |
withhold_trapped_mass(hf_row)
¶
Take the trapped mass out of the reservoirs a chemistry solve owns.
Every outgassing backend partitions a whole-planet inventory between melt and
atmosphere, and rewrites the _kg_solid columns, with zero for every backend;
atmodeller writes its condensates to _kg_condensed instead. Trapped mass
belongs to neither. Left in an element total, it would be re-dissolved by the
solve; left in a solid column, it would be overwritten by the backend. It is
therefore subtracted from each element's
_kg_total and from each species' and element's _kg_solid, which leaves
the backend exactly the share it owns.
Pair with :func:restore_trapped_mass, or use :func:trapped_mass_withheld,
which pairs the two. An element whose total the chemistry recomputes, such
as oxygen under a buffered fO2, is treated the same way: the solve writes its
own total over the withheld one and the restore adds the trapped share to
it, and a solve that writes nothing leaves the total where it started.
Source code in src/proteus/outgas/trapping.py
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 | |