Skip to content

aragog.output

The aragog.output package exposes standalone diagnostic functions used by the entropy solver and by post-processing code. The primary output contract for callers is the SolverOutput dataclass returned by EntropySolver.get_state(); this module hosts auxiliary diagnostics that operate on a SolverOutput or its constituent arrays.

Name Role
total_enthalpy EOS-consistent integrated mantle enthalpy \(\sum h(P_i, S_i)\,m_i\). Underpins the E_state / E_state_cons columns reported by aragog inspect and by the PROTEUS energy-conservation diagnostics.
volume_average Cell-volume-weighted mean of a staggered-node quantity.
rheological_front Depth (as fractional radius) of the rheological transition, given a basic-node \(\phi\) array, the basic-node radii, and a critical melt fraction \(\phi_\mathrm{rheo}\).

The global mantle melt fraction (mass-weighted \(M_\mathrm{liq}/M_\mathrm{mantle}\) and porosity-derived \(V_\mathrm{liq}/V_\mathrm{mantle}\)) is exposed directly on SolverOutput as Phi_global and Phi_global_vol; no separate diagnostic helper is needed.

Standalone NetCDF output is handled by SolverOutput.to_netcdf(path) (also reachable as EntropySolver.write_netcdf(path) and as aragog run --out path.nc); the PROTEUS wrapper builds its own per-iteration int.nc snapshot independently.

output

Output subpackage: diagnostics for model results.

Hosts standalone diagnostic functions that operate on a SolverOutput or its constituent arrays. The primary output contract for callers is the SolverOutput dataclass returned by EntropySolver.get_state(); everything in this subpackage is auxiliary.

rheological_front(mesh, melt_fraction_basic, rheological_transition_melt_fraction, phi_global, phi_high=0.99, phi_low=0.01)

Compute the dimensionless rheological front position.

The rheological front is defined as the dimensionless depth (relative to the outer radius) where the melt fraction crosses the rheological transition threshold. The bypass thresholds phi_high and phi_low short-circuit the argmin search when the entire mantle is essentially liquid (no transition has formed yet) or essentially solid (transition has reached the surface). The defaults assume a bottom-up solidification mode; the search returns the first radial crossing of rheological_transition_melt_fraction from the CMB outward, so non-bottom-up modes (e.g. middle-out crystallisation) will need a redefined RF concept.

Parameters:

Name Type Description Default
mesh Mesh

The staggered mesh.

required
melt_fraction_basic NDArray

Melt fraction on the basic mesh (nodes x times).

required
rheological_transition_melt_fraction float

Melt fraction threshold for the rheological transition.

required
phi_global float

Volume-averaged global melt fraction at the final timestep.

required
phi_high float

Upper bypass threshold (default 0.99). If phi_global is above this, RF is set to the inner radius (no transition yet).

0.99
phi_low float

Lower bypass threshold (default 0.01). If phi_global is below this, RF is set to the outer radius (fully solidified).

0.01

Returns:

Type Description
float

Dimensionless rheological front (0 = surface, 1 = fully solidified to CMB).

Source code in src/aragog/output/diagnostics.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def rheological_front(
    mesh: Mesh,
    melt_fraction_basic: npt.NDArray,
    rheological_transition_melt_fraction: float,
    phi_global: float,
    phi_high: float = 0.99,
    phi_low: float = 0.01,
) -> float:
    """Compute the dimensionless rheological front position.

    The rheological front is defined as the dimensionless depth (relative to
    the outer radius) where the melt fraction crosses the rheological
    transition threshold. The bypass thresholds ``phi_high`` and
    ``phi_low`` short-circuit the ``argmin`` search when the entire mantle
    is essentially liquid (no transition has formed yet) or essentially
    solid (transition has reached the surface). The defaults assume a
    bottom-up solidification mode; the search returns the *first* radial
    crossing of ``rheological_transition_melt_fraction`` from the CMB
    outward, so non-bottom-up modes (e.g. middle-out crystallisation)
    will need a redefined RF concept.

    Parameters
    ----------
    mesh : Mesh
        The staggered mesh.
    melt_fraction_basic : npt.NDArray
        Melt fraction on the basic mesh (nodes x times).
    rheological_transition_melt_fraction : float
        Melt fraction threshold for the rheological transition.
    phi_global : float
        Volume-averaged global melt fraction at the final timestep.
    phi_high : float, optional
        Upper bypass threshold (default 0.99). If ``phi_global`` is above
        this, RF is set to the inner radius (no transition yet).
    phi_low : float, optional
        Lower bypass threshold (default 0.01). If ``phi_global`` is below
        this, RF is set to the outer radius (fully solidified).

    Returns
    -------
    float
        Dimensionless rheological front (0 = surface, 1 = fully solidified to CMB).
    """
    # Bypass argmin when no transition is present in the mantle.
    if phi_global > phi_high:
        rf: float = mesh.basic.radii[0]
    elif phi_global < phi_low:
        rf = mesh.basic.radii[-1]
    # General case
    else:
        idx = np.argmin(
            np.abs(melt_fraction_basic[:, -1] - rheological_transition_melt_fraction)
        )
        rf = mesh.basic.radii[idx]

    # Return dimensionless rheological front
    return ((mesh.basic.radii[-1] - rf) / mesh.basic.radii[-1]).item()

total_enthalpy(eos, P_stag, S_stag, mass_stag)

Mass-integrated specific enthalpy of the mantle [J].

Computed via the EOS-consistent h(P, S) table that EntropyEOS precomputes from the fundamental thermodynamic relation dh = T dS + (1/rho) dP. Latent heat is captured automatically because the integration path crosses the mushy zone at constant temperature while entropy sweeps across the phase transition.

Parameters:

Name Type Description Default
eos EntropyEOS

EOS object whose specific_enthalpy(P, S) lookup table has already been built (done in EntropyEOS.__init__).

required
P_stag ndarray

Pressure at staggered nodes [Pa].

required
S_stag ndarray

Entropy at staggered nodes [J/kg/K].

required
mass_stag ndarray

Mass per shell at staggered nodes [kg].

required

Returns:

Type Description
float

Total enthalpy [J]. The absolute value is anchor-dependent (zero is fixed by the EOS table corner) but the additive constant cancels in any time difference, which is what the conservation diagnostic uses.

Source code in src/aragog/output/diagnostics.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def total_enthalpy(
    eos: EntropyEOS,
    P_stag: npt.NDArray,
    S_stag: npt.NDArray,
    mass_stag: npt.NDArray,
) -> float:
    """Mass-integrated specific enthalpy of the mantle [J].

    Computed via the EOS-consistent ``h(P, S)`` table that
    ``EntropyEOS`` precomputes from the fundamental thermodynamic
    relation ``dh = T dS + (1/rho) dP``. Latent heat is captured
    automatically because the integration path crosses the mushy
    zone at constant temperature while entropy sweeps across the
    phase transition.

    Parameters
    ----------
    eos : EntropyEOS
        EOS object whose ``specific_enthalpy(P, S)`` lookup table has
        already been built (done in ``EntropyEOS.__init__``).
    P_stag : ndarray
        Pressure at staggered nodes [Pa].
    S_stag : ndarray
        Entropy at staggered nodes [J/kg/K].
    mass_stag : ndarray
        Mass per shell at staggered nodes [kg].

    Returns
    -------
    float
        Total enthalpy [J]. The absolute value is anchor-dependent
        (zero is fixed by the EOS table corner) but the additive
        constant cancels in any time difference, which is what the
        conservation diagnostic uses.
    """
    h_stag = np.asarray(eos.specific_enthalpy(P_stag, S_stag)).ravel()
    mass = np.asarray(mass_stag).ravel()
    return float(np.dot(h_stag, mass))

volume_average(mesh, staggered_quantity)

Compute the volume-weighted average of a quantity on the staggered mesh.

Parameters:

Name Type Description Default
mesh Mesh

The staggered mesh.

required
staggered_quantity NDArray

A 1D array of values at staggered nodes.

required

Returns:

Type Description
float

Volume-averaged value.

Source code in src/aragog/output/diagnostics.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def volume_average(mesh: Mesh, staggered_quantity: npt.NDArray) -> float:
    """Compute the volume-weighted average of a quantity on the staggered mesh.

    Parameters
    ----------
    mesh : Mesh
        The staggered mesh.
    staggered_quantity : npt.NDArray
        A 1D array of values at staggered nodes.

    Returns
    -------
    float
        Volume-averaged value.
    """
    return np.dot(staggered_quantity.T, mesh.basic.volume).item() / mesh.basic.total_volume