Skip to content

Add an escape regime dispatcher with per-species fractionation - #34

Draft
maraattia wants to merge 92 commits into
mainfrom
ma/escape-regime-dispatcher
Draft

Add an escape regime dispatcher with per-species fractionation#34
maraattia wants to merge 92 commits into
mainfrom
ma/escape-regime-dispatcher

Conversation

@maraattia

@maraattia maraattia commented Aug 20, 2026

Copy link
Copy Markdown

Description

This PR adds an escape regime dispatcher to ZEPHYRUS. One call, zephyrus.dispatch, takes a planetary state (scalars plus an atmosphere profile) and returns one regime label, one bulk mass-loss rate, per-species rates that sum to it, flags, and a boxed diagnostics container. Every physically posed input returns one and only one labeled result; exceptions are reserved for malformed input.

Every state receives one of five regime labels (boiloff, hydrodynamic:EL, hydrodynamic:RR, hydrostatic, roche_overflow), decided in a fixed evaluation order:

  1. Bolometrically driven boil-off (Owen & Wu 2016 transonic Parker wind in exact Lambert-W form, Bondi-capped per Gupta & Schlichting 2020), activated by the restricted Jeans parameter. Past the activation gate the same machinery survives as a residual capped by the interior luminosity (Gupta & Schlichting 2019), with that cap dividing by the same tidally reduced barrier the energy-limited rate divides by, so the two candidates that compete for the label measure one barrier between them.
  2. Hydrodynamic escape as min(EL, RR): the energy-limited rate in the Erkaev et al. (2007) form and the radiation-recombination-limited chain of Murray-Clay et al. (2009), with the wind temperature set by a thermostat balancing photoionization heating against four radiative cooling channels (atomic lines on the Nakayama et al. 2022 data, the CO2 15 micron band and O fine structure of Johnstone et al. 2018, and recombination cooling with the Badnell 2006 fit). The min() winner names the sub-label. The selection diagnostic reports which candidate won and whether the sonic radius had to be floored, and deliberately does not name a mechanism: what separates genuine recombination saturation from a wind throttled between base and sonic point is the barometric factor, reported beside the rate.
  3. A sonic-point Knudsen switch (Chatterjee & Pierrehumbert 2026 construction; Laricchiuta et al. 2009 collision integrals with provenance-classed fallbacks) decides whether that wind is collisional enough to exist; otherwise the point re-routes to per-species hydrostatic escape: Jeans effusion with the Volkov et al. (2011) kinetic enhancement, capped by the diffusion-limited supply and combined by the Yelle (2024) harmonic mean, all evaluated on a Bates profile upper structure rather than photospheric values.
  4. A per-branch Roche screen tests the active flow radius against the periapsis Hill radius. An overflowing point is renamed roche_overflow and keeps the rate and the split its own branch produced, because that boundary is a comparison between rate candidates and the two sides of it hold the same branch; substituting another branch's formula would make the dispatched rate jump at a line the physics puts nowhere in particular. The label therefore asserts a geometry, and the rate beside it is a bound-flow estimate, a lower limit on what tides would do. A subflag separates an atmosphere whose own extent reaches the lobe from one where only the would-be sonic surface would sit outside it (Owen & Jackson 2012). A hysteresis window on the switch threshold is available for evolutionary use so tracks cannot chatter between branches.

Confirmed hydrodynamic outflows are partitioned over species by a simultaneous N-species fractionation closure (the constant-composition closure of the Zahnle et al. 1990 multispecies wind system with active-set dropout, generalizing Hunten et al. 1987), on a binary-diffusion-coefficient library in which every pair carries a provenance class. The split follows the branch that produced the rate rather than the label, so a wind that reaches the Hill sphere keeps its fractionation.

New modules (flat layout): composition, profiles, knudsen, diffusion, atomic_data, thermostat, boiloff, hydrodynamic, hydrostatic, fractionation, diagnostics, dispatcher. The energy-limited physics now lives in hydrodynamic.py beside the other rate prescriptions, and escape.py is a thin compatibility re-export, so from zephyrus.escape import EL_escape and the PROTEUS version pin keep working unchanged; the released function's behavior is intact and additive apart from the tidal convention fix below. The dispatcher is a standalone API until the PROTEUS-side wiring lands in a follow-up PR (config schema, wrapper dispatch, profile transport, per-element reservoir debits).

EL_escape itself receives three documentation and convention fixes: the R_XUV docstring now names which level the 20 mbar value is and distinguishes it from the nanobar level where the wind is launched (Lopez 2017); the tidal factor's xi is now measured from the radius the scaling argument selects (R_p for the Erkaev form, restoring that paper's own convention; R_XUV for the single-radius form), which changes standalone tidal_contribution=True, scaling=2 results; and the efficiency docstring carries the Caldiroli et al. (2022) threshold-potential caveat. constants.py moves to the exact SI defined values and gains the proton mass, atomic mass constant, Planck constant, and eV conversion; planets_parameters.py gains the IAU nominal Jupiter values.

The documentation is restructured around this model: a refreshed landing page, a hub page presenting the two loss channels (continuous escape and giant impacts) and the five regimes with a simplified decision flowchart, and topic pages covering the energy-limited default, the regime framework, fractionation (citing Attia & Lichtenberg 2026, in prep.), and the impact law, each written with every quantity defined and a bibliography. Alongside them, for the dispatcher specifically: a tutorial that crosses the boundaries on a worked planet, a reference page documenting every flag and diagnostics group the result carries, and a troubleshooting guide keyed to the symptoms a first user hits. Every printed number in those pages is the verbatim output of a snippet the reader can run, the two figures come from examples/demo_dispatcher/, which is itself covered by tests, and the mermaid flowcharts now render on the built site instead of arriving as diagram source.

Three boundaries are worth calling out for reviewers. Every regime criterion carries a stated physical band (the Knudsen threshold band 0.1 to 3, the boil-off activation band 15 to 35), and the diagnostics container prints the counterfactual labels at the band edges beside every verdict; the container is reporting only and nothing in the control flow reads it. Two known limitations are flagged on every affected result rather than hidden: hydrostatic heavy-element rates are lower limits (nonthermal channels are absent), and grid points where the neutral and plasma escape-temperature conventions disagree are marked contested with both branch rates recorded. And because a strongly bound heavy atmosphere returns rates dozens of decades below anything physical, every verdict reports whether it clears one proton per Julian year, which is the smallest rate with content; the module never applies that floor, since what counts as negligible belongs to the caller.

Validation of changes

  • 177 unit and smoke tests pass locally (Linux, Python 3.13) and in CI, coverage 98.1% against the 90% fast gate, the structure and test-quality gates at zero violations, ruff check clean over src/, tests/, and tools/, and the documentation site builds without issues. (ruff check over the whole tree reports two pre-existing findings in examples/demo_earth/, untouched by this PR and not run by CI.)
  • Every new physics source carries reference-pinned tests; the anchors are inventoried per source under docs/Validation/. Highlights: the Erkaev et al. (2007) Table 1 enhancement factors within 1%, on both consumers of that factor; the Caldiroli et al. (2022) efficiency fit at six spot points within 5% and a clean rejection of its complex-valued region; the Murray-Clay et al. (2009) fiducial hot Jupiter (base Jeans parameter, sonic radius, and printed sonic-point Knudsen numbers); the Laricchiuta et al. (2009) collision integrals reproducing four measured viscosities within 7%; the Yelle (2024) Figure 1 Mars hydrogen flux through the Jeans-to-diffusion transition; the Volkov et al. (2011) coefficient table; the Owen & Wu (2016) sonic-point limit and shutoff; the Salz et al. (2016) wind-versus-thermosphere thresholds, reported with their hydrogen-dominated scope rather than applied; and the Hunten et al. (1987) Earth, Mars, and Venus anchors.
  • The fractionation closure is verified against every special case of the lineage it generalizes: exact reductions to Hunten et al. (1987) / Cherubim & Wordsworth (2024), Gu & Chen (2023), Odert et al. (2018), Zahnle et al. (1990) Eqs. 35, 36, and 42, Zahnle & Kasting (2023) Eqs. 19 and 20, and Chassefiere (1996), plus randomized ensembles for conservation, non-negativity, monotonicity, threshold continuity, active-set uniqueness by brute-force enumeration, and fixed-point stability.
  • The diffusion-limited supply integrals are refined rather than trusted. They are first order in the log-pressure step (a refinement triple gives a Richardson ratio of 2.012), so the previous fixed 400-level grid sat 3.8% above the extrapolated limit with nothing to say so. The resolution now doubles until the relative change in the bulk rate falls below a target, and the levels reached, the last change, the worst change across the species, and whether the target was met travel with the rate; a ceiling reached first is reported, not raised. Cases where the supply cap does not bind converge on the first refinement and are unchanged.
  • Coefficient and criterion provenance is ordered and labelled rather than assumed. The binary-diffusion ladder takes a measured row before an estimated one (an Eq. (10) scaling had preempted eleven measured rows and read up to 26% below them); six elements with no published van der Waals radius carry a provenance class saying their hard-sphere cross section rests on an assumed one; the CO2 band reads its detailed-balance exponential from the quantum in kelvin rather than the wavenumber the source prints as a temperature, and caps its escape probability at the non-LTE ceiling the fit would otherwise diverge through.
  • Two reported quantities now say what they are. A clamped wind temperature is the edge of the thermostat bracket and not a root, which the balance does have near 1e5 K, outside the model; and the interior-luminosity cap flags itself when it is the term setting the bolometric rate, since it switches on at the activation gate and a state crossing that threshold falls by a factor of thousands with its label unchanged. The collisionality switch states the one-sided cost of its neutral cross sections, in the source's own terms.
  • A prescribed exobase temperature below the profile's top level is floored at the top and flagged, rather than building an extension that cools with height and an exobase more strongly bound than its own anchor.
  • Coverage of the species a coupled run can supply is pinned directly: every one of the 37 members of the PROTEUS gas list, all 18 elements, and seven chemistry products outside both lists resolve to a finite binary diffusion coefficient with a named provenance, against four different backgrounds. Four species of that list carry no kinetic diameter in any compilation and reach their coefficient by a recorded substitution.
  • A totality fuzz test dispatches 200 random physically posed inputs across compositions, masses, radii, fluxes (including zero), eccentricities, and profile depths, asserting one known label, finite non-negative rates, and per-species sums equal to the bulk rate.
  • Every relative tolerance also fixes the absolute tolerance at zero. pytest.approx keeps its default abs=1e-12 alongside any rel=, and takes the larger, so a pin on a cross section, a recombination coefficient, or a physical constant accepted any value at all: inflating the proton mass, atomic mass constant, and Planck constant by a tenth, or a collision integral by 30%, left the suite green. Both are now caught, and the boxedness check runs on one state per branch at a rate of order unity or above rather than one near the denormal floor.
  • Behavioral invariants of the dispatch itself are tested, not only the individual rate laws: the Roche screen renames a state without changing its rate, checked by bisecting the boundary in orbital distance and asserting continuity across it, with the rate a recomputing screen would have returned as the discrimination guard; the diagnostics container is boxed, checked by replacing every producer with a stub returning garbage and asserting the verdict, the rate, and the split are unchanged.
  • A cross-implementation pin ties the dispatcher's EL candidate to the released EL_escape at 1e-9 relative, and the worked example is driven end to end by its own test module.

Checklist

  • I have followed the contributing guidelines
  • My code follows the style guidelines of this project
  • 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
  • I have updated the docs, as appropriate
  • I have added tests for these changes, as appropriate
  • I have checked that all dependencies have been updated, as required (scipy joins the runtime dependencies)

Relevant people

@timlichtenberg @nichollsh @EmmaPostolec

…assertions

Pin the tidally corrected critical exobase temperature away from the
x = 1 point where a dropped 1/x factor is invisible, pin the nitrogen
recombination coefficient with a swapped-coefficient guard, pin both
Tang timescales and both verdict directions, prove the diagnostics
container is boxed by sabotaging its producers instead of re-running,
pin the scaled kinetic diameters in the assembled table, and assert the
shallow-column CO2 trapping branch. The Volkov flat-factor test now
exercises the source function beside the published-coefficient oracle.
Document the wind-temperature sensitivity of the EL/RR crossover and
the dense-base clamp behavior of the thermostat in their modules.
@maraattia maraattia added enhancement New feature or request Priority 1: critical Priority level 1: highest priority – critical & fast import Interra labels Aug 20, 2026
@maraattia maraattia moved this to In Progress in PROTEUS Development Roadmap Aug 20, 2026
The physical base pressure the base method targets is a property of the
level, not a warning, so it moves onto the level dict as p_physical and
into the dispatch diagnostics beside the level it describes. The flags a
dispatch returns are now warnings only: a call with nothing to report
returns an empty flags dict instead of one that always carried a value.
Runs the framework over synthetic atmospheres: one verdict read field by
field, flux sweeps that cross two boundaries on two compositions, the
boil-off and overflow labels, the diagnostics container, the four knobs
that move a boundary, the hysteresis window, and one frozen atmosphere
dispatched along a stellar XUV history. Every step is a function that
returns its results, so the pieces can be imported one at a time, and the
figure follows the framework figure conventions with the escape domain
color and one marker per regime.
Three pages, one per purpose. The tutorial drives the framework on
synthetic atmospheres: one verdict read field by field, two boundary
crossings on one planet with the criterion band measured around one of
them, the boil-off and overflow labels, the diagnostics container in the
order the questions occur, the four knobs that move a boundary, and one
atmosphere dispatched along a stellar history. The results reference
tabulates every field, flag, and diagnostics group, with the two reporting
conventions a caller needs: a rate floor for numerical noise and the
depletion timescale for relevance. The triage guide goes from a symptom to
the entry that explains it.
The example tests exercise the shipped script's own entry points: the
regime sequence of the documented flux sweep, the per-species closure on
both the fractionating and the hydrostatic path, the zero-flux limit, the
error contract on a malformed state, the bisection that produces the
boundary fluxes the tutorial quotes, and the label change along a stellar
history with the stellar lookup mocked. The dispatcher test now pins the
unclamped base pressure the diagnostics report, and both rule files record
the naming exception this file takes.
The new test carried its own tier marker beside the module-level one, which
the structure validator rejects: exactly one tier per test, and the module
already declares smoke.
The tutorial's opening snippet imports the worked example by its path from
the repository root, which is what a reader with a clone would type, and that
resolves only with the root on sys.path. Whether it is there depends on how
pytest was started: running it as a module puts the working directory on the
path and the console script does not, so the test passed locally and failed
in CI. It now prepends the root itself.
The snippet test executed the whole page, and the page closes on a track
dispatched along a real MORS stellar history, which reads the Spada grid
under FWL_DATA. The unit and smoke tier does not download that grid, so the
test could not pass on a fresh runner however it was invoked. That block now
runs in the integration tier beside the other test that uses the real tracks,
with the same character-for-character comparison, and the fast tier covers
the other eighteen output blocks. The block is excluded by name rather than
by whether the data happens to be present, so both tiers check the same thing
on every machine, and the fast tier is verified against an empty data root.
Twelve accuracy corrections, each checked against the code or the arithmetic.
The wind-base clamp does not depend on a single profile top: the Lopez level
is tens of nanobars on an Earth-mass carbon dioxide planet and below a
nanobar on a low-gravity hydrogen envelope, where a profile stopping at a
nanobar misses it, so both pages that promised one number now say to read the
flag. The tidal setting divides the interior-luminosity cap as well as the
energy-limited rate. The diagnostics API page was missing the function behind
a documented group. The floor illustration said 66 grams a year where two
nanograms a second gives 63. The README still described the tidal domain from
before the reference radius followed the scaling. The bolometric instellation
is carried and validated but no branch reads it, which the parameter table now
discloses. The hysteresis flag says the window was available, not that it
changed anything. The self-consistency span is 65 to 767 Myr, not 40 to 800.
The boil-off label does not test whether the atmosphere is inflated past its
sonic radius. And the evaluation order listed the Roche screen before the
bolometric residual in both the page and the module comments, while the code
runs the residual first, which matters because the radius the screen tests
belongs to whichever branch actually won.

The hydrostatic section also called an effusion velocity a flux, used an
undefined flux symbol in the harmonic mean, and left the base density and its
mean-mass convention unstated; all four are now written out.
The accent is restored on Chassefière, in the module, its tests, and two
pages. The Caldiroli threshold potential is quoted on the untidal binding
energy, which is the convention the screen uses and the one Salz share; the
tidal factor enters the rate and not the potential the threshold is measured
against. The interior-luminosity cap is their Eq. 8, the update equation that
carries the minimum against the cooling luminosity, not Eq. 9, which is the
period distribution of their population model. The upper edge of the Knudsen
band is not Johnson's number: their simulations give 0.1 and 1, and the 3
comes from Chatterjee and Pierrehumbert arguing the energy limit may survive
that far, so the band is asymmetric in what supports it and the page now says
which end rests on what. And the activation parameter is written with the
composition mean molecular mass where Fossati write the atomic hydrogen mass,
a factor mu over m_H between the two, which is now stated where the equation
is given rather than left for a reader to discover by comparing numbers.
The metaphor family was the project's own coinage for the coefficient
fallback order, not a code identifier and not the field's standard name, so
the prose rules replace it with what it describes: 33 occurrences across four
modules, three test files, and three pages. Two test names carried it and are
renamed with it.

The same pass takes the comma after e.g. and i.e. in three shipped
docstrings, settles the prefix hyphenation on the unhyphenated form for
nonthermal and deexcitation, which the branch had split down the middle,
writes numbers above ten as numerals in three places, and drops a borrowed
noun from a dispatcher docstring. The worked example said it writes one
figure and writes two, as a PDF and a PNG each. The energy-limited API entry
was rendered on two pages and is now rendered once, on the released import
path, with a pointer from the module page. And the validation page for that
function pointed at a docstring and an overview this branch moved.
Three corrections of the same kind as the documentation pass. The figures
named Spline Sans Mono unconditionally while the colours that come with it
sat behind an optional import, so without proteus-mpl matplotlib substituted
a face with a warning and a reader's figures differed from the committed ones
with nothing saying why; the family now falls back to a generic monospace
where the package is absent. The module notes on the recombination chain
still said the flux scaling separates genuine saturation from barometric
suppression, which the barometric factor does and the flux exponent does not.
And the Jupiter constants had been inserted into the middle of the Earth
block, leaving six Earth quantities under the Jupiter heading.
The tidally driven transfer through L1 from Jackson et al. (2017),
ApJ 835, 145: their Eq. 3 rate with the Eq. 10 curvature fit, the
Eggleton (1983) lobe radius, and the Eq. 14 volume averaged potential,
closed form throughout. At and beyond lobe contact the exponent clamps
at the lobe filling boundary value, which is their Figure 5 case.
Validated against their Table 2 planet rates, their Figure 5 binary
curve, and the exact corotating Roche potential at a numerically
solved L1 point.
The nozzle competes in the final comparison on both sides of the
activation gate, wherever the overflow description applies: the
isothermal sonic radius must reach the L1 distance (Jackson et al.
2017, their Figure 9 crossover), because inward of that a spherical
wind chokes at its own sonic surface first, and the nozzle area grows
as the cube of the separation, which otherwise hands a loosely bound
envelope an unbounded rate far from its lobe. A candidate below the
one proton per year floor does not compete either: this label boundary
is a rate crossing, and a crossing between two numerically empty
numbers would rename the deeply bound corner on no content.

A nozzle win labels roche_overflow with the transfer rate itself,
continuous at the crossing; the geometric rename keeps its bound flow
meaning and its rate. The subflag still reads the geometry, with a
third value, nozzle, for a win whose structure sits inside the Hill
sphere. The flow carries no energy cap, faithful to the primary, and
the lift power is reported beside the interior and intercepted
luminosities. nozzle_temperature selects the photospheric level or
the thermostat wind state; eccentric wins are periapsis evaluated and
flagged.
The regimes page derives the nozzle candidate, its applicability
criterion, and the conventions it carries; the results reference
separates the rate crossing reading from the geometric rename and adds
the nozzle diagnostics group and the two new flags; the parameters page
gains nozzle_temperature; the limitations and troubleshooting pages
state what the transfer rate assumes and how to read a roche_overflow
verdict, including the torque balance regulator the module deliberately
does not compute.
The nozzle group brought the container to nineteen entries on a call
that also carries the species closure, and to eighteen without it, so
the reference page and the tutorial both understated it by one. The
tutorial also sent the reader to three cases in the troubleshooting
guide, which lists four. The tutorial's own printed key list already
showed nineteen, so the pages disagreed with output quoted on the same
page.
A secular caller integrates over many orbital periods, so the dispatched
transfer is now the time average of Jackson et al. (2017) Eq. 3 over the
orbit, Kepler-weighted through the eccentric anomaly and duty-cycled
over the arc where the overflow description applies. That arc surrounds
periapsis, because the L1 distance grows with separation while the sonic
radius does not, and the criterion now uses the L1 distance itself
rather than the Hill radius that stood in for it. The lift power is
rebuilt from the barrier the rate applied plus the acceleration to the
sonic speed, which is the heat an isothermal flow demands, so it stays
finite at lobe contact instead of reading a potential the clamp had
already discarded. A circular orbit is unchanged to machine precision.
The subflag claimed to say whether the atmosphere reaches its lobe while
testing it against the Hill radius, which sits about 43 percent further
out, so a structure past its own lobe could still be reported as staying
inside. It now tests the lobe radius, which is the critical surface, and
the third value is geometric rather than named after the candidate that
won: the mechanism is already carried by the rate branch. The overflow
label and its subflag are assigned in one block instead of two, so the
precedence rule is written once. The winning branch's flow radius is no
longer replaced by the lobe radius, which had pinned the reported ratio
at a constant and left the near miss warning suppressed only by clause
order.
Under the wind temperature setting the launch density came from the
photosphere while the sound speed evaluating the barrier came from the
wind, so the two belonged to different structures and the Bernoulli
cancellation behind the launch-level convention did not hold: the rate
tracked whichever level was nominated, moving by nearly three decades
across the plausible range. The launch level now sits on the wind's own
isothermal column, anchored at the wind base with the ideal-gas density
there for the wind's temperature and mean mass, since pressure is
continuous across the temperature transition and density is not. Across
a factor of four in launch radius the rate now holds to half a percent,
against a factor of eleven on the profile's colder column, and a test
pins both halves. The column places the launch level and makes no claim
about structure below the anchor.
@stuitje

stuitje commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@maraattia I can review the docs part of this PR now, should I or are you still making changes?

@maraattia

Copy link
Copy Markdown
Author

@stuitje No not yet please, when I open the PR I'll explicitly tag you. Thanks!

Each candidate's flags were merged into the result as they were computed,
so a dispatched rate arrived carrying warnings about branches that lost:
across three thousand random states, a third of the overflow results
still advertised the luminosity cap of a bolometric candidate they had
beaten, and hydrostatic extension warnings survived onto rates the
hydrostatic branch did not produce. Each candidate now holds its own
flags and only the winner's are merged, so the flag set always describes
the dispatched rate. Input hygiene stays separate, since a stale input or
a clamped base is a property of the state rather than of a candidate.
This replaces two hand-maintained lists of flags to discard, which had
already drifted once.
The rate branch reported alongside an overflow verdict took a value that
no exported tuple contained, so a consumer validating it against the
regime labels failed silently and nothing enumerated the legal set. The
transfer now reports the label itself, which puts every value back inside
the regime labels: reading roche_overflow there means the L1 transfer was
dispatched, and any other value under that label means the geometric
screen renamed a state whose rate that branch produced. The geometric
case needs no name of its own, since it ships whatever rate won before
the screen and the screen is already visible in the flags.
The overflow module is documented public API with its own reference page,
but only the mass ratio was checked, so a direct caller got a negative
mass-loss rate from a negative density, a complex cube root from a
negative mass ratio, and a bare division by zero from a launch level at
the origin or an eccentricity of one. Every exported function now raises
with a message naming the argument, in the style the mass-ratio check
already used. Nothing reachable through the dispatcher changes, since the
state and profile validation upstream already excluded these cases.
The transfer rate is invariant to the launch level only along a column
whose sound speed matches the density it carries, and every dispatcher
fixture was isothermal at the equilibrium temperature, so no test could
see that cancellation fail or tell a profile temperature from an
equilibrium one. A fixture with a temperature rising through the column
now measures the residual: a factor 2.4 across three decades of launch
level, against 1.14 on the isothermal control. Three gaps close with it:
the flow radius reported on an overflow win is pinned as the wind's
rather than the lobe's, the bulk split on that branch is pinned against
the Jeans split its rival would have produced, and the boxedness check
gains an overflow state. The unreachable second clause of the saturation
test is gone, since the potential rises monotonically to the lobe at
every mass ratio and the exponent can only reach zero where the geometry
already has.
The validation page described Jackson et al.'s Table 2 as six rows with
two near-lobe hot Jupiters left unpinned, and quoted a residual of 1.4 to
1.7 that only exists after a radius conversion this implementation
deliberately omits. The table lists twenty-one objects, seventeen of
which this implementation does not reproduce, by factors running to 3.9e6
and rising monotonically with launch depth. The pin now covers every row
whose launch level sits below 0.4 lobe radii, four instead of two, at the
depth where the potential approximation holds, and the page states the
two causes of the shortfall: the omitted radius conversion, which needs
2.2 to 5.2 percent in radius against an exponential sensitivity of 9.7 to
276, and the assumed masses the candidate rows carry. Three quoted
figures are corrected: the agreement against the total potential is
dominated by a constant that cancels, the Figure 5 anchor asserts at 30
percent and exercises no barrier, and the curvature row no longer cites
the paper's own printed asymptotic, which carries half the coefficient
the fit uses.
Four statements did not survive a check against Jackson et al. (2017).
The isothermal flow was described as one the radiation field maintains,
where their Section 3 names the neglected heating and cooling balance and
calls isothermality an important limitation. The bound-flow rate under a
geometric rename was called a lower limit on the tidal transfer without
qualification, which has the wrong sign wherever the nozzle candidate was
applicable and lost, since the module's own estimate of that transfer is
then the smaller number. The fractionation page still said the overflow
label never changes the rate. And the rate floor was quoted under three
different names for one constant. The regimes page also splits into six
paragraphs from one of nearly eight hundred words, and states the floor's
reach: it marks what is distinguishable from zero in floating point, so
about one overflow win in twenty is dispatched below 1e-20 kg per second,
and the depletion screen rather than the floor is what says whether a
rate matters.
The page recorded its last comparison against the source one day early.
Past the activation gate the luminosity-capped bolometric candidate is still computed and reported, but it now competes for the dispatched rate only under the new `residual` setting, whose default `off` leaves the XUV or hydrostatic verdict standing. In the window just past the gate the closed-form Parker and Bondi rates have not yet shut off, so the candidate there is the interior-luminosity cap itself, the core-powered rate whose persistence Tang et al. (2024) dispute, and it outran the XUV rate at interior fluxes near a watt per square meter on a three Earth-mass hydrogen envelope, enough to strip a one percent envelope in a few hundred million years. The bolometric diagnostics gain `residual_mode` and `competes` so a reported candidate can be told apart from a losing one. The tests whose families were built on a residual win admit it explicitly, the overflow flag case moves to a nozzle win under the defaults, and the tutorial's overflow example admits the residual and states what the default returns instead.
The regimes page states the dispute the residual sits on and the side the default takes: Gupta & Schlichting (2019) keep a bolometric wind fed by the cooling interior alive for gigayears, Tang et al. (2024) find it removes at most a tenth of a percent of the envelope once boil-off is initialized self-consistently, and the luminosity cap does not make the difference small, since just past the gate the residual is the cap itself and exceeds the XUV rate by more than a decade at interior fluxes of a few watts per square meter. The evaluation order, the flowchart, and the paragraph that previously claimed the two readings differ by little now say what the setting does instead. The results and parameter references gain the `residual` knob, the two new bolometric diagnostics, and the qualification that the residual flags are reachable only when it is admitted; the model overview and the troubleshooting guide follow.
The Bondi-limited cap is evaluated at the same wind temperature as the Parker rate, T_eq / 2^(1/4), where Gupta & Schlichting (2020) write their Eq. (10) at T_eq; Misener et al. (2025) recommend the cooler value for that same isothermal form, so the cap follows the correction rather than the original. The module header and the regimes page now say so, give the factor between the two conventions, which grows exponentially with the launch-level Jeans parameter and reaches about 30 at the shutoff, and state why it seldom reaches the dispatched rate: the cap and the Parker rate describe one isothermal wind, sitting a constant factor of about e^(3/2) apart at a launch level of unit optical depth, and on the default launch level, which is optically thick to the supplied opacity on an inflated hydrogen envelope, the cap does not bind at all. The reported launch-level optical depth is what tells the two cases apart.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request import Interra Priority 1: critical Priority level 1: highest priority – critical & fast

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Volatile fractionation with escape

2 participants