Abstract

Meaningful analysis of uranium-series isotopic disequilibria in basaltic lavas relies on the use of complex forward numerical models like dynamic melting McKenzie, 1985 and equilibrium porous flow Spiegelman & Elliott, 1993. Historically, such models have either been solved analytically for simplified scenarios, such as constant melting rate or constant solid/melt trace element partitioning throughout the melting process, or have relied on incremental or numerical calculators with limited power to solve problems and/or restricted availability. The most public numerical solution to reactive porous flow, UserCalc Spiegelman, 2000 was maintained on a private institutional server for nearly two decades, but that approach has been unsustainable in light of modern security concerns. Here we present a more long-lasting solution to the problems of availability, model sophistication and flexibility, and long-term access in the form of a cloud-hosted, publicly available Jupyter notebook. Similar to UserCalc, the new notebook calculates U-series disequilibria during time-dependent, equilibrium partial melting in a one-dimensional porous flow regime where mass is conserved. In addition, we also provide a new disequilibrium transport model which has the same melt transport model as UserCalc, but approximates rate-limited diffusive exchange of nuclides between solid and melt using linear kinetics. The degree of disequilibrium during transport is controlled by a Damköhler number, allowing the full spectrum of equilibration models from complete fractional melting (Da=0Da=0) to equilibrium transport (Da=Da=\infty).

Access this manuscript as a static HTML or downloadable PDF file on the Earth and Space Science journal website here: https://agupubs.onlinelibrary.wiley.com/doi/10.1029/2020EA001619.

Key Points

  • Cloud-based Jupyter notebook presents an open source, reproducible tool for modeling U-series in basalts
  • Equilibrium and pure disequilibrium porous flow U-series models with 1D conservation of mass
  • Scaled porous flow model introduces incomplete equilibrium scenario with reaction rate limitations
Keywords:U-seriesbasaltmeltingmodelingporous flow

1Introduction

Continuous forward melting models are necessary to interpret the origins of empirically-measured U-series isotopic disequilibria in basaltic lavas, but the limited and unreliable availability of reproducible tools for making such calculations remains a persistent problem for geochemists. To date, a number of models have been developed for this task, including classical dynamic melting after McKenzie (1985) and the reactive porous flow model of Spiegelman & Elliott (1993). There have since been numerous approaches to using both the dynamic and porous flow models that range from simplified analytical solutions e.g., Sims et al., 1999Zou & Zindler, 2000 to incremental dynamic melting calculators Stracke et al., 2003, two-porosity calculators Jull et al., 2002Lundstrom et al., 2000Sims et al., 2002, and one-dimensional numerical solutions to reactive porous flow Spiegelman, 2000 and dynamic melting Bourdon et al., 2005Elkins et al., 2019. Unfortunately, some of the approaches published since 1990 lacked publicly available tools that would permit others to directly apply the authors’ methods, and while the more simplified and incremental approaches remain appropriate for asking and approaching some questions, they are insufficient for other applications that require more complex approaches e.g., two-lithology melting Elkins et al., 2019. Other tools like UserCalc that were available to public users Spiegelman, 2000 were limited in application and have since become unavailable.

In light of the need for more broadly accessible and flexible solutions to U-series disequilibrium problems in partial melting, here we present a cloud-server hosted, publicly available numerical calculator for one-dimensional, decompression partial melting. The tool is provided in a Jupyter notebook with importable Python code and can be accessed from a web browser. Users will be able to access and use the tool using a free cloud server account, or on their own computer given any standard Python distribution. As shown below, the notebook is structured to permit the user to select one of two primary model versions, either classical reactive porous flow after Spiegelman & Elliott (1993) and Spiegelman (2000), or a new disequilibrium transport model, developed after the appendix formulas of Spiegelman & Elliott (1993). The new model ranges from pure disequilibrium porous flow transport (i.e., the mass-conserved equivalent of true fractional melting over time) to a “scaled” disequilibrium scenario, where the degree of chemical equilibrium that is reached is determined by the relationship between the rate of chemical reaction and the solid decompression rate (which is, in turn, related to the overall melting rate), in the form of a Damköhler number.

This scaled disequilibrium model resembles the classic dynamic melting model of McKenzie (1985), with the caveat that ours is the first U-series melting model developed for near-fractional, disequilibrium transport where mass is also conserved within a one-dimensional melting regime. That is, rather than controlling the quantity of melt that remains in equilibrium with the solid using a fixed residual porosity, the melt porosity is controlled by Darcy’s Law and mass conservation constraints after Spiegelman & Elliott (1993), and the “near-fractional” scenario is simulated using the reaction rate of the migrating liquid with the upwelling solid matrix.

2Calculating U-series in basalts during mass-conserved, one-dimensional porous flow

2.1Solving for equilibrium transport

Here we consider several forward melting models that calculate the concentrations and activities of U-series isotopes (238U, 230Th, 226Ra, 235U, and 231Pa) during partial melting and melt transport due to adiabatic mantle decompression. Following Spiegelman & Elliott (1993), we start with conservation of mass equations for the concentration of a nuclide ii, assuming chemical equilibrium between melt and solid:

t[ρfϕ+ρs(1ϕ)Di]cif+[ρfϕv+ρs(1ϕ)DiV]cif=λi1[ρfϕ+ρs(1ϕ)Di1]ci1fλi[ρfϕ+ρs(1ϕ)Di]cif\begin{align*} \frac{\partial}{\partial t}[\rho_{f} \phi + \rho_{s}(1-\phi)D_i]c_{i}^f + \nabla \cdot [\rho_f \phi v + \rho_s (1 - \phi) D_i V] c_i^f \\ = \lambda_{i-1}[\rho_f \phi + \rho_s (1-\phi) D_{i-1}]c_{i-1}^f - \lambda_{i}[\rho_f \phi + \rho_s (1-\phi) D_{i}]c_{i}^f \end{align*}

where tt is time, cifc_i^f is the concentration of nuclide ii in the melt, DiD_i is the bulk solid/liquid partition coefficient for nuclide ii, ρf\rho_f is the density of the fluid and ρs\rho_s is the density of the solid, ϕ is the porosity (local volume fraction of melt), vv is the velocity of the melt and VV the velocity of the solid in three dimensions, λi\lambda_i is the decay constant of nuclide ii, and (i1)(i-1) indicates the radioactive parent of nuclide ii (see Table 1). Equation (1) states that the change in total mass of nuclide ii in both the melt and the solid is controlled by the divergence of the mass flux transported by both phases and by the radioactive decay of both parent and daughter nuclides (i.e., the right hand side of the equation above).

Table 1:List of variables used in this study.

VariableDefinition
cifc_i^fConcentration of nuclide ii in the liquid
cisc_i^sConcentration of nuclide ii in the solid
UifU_i^fNatural log of the concentration of nuclide ii in the liquid relative to its initial concentration
UisU_i^sNatural log of the concentration of nuclide ii in the solid relative to its initial concentration
UistableU_i^{stable}Stable element component of UifU_i^f
UiradU_i^{rad}Radiogenic component of UifU_i^f
aia_iActivity of nuclide ii
ai0a_i^0Initial activity of nuclide ii
zzHeight in a one-dimensional melting column
hhTotal height of the melting column
ζ=z/h=z/h, Dimensionless fractional height in scaled one-dimensional melting column
DiD_iBulk solid/liquid partition coefficient for nuclide ii
Di0D_i^0Initial bulk solid/liquid partition coefficient for nuclide ii
ρf\rho_{f}Density of the liquid
ρs\rho_{s}Density of the solid
ϕPorosity (volume fraction of liquid present)
ϕ0\phi_0Maximum or reference porosity
VVSolid velocity
vvLiquid velocity
WWOne-dimensional solid velocity
wwOne-dimensional liquid velocity
W0W_0Solid mantle upwelling velocity
λi\lambda_iDecay constant for nuclide ii
λi\lambda'_i=λih/W0=\lambda_i h/W_0, Decay constant for nuclide ii scaled by solid transport time
ΓMelting rate
Γ0\Gamma_0Constant melting rate
FmaxF_{max}Maximum degree of melting
weffiw_{eff}^{i}Effective liquid velocity of nuclide ii
Rii1R_i^{i-1}Ingrowth factor
αi0\alpha_i^0Initial degree of secular disequilibrium in the unmelted solid
kkPermeability
KrK_rRelative permeability factor
nnPermeability exponent
AdA_dPermeability calibration function
\ReReactivity rate factor
ddDiffusion/Reaction length scale (e.g., grain-size)
DaDaDamköhler number

The equilibrium model of Spiegelman & Elliott (1993) assumes that complete chemical equilibrium is maintained between the migrating partial melt and the solid rock matrix along a decompressing one-dimensional column. To close the equations, they assume that melt transport is described by a simplified form of Darcy’s Law for permeable flow through the solid matrix. In one dimension, for a steady-state upwelling column of melting mantle rocks, they defined the one-dimensional melt and solid velocities (ww and WW, respectively), and expressed the melt and solid fluxes as functions of height (zz) in terms of a constant melting rate Γ0\Gamma_0:

ρfϕw=Γ0z\rho_f \phi w = \Gamma_0 z
ρs(1ϕ)W=ρsW0Γ0z\rho_s (1 - \phi) W = \rho_s W_0 - \Gamma_0 z

where W0W_0 is the solid mantle upwelling rate, and Γ0\Gamma_0 is equivalent to ρsW0Fmax\rho_s W_0 F_{max} divided by the column height hh for a maximum degree of melting FmaxF_{max}.

Assuming an initial condition of secular equilibrium, where the initial activities λici,0fDi\lambda_i c_{i,0}^f D_i are equivalent for parent and daughter nuclides, they derived a system of differential equations for the concentration cifc_i^f in any decay chain, which can be solved numerically using equation (10) from Spiegelman & Elliott (1993):

dcidζ=ci(Di1)FmaxDi+(1Di)Fmaxζ+λih[Di[Di1+(1Di1)Fmaxζ]Di1[Di+(1Di)Fmaxζ]ci1weffi1ciweffi]\frac{dc_i'}{d\zeta} = c_i' \frac{(D_i-1)F_{max}}{D_i + (1-D_i)F_{max} \zeta} + \lambda_i h\left[ \frac{D_i[D_{i-1}+(1-D_{i-1})F_{max}\zeta]}{D_{i-1}[D_i + (1-D_i)F_{max}\zeta]} \frac{c_{i-1}'}{w_{eff}^{i-1}} - \frac{c_i'}{w_{eff}^i}\right]

where cic_i' is the scaled melt concentration (=cif/ci,0f)(= c_i^f/c_{i,0}^f), ζ is the dimensionless fractional height in the scaled column, equal to 0 at the base and 1 at the top, and

weffi=ρfϕw+ρs(1ϕ)DiWρfϕ+ρs(1ϕ)Diw_{eff}^i = \frac{\rho_f \phi w + \rho_s(1-\phi)D_i W}{\rho_f \phi + \rho_s(1-\phi)D_i}

is the effective velocity for element ii.

In their appendix, Spiegelman & Elliott (1993) developed the more general (and, arguably, realistic) form where Γ and DiD_i are functions of height zz. The UserCalc model of Spiegelman (2000) then formulated a one-dimensional numerical integration for the concentrations of selected U-series isotopes in continuously produced partial melts with height zz, after the equilibrium formulas above. The concentration expression derived by Spiegelman (2000) for the equilibrium scenario (formula 6 in Spiegelman (2000)) is:

dcifdz=cif(z)F(z)+(1F(z))Di(z)ddz[F(z)+(1F(z))Di(z)]+λi1ρDi1ci1f(z)λiρDicif(z)ρsW0[F(z)+(1F(z))Di(z)]\frac{dc_i^f}{dz} = \frac{-c_i^f(z)}{F(z) + (1 - F(z)) D_i(z)} \frac{d}{dz} [F(z) + (1 - F(z)) D_i(z)] + \frac{\lambda_{i-1}\overline{\rho D_{i-1}} c_{i-1}^f(z) - \lambda_{i}\overline{\rho D_{i}} c_{i}^f(z)}{\rho_s W_0 [F(z) + (1 - F(z)) D_i(z)]}

where FF is the degree of melting. Spiegelman (2000) further observed that solving for the natural log of the concentrations normalized to the initial concentration of ii, UiU_i, rather than the concentrations themselves, is more accurate, particularly for highly incompatible elements (formulas 7-9 in that reference). This is because log concentrations change linearly during melting, rather than exponentially, and are more numerically stable to calculate.

Uif=ln(cifci,0f)U_i^f = \ln\left(\frac{c_i^f}{c_{i,0}^f}\right)
dUifdz=1cif(z)dcifdz\frac{dU_i^f}{dz} = \frac{1}{c_i^f(z)} \frac{dc_i^f}{dz}
dUifdz=1F(z)+(1F(z))Di(z)ddz[F(z)+(1F(z))Di(z)]+λiweffi[Rii1exp[Ui1f(z)Uif(z)]1]\frac{dU_i^f}{dz} = \frac{-1}{F(z) + (1 - F(z)) D_i(z)} \frac{d}{dz} [F(z) + (1-F(z))D_i(z)] + \frac{\lambda_i}{w_{eff}^i} [R_i^{i-1} \exp[U_{i-1}^f(z)-U_i^f(z)]-1]

For the formulas above, Spiegelman (2000) defined a series of variables that allow for simpler integration formulas and aid in efficient solution of the model, namely

ρDi=ρfϕ+ρs(1ϕ)Di(z),\overline{\rho D_i} = \rho_f \phi + \rho_s (1-\phi)D_i(z) ,
F=F(z)+(1F(z))Di(z),\overline{F} = F(z) + (1-F(z)) D_i(z) ,
Rii1=αi0Di0Di10ρDi1ρDi,R_i^{i-1} = \alpha_i^0 \frac{D_i^0}{D_{i-1}^0} \frac{\overline{\rho D_{i-1}}}{\overline{\rho D_i}} ,
αi0=λi1c(i1),0sλici,0s,\alpha_i^0 = \frac{\lambda_{i-1} c_{(i-1),0}^s}{\lambda_i c_{i,0}^s} ,

and substituting from the formulas above

weffi=ρsW0FρDi.w_{eff}^i = \frac{\rho_s W_0 \overline{F}}{\overline{\rho D_i}} .

where Di0D_i^0 is the initial bulk solid/melt partition coefficient for element ii, Rii1R_i^{i-1} is the ingrowth factor, and αi0\alpha_i^0 is the initial degree of secular disequilibrium for nuclide ii in the unmelted solid.

Ui(z)=ln(cf(z)/cf0)U_i(z) = \ln(c_f(z)/c_f^0), the log of the total concentration of nuclide ii in the melt, can then be decomposed into

Ui(z)=Uistable(z)+Uirad(z)U_i(z) = U^{stable}_i(z) + U^{rad}_i(z)

where

Uistable(z)=ln[Di0FDi(z)]U^{stable}_i(z) = \ln\left[ \frac{D_i^0}{\overline{F}D_i(z)}\right]

is the log concentration of a stable nuclide with the same partition coefficients, and Uirad(z)U^{rad}_i(z) is the radiogenic ingrowth component. An alternate way of writing the radiogenic ingrowth component of equation (9) of Spiegelman (2000) is:

dUiraddζ=λiρDiFDi[Rii1exp[Ui1(ζ)Ui(ζ)]1]\frac{dU_i^{rad}}{d\zeta} = \lambda'_i\frac{\overline{\rho D_{i}}}{\overline{F} D_{i}}\left[R_i^{i-1}\exp[U_{i-1}(\zeta)-U_i(\zeta)] - 1\right]

where

λi=hλiW0\lambda'_i = \frac{h\lambda_i}{W_0}

is the decay constant of nuclide ii, scaled by the solid transport time (h/W0h/W_0) across a layer of total height hh. Note equation (17) is solved over a column of dimensionless height 1 where ζ[0,1]\zeta\in[0,1].

Using these equations, the UserCalc reactive porous flow calculator accepted user inputs for both F(z)F(z) and Di(z)D_i(z). The method further uses a formula for the melt porosity (ϕ(z)\phi(z)) based on a Darcy’s Law expression with a relative permeability factor (formula 20 from Spiegelman (2000)):

Kr(z)Adϕn(1ϕ)2+ϕ[1+F(z)(ρsρf1)]ρsρfF(z)=0K_r(z)A_d \phi^n (1-\phi)^2 + \phi [1 + F(z) (\frac{\rho_s}{\rho_f} - 1)] - \frac{\rho_s}{\rho_f}F(z) = 0

where Kr(z)K_r(z) is the relative permeability with height zz, AdA_d is a permeability calibration function, and nn is the permeability exponent. The permeability exponent for a tube-shaped fluid network is expected to be n=2n = 2, while for a sheet-shaped network it is n=3n = 3; recent measurements of the permeabilities of experimental magmatic melt networks suggest realistic magma migration occurs in a manner intermediate between these two scenarios, with n=2.6n = 2.6 Miller et al., 2014. The relative permeability KrK_r is calculated with respect to the permeability at the top of the column, i.e. depth z=zfinalz=z_{final}:

Kr(z)=k(z)k(zfinal)K_r(z) = \frac{k(z)}{k(z_{final})}

and allows for locally enhanced flow (e.g., mimicking the effects of a relatively low viscosity fluid).

Our model implementation reproduces and builds on the prior efforts summarized above, using a readily accessible computer language (Python) and web application (Jupyter notebooks).

2.2Solving for complete disequilibrium transport

We further present a calculation tool that solves a similar set of equations for pure chemical disequilibrium transport during one-dimensional decompression melting. This model assumes that the solid produces an instantaneous fractional melt in local equilibrium with the solid; however, the melt is not allowed to back-react with the solid during transport, as it would in the equilibrium model above. In the limiting condition defined by stable trace elements (i.e., without radioactive decay), the model reduces to the calculation for an accumulated fractional melt. The model solves for the concentration of each nuclide ii in the solid (s)(s) and liquid (f)(f) using equations (26) and (27) of Spiegelman & Elliott (1993):

dcisdz=cis(z)(11Di(z))1F(z)dFdz+1ϕW0(1F(z))[λi1ci1s(z)λicis(z)]\frac{dc_i^s}{dz} = \frac{c_i^s(z) (1 - \frac{1}{D_i(z)})}{1-F(z)} \frac{dF}{dz} + \frac{1-\phi}{W_0(1-F(z))} [\lambda_{i-1} c_{i-1}^s(z) - \lambda_i c_i^s(z)]
dcifdz=cis(z)Di(z)cif(z)F(z)dFdz+ρfϕρsW0F(z)[λi1ci1f(z)λicif(z)]\frac{dc_i^f}{dz} = \frac{\frac{c_i^s(z)}{D_i(z)}-c_i^f(z)}{F(z)} \frac{dF}{dz} + \frac{\rho_f \phi}{\rho_s W_0 F(z)} [\lambda_{i-1} c_{i-1}^f(z) - \lambda_i c_i^f(z)]

which maintain conservation of mass for both fluid and solid individually, and do not assume chemical equilibration between the two phases. As above, the equations depend on F(z)F(z) and Di(z)D_i(z), i.e. melt fractions and bulk rock partition coefficients that can vary with depth.

As above, the solid and fluid concentration equations are rewritten in terms of the logs of the concentrations:

Uis(z)=ln(cis(z)ci,0s),Uif(z)=ln(cif(z)ci,0f)U^s_i(z) = \ln\left(\frac{c_i^{s}(z)}{c_{i,0}^s}\right), \quad U^f_i(z) = \ln\left(\frac{c_i^{f}(z)}{c_{i,0}^f}\right)

and thus

dUidz=1ci(z)dcidz\frac{dU_i}{dz} = \frac{1}{c_i(z)} \frac{dc_i}{dz}

We assume that initial ci,0s=Di,0ci,0fc_{i,0}^s = D_{i,0} c_{i,0}^f. As above, the log concentration equations can be broken into stable and radiogenic components, where the stable log concentration equations are:

dUis,stabledz=11Di(z)1F(z)dFdz\frac{dU_{i}^{s,stable}}{dz} = \frac{1-\frac{1}{D_i(z)}}{1-F(z)} \frac{dF}{dz}
dUif,stabledz=Di0Di(z)exp(Uis(z)Uif(z))F(z)dFdz\frac{dU_{i}^{f,stable}}{dz} = \frac{\frac{D_i^0}{D_i(z)} \exp(U_i^s(z)-U_i^f(z))}{F(z)}\frac{dF}{dz}

which are equivalent to a model for a fractionally melted residual solid and an accumulated fractional melt for the liquid.

Reincorporating this with the radiogenic component and scaling all distances by hh gives the dimensionless equations:

dUisdζ=11Di(ζ)1F(ζ)dFdζ+1ϕ1F(ζ)λi[αi10αi0exp[Ui1s(ζ)Uis(ζ)]1]\frac{dU_{i}^{s}}{d\zeta} = \frac{1-\frac{1}{D_i(\zeta)}}{1-F(\zeta)} \frac{dF}{d\zeta} + \frac{1-\phi}{1-F(\zeta)}\lambda^\prime_i \left[\frac{\alpha_{i-1}^0}{\alpha_i^0} \exp[U_{i-1}^s(\zeta) - U_i^s(\zeta)]-1\right]
dUifdζ=Di0Di(ζ)exp(Uis(ζ)Uif(ζ))F(ζ)+ρfϕρsF(ζ)λi[Di0αi10Di10αi0exp[Ui1f(ζ)Uif(ζ)]1]\frac{dU_{i}^{f}}{d\zeta} = \frac{\frac{D_i^0}{D_i(\zeta)} \exp(U_i^s(\zeta)-U_i^f(\zeta))}{F(\zeta)} + \frac{\rho_f \phi}{\rho_s F(\zeta)}\lambda^\prime_i \left[\frac{D_i^0 \alpha_{i-1}^0}{D_{i-1}^0 \alpha_i^0} \exp[U_{i-1}^f(\zeta) - U_i^f(\zeta)]-1\right]

2.3Solving for transport with chemical reactivity rates

The two models described above are end members for complete equilibrium and complete disequilibrium transport. For stable trace elements, these models produce melt compositions that are equivalent to batch melting and accumulated fractional melting e.g., Spiegelman & Elliott, 1993. However, the actual transport of a reactive fluid (like a melt) through a solid matrix can fall anywhere between these end members depending on the rate of transport and re-equilibration between melt and solid, which can be sensitive to the mesoscopic geometry of melt and solid e.g., Spiegelman & Kenyon, 1992. In an intermediate scenario, we envision that some reaction occurs, but chemical equilibration is incomplete due to slow reaction rates relative to the differential transport rates for the fluid and solid. If reaction times are sufficiently rapid to achieve chemical exchange over the lengthscale of interest before the liquid segregates, chemical equilibrium can be achieved; but for reactions that occur more slowly than effective transport rates, only partial chemical equilibrium can occur e.g., Grose & Afonso, 2019Iwamori, 1993Iwamori, 1994Kogiso et al., 2004Liang & Liu, 2016Peate & Hawkesworth, 2005Qin et al., 1992Yang et al., 2000. Such reaction rates can include, for example, the rate of chemical migration over the distance between high porosity veins or channels e.g., Aharonov et al., 1995Jull et al., 2002Spiegelman, 2000Stracke & Bourdon, 2009; or, at the grain scale, the solid chemical diffusivity of elements over the diameter of individual mineral grains e.g., Qin et al., 1992Feineman & DePaolo, 2003Grose & Afonso, 2019Oliveira et al., 2020Van Orman et al., 2002Van Orman et al., 2006.

To model this reactive transport scenario, we start with our equations for disequilibrium transport in a steady-state, one-dimensional conservative system, and add a chemical back-reaction term that permits exchange of elements between the fluid and the solid. The reaction term is scaled by a reactivity rate factor, \Re and expressed in kg/m3/yr. (i.e., the same units as the melting rate). The reactivity rate thus behaves much like the melting rate by governing the rate of exchange between the solid and liquid phases, effectively scaling the degree to which chemical exchange can occur. This new term could simulate a number of plausible scenarios that would physically limit the rate of chemical exchange by transport along a given distance in a linear manner, such as the movement or diffusion of nuclides through the porous solid matrix between melt channels a given distance apart.

First, returning to the conservation of mass equations for a steady-state, one-dimensional, reactive system of stable trace elements, and using Γ(z)\Gamma(z) to represent the melting rate:

ddzρfϕw=Γ(z)\frac{d}{dz} \rho_f \phi w = \Gamma(z)
ddzρs(1ϕ)W=Γ(z)\frac{d}{dz} \rho_s (1-\phi) W = -\Gamma(z)
ddzρfϕwcif(z)=cis(z)Di(z)Γ(z)(cif(z)cis(z)Di(z))\frac{d}{dz} \rho_f \phi w c_i^f(z) = \frac{c_i^s(z)}{D_i(z)}\Gamma(z) - \Re\left(c_i^f(z) - \frac{c_i^s(z)}{D_i(z)}\right)
ddzρs(1ϕ)Wcis(z)=cis(z)Di(z)Γ(z)+(cif(z)cis(z)Di(z))\frac{d}{dz} \rho_s (1-\phi) W c_i^s(z) = - \frac{c_i^s(z)}{D_i(z)}\Gamma(z) + \Re\left(c_i^f(z) - \frac{c_i^s(z)}{D_i(z)}\right)

where, for an adiabatic upwelling column,

Γ(z)=ρsW0dFdz\Gamma(z) = \rho_s W_0 \frac{dF}{dz}

From this, the equations (29) and (30) can be integrated (with appropriate boundary conditions at z=0z=0) to give

ρfϕw=ρsW0F(z)\rho_f \phi w = \rho_s W_0 F(z)
ρs(1ϕ)W=ρsW0(1F(z))\rho_s (1-\phi) W = \rho_s W_0 (1 - F(z))

Next, we expand the concentration equations to include the reactivity factor, and substitute the conservation of total mass determined above:

ρsW0F(z)ddzcif(z)+cif(z)Γ(z)=cis(z)Di(z)Γ(z)(cif(z)cis(z)Di(z))\rho_s W_0 F(z)\frac{d}{dz} c_i^f(z) + c_i^f(z)\Gamma(z) = \frac{c_i^s(z)}{D_i(z)}\Gamma(z) - \Re\left(c_i^f(z) - \frac{c_i^s(z)}{D_i(z)}\right)
ρsW0(1F(z))ddzcis(z)cis(z)Γ(z)=cis(z)Di(z)Γ(z)+(cif(z)cis(z)Di(z))\rho_s W_0 (1 - F(z))\frac{d}{dz}c_i^s(z) - c_i^s(z)\Gamma(z) = - \frac{c_i^s(z)}{D_i(z)}\Gamma(z) + \Re\left(c_i^f(z) - \frac{c_i^s(z)}{D_i(z)}\right)

If we then combine the Γ(z)\Gamma(z) terms and rearrange:

ρsW0F(z)ddzcif(z)=Γ(z)(cis(z)Di(z)cif(z))(cif(z)cis(z)Di(z))\rho_s W_0 F(z)\frac{d}{dz} c_i^f(z) = \Gamma(z)\left(\frac{c_i^s(z)}{D_i(z)} - c_i^f(z)\right) - \Re\left(c_i^f(z) - \frac{c_i^s(z)}{D_i(z)}\right)
ρsW0(1F(z))ddzcis(z)=Γ(z)cis(z)(11Di(z))+(cif(z)cis(z)Di(z))\rho_s W_0 (1 - F(z))\frac{d}{dz}c_i^s(z) = \Gamma(z) c_i^s(z)\left(1 - \frac{1}{D_i(z)}\right)+ \Re\left(c_i^f(z) - \frac{c_i^s(z)}{D_i(z)}\right)

We can now divide the fluid and solid equations by cifc_i^f and cisc_i^s, respectively, and rearrange the W0W_0 terms:

1cif(z)dcifdz=1ρsW0F(z)[Γ(z)(cis(z)Di(z)cif(z)1)(1cis(z)Di(z)cif(z))]\frac{1}{c_i^f(z)}\frac{dc_i^f}{dz} = \frac{1}{\rho_s W_0 F(z)}\left[\Gamma(z)\left(\frac{c_i^s(z)}{D_i(z) c_i^f(z)} - 1\right) - \Re\left(1 - \frac{c_i^s(z)}{D_i(z) c_i^f(z)}\right)\right]
1cis(z)dcisdz=1ρsW0(1F(z))[Γ(z)(11Di(z))+Di(z)(Di(z)cif(z)cis(z)1)]\frac{1}{c_i^s(z)}\frac{dc_i^s}{dz} = \frac{1}{\rho_s W_0 (1 - F(z))}\left[\Gamma(z) \left(1 - \frac{1}{D_i(z)}\right)+ \frac{\Re}{D_i(z)}\left(\frac{D_i(z) c_i^f(z)}{c_i^s(z)} - 1\right) \right]

The first terms on the right-hand side of each of these equations are identical to pure disequilibrium melting, such that if \Re is zero, the equations reduce to the disequilibrium transport case of Spiegelman & Elliott (1993).

To solve, the final terms that involve the reactivity factor can be further rewritten using the definitions for UifU_i^f and UisU_i^s:

cif(z)=ci,0fexp[Uif(z)]=ci,0sDi0exp[Uif(z)]c_i^f(z) = c_{i,0}^f \exp[U_i^f(z)] = \frac{c_{i,0}^s}{D_i^0} \exp[U_i^f(z)]
cis(z)=ci,0sexp[Uis(z)]c_i^s(z) = c_{i,0}^s \exp[U_i^s(z)]

Thus:

Di(z)cif(z)cis(z)=Di(z)Di0exp[Uif(z)Uis(z)]\frac{D_i(z) c_i^f(z)}{c_i^s(z)} = \frac{D_i(z)}{D_i^0} \exp[U_i^f(z) - U_i^s(z)]
cis(z)Di(z)cif(z)=Di0Di(z)exp[Uis(z)Uif(z)]\frac{c_i^s(z)}{D_i(z) c_i^f(z)} = \frac{D_i^0}{D_i(z)} \exp[U_i^s(z) - U_i^f(z)]

and:

dUifdz=1ρsW0F(z)[Γ(z)(Di0Di(z)exp[Uis(z)Uif(z)]1)(1Di0Di(z)exp[Uis(z)Uif(z)])]\frac{dU_i^f}{dz} = \frac{1}{\rho_s W_0 F(z)}\left[\Gamma(z)\left(\frac{D_i^0}{D_i(z)} \exp[U_i^s(z) - U_i^f(z)] - 1\right) - \Re\left(1 - \frac{D_i^0}{D_i(z)} \exp[U_i^s(z) - U_i^f(z)]\right)\right]
dUisdz=1ρsW0(1F(z))[Γ(z)(11Di(z))+Di(z)(Di(z)Di0exp[Uif(z)Uis(z)]1)]\frac{dU_i^s}{dz} = \frac{1}{\rho_s W_0 (1 - F(z))}\left[\Gamma(z) \left(1 - \frac{1}{D_i(z)}\right)+ \frac{\Re}{D_i(z)}\left(\frac{D_i(z)}{D_i^0} \exp[U_i^f(z) - U_i^s(z)] - 1\right) \right]

Finally, substituting adiabatic upwelling and scaling depth by hh, and adding radioactive terms gives the full solutions for the dimensionless equations dUi/dζdU_i/d\zeta:

dUifdζ=1F(ζ)[dFdζ(Di0Di(ζ)exp[Uis(ζ)Uif(ζ)]1)]hρsW0F(ζ)[1Di0Di(ζ)exp[Uis(ζ)Uif(ζ)]]+ρfϕρsFλi[Di0αi10Di10αi0exp[Ui1f(ζ)Uif(ζ)]1]\frac{dU_i^f}{d\zeta} = \frac{1}{F(\zeta)}\left[\frac{dF}{d\zeta}\left(\frac{D_i^0}{D_i(\zeta)}\exp[U_i^s(\zeta) - U_i^f(\zeta)] - 1\right)\right] - \frac{\Re h}{\rho_s W_0 F(\zeta)}\left[1 - \frac{D_i^0}{D_i(\zeta)}\exp[U_i^s(\zeta) - U_i^f(\zeta)]\right] + \frac{\rho_f \phi}{\rho_s F}\lambda^\prime_i \left[\frac{D_i^0 \alpha_{i-1}^0}{D_{i-1}^0 \alpha_i^0} \exp[U_{i-1}^f(\zeta) - U_i^f(\zeta)]-1\right]
dUisdζ=1(1F(ζ))[dFdζ(11Di(ζ))]+hρsW0Di(ζ)(1F(ζ))[Di(ζ)Di0exp[Uif(ζ)Uis(ζ)]1]+1ϕ1F(ζ)λi[αi10αi0exp[Ui1s(ζ)Uis(ζ)]1]\frac{dU_i^s}{d\zeta} = \frac{1}{(1 - F(\zeta))}\left[\frac{dF}{d\zeta} \left(1 - \frac{1}{D_i(\zeta)}\right)\right]+ \frac{\Re h}{\rho_s W_0 D_i(\zeta)(1 - F(\zeta))}\left[\frac{D_i(\zeta)}{D_i^0} \exp[U_i^f(\zeta) - U_i^s(\zeta)] - 1 \right] + \frac{1-\phi}{1-F(\zeta)}\lambda^\prime_i \left[\frac{\alpha_{i-1}^0}{\alpha_i^0} \exp[U_{i-1}^s(\zeta) - U_i^s(\zeta)]-1\right]

where hh is the total height of the melting column.

2.3.1The Damköhler number

The dimensionless combination

Da=hρsW0Da = \frac{\Re h}{\rho_s W_0}

is the Damköhler number, which governs the reaction rate relative to the solid transport time. Damköhler numbers more generally are used to relate the timescales of chemical reactions to the rates of physical transport in a system. If re-equilibration is limited by solid state diffusion, \Re can be estimated using:

ρsDid2\Re \approx \frac{\rho_s{\cal D}_i}{d^2}

where Di{\cal D}_i is the solid state diffusivity of element ii, and dd is a nominal spacing between melt-channels (this spacing could, for example, be the average grain diameter for grain-scale channels, or 10 cm for closely spaced veins).

In this case (which we will assume for this paper), the Damköhler number can be written

Da=DihW0d2Da = \frac{{\cal D}_i h}{W_0 d^2}

Substituting the definition of DaDa above yields the final dimensionless ODEs for the disequilbrium transport model:

dUifdζ=1F(ζ)(dFdζ+Da)(Di0Di(ζ)exp[Uis(ζ)Uif(ζ)]1)+ρfϕρsFλi[Di0αi10Di10αi0exp[Ui1f(ζ)Uif(ζ)]1]\frac{dU_i^f}{d\zeta} = \frac{1}{F(\zeta)}\left(\frac{dF}{d\zeta} + Da\right)\left(\frac{D_i^0}{D_i(\zeta)}\exp[U_i^s(\zeta) - U_i^f(\zeta)] - 1\right) + \frac{\rho_f \phi}{\rho_s F}\lambda^\prime_i \left[\frac{D_i^0 \alpha_{i-1}^0}{D_{i-1}^0 \alpha_i^0} \exp[U_{i-1}^f(\zeta) - U_i^f(\zeta)]-1\right]
dUisdζ=1(1F(ζ))[dFdζ(11Di(ζ))+DaDi(ζ)(Di(ζ)Di0exp[Uif(ζ)Uis(ζ)]1)]+1ϕ1F(ζ)λi[αi10αi0exp[Ui1s(ζ)Uis(ζ)]1]\frac{dU_i^s}{d\zeta} = \frac{1}{(1 - F(\zeta))}\left[\frac{dF}{d\zeta} \left(1 - \frac{1}{D_i(\zeta)}\right)+ \frac{Da}{D_i(\zeta)}\left(\frac{D_i(\zeta)}{D_i^0}\exp[U_i^f(\zeta) - U_i^s(\zeta)] - 1\right) \right]+ \frac{1-\phi}{1-F(\zeta)}\lambda^\prime_i \left[\frac{\alpha_{i-1}^0}{\alpha_i^0} \exp[U_{i-1}^s(\zeta) - U_i^s(\zeta)]-1\right]

with initial conditions Uis=Uif=0U_i^s = U_i^f = 0.

In the limit where the Damköhler number approaches zero, the above formulas reduce to pure disequilibrium transport, whereas if DaDa approaches infinity (i.e., infinitely fast reactivity compared to physical transport), the system approaches equilibrium conditions (cisDicifc_i^s \rightarrow D_i c_i^f).

2.3.2Initial conditions

Inspection of equation (53) shows that for the initial conditions described above and F(0)=0F(0)=0, dUifdζ\frac{dU_i^f}{d\zeta} is ill-defined (at least numerically in a floating-point system). However, taking the limit ζ0\zeta\rightarrow 0 and applying L’Hôpital’s rule yields

limζ0dUifdζ=Uis(0)Uif(0)F(0)(dFdζ+Da)+λi[Di0αi10Di10αi01]\lim_{\zeta\rightarrow 0} \frac{dU_i^f}{d\zeta} = \frac{U_i^{'s}(0) - U_i^{'f}(0)}{F'(0)}\left(\frac{dF}{d\zeta} + Da\right) + \lambda^\prime_i \left[\frac{D_i^0 \alpha_{i-1}^0}{D_{i-1}^0 \alpha_i^0} - 1\right]

where

Uis(0)=dUisdζζ=0U_i^{'s}(0) = \left.\frac{dU_i^s}{d\zeta}\right|_{\zeta=0}
Uif(0)=dUifdζζ=0U_i^{'f}(0) = \left.\frac{dU_i^f}{d\zeta}\right|_{\zeta=0}
F(0)=dFdζζ=0F'(0) = \left.\frac{dF}{d\zeta}\right|_{\zeta=0}

The initial radiogenic term also uses the limit from equation (34):

limζ0ρfϕρsF=W0w(0)=1\lim_{\zeta\rightarrow 0} \frac{\rho_f \phi}{\rho_s F} = \frac{W_0}{w(0)} = 1

Rearranging equation (55) gives the value for Uif(0)U_i^{'f}(0) for F=0F=0 as

limζ0dUifdζ=12+DaF(0)[Uis(0)(1+DaF(0))+λi[Di0αi10Di10αi01]]\lim_{\zeta\rightarrow 0} \frac{dU_i^f}{d\zeta} = \frac{1}{2 + \frac{Da}{F'(0)}}\left[U_i^{'s}(0)\left(1 + \frac{Da}{F'(0)}\right) +\lambda^\prime_i \left[\frac{D_i^0 \alpha_{i-1}^0}{D_{i-1}^0 \alpha_i^0} - 1\right] \right]

3A pyUserCalc Jupyter notebook

3.1Code design

The UserCalc Python package implements both equilibrium and reactive disequilibrium transport models and provides a set of code classes and utility functions for calculating and visualizing the results of one-dimensional, steady-state, partial melting forward models for both the 238U and 235U decay chains. The code package is organized into a set of Python classes and plotting routines, which are documented in the docstrings of the classes and also demonstrated in detail below. Here we briefly describe the overall functionality and design of the code, which is open-source and can be modified to suit an individual researcher’s needs. The code is currently available in a Git repository (https://gitlab.com/ENKI-portal/pyUsercalc), and any future edits or merge requests will be managed through GitLab.

The equilibrium and disequilibrium transport models described above have each been implemented as Python classes with a generic code interface:

'''
Interface:
----------
    model(alpha0,lambdas,D,W0,F,dFdz,phi,rho_f=2800.,rho_s=3300.,method=method,Da=inf)

Parameters:
-----------
    alpha0  :  numpy array of initial activities
    lambdas :  numpy array of decay constants scaled by solid transport time
    D       :  Function D(z) -- returns an array of partition coefficients at scaled height z
    W0      :  float -- Solid mantle upwelling rate
    F       :  Function F(z) -- returns the degree of melting F  
    dFdz    :  Function dFdz(z) --  returns the derivative of F  
    phi     :  Function phi(z) --  returns the porosity  
    rho_f   :  float -- melt density
    rho_s   :  float -- solid density
    method  :  string -- ODE time-stepping scheme to be passed to solve_ivp (one of 'RK45', 'Radau', 'BDF')
    Da      :  float -- Damköhler Number (defaults to \inf, unused in equilibrium model)

Required Method:
----------------
        model.solve(): returns depth and log concentration numpy arrays  z, Us, Uf
'''

which solves the scaled equations (i.e., equation (9) or equations (53) and (54)) for the log concentrations of nuclides UisU_i^s and UifU_i^f in a decay chain of arbitrary length, with scaled decay constants λi\lambda^\prime_i and initial activity ratios αi0\alpha_i^0. In the code, we use the variable zz for the scaled height in the column (i.e. zζz\equiv \zeta), and the model equations assume a one-dimensional column with scaled height 0z10\leq z \leq 1. The bulk partition coefficients Di(z)D_i(z), degree of melting F(z)F(z), melting rate dF/dz(z)dF/dz(z), and porosity ϕ(z)\phi(z) are provided as functions of height in the column. Optional arguments include the melt and solid densities ρf\rho_f and ρs\rho_s, the Damköhler number DaDa, and the preferred numerical integration method (see scipy.integrate.solve_ivp). Some of these variables, such as Di(z)D_i(z) and F(z)F(z), are provided by the user as described further below, and are then interpolated using model functions.

UserCalc provides two separate model classes, EquilTransport and DisequilTransport, for the different transport models; the user could add any other model that uses the same interface, if desired. Most users, however, will not access the models directly but rather through the driver class UserCalc.UserCalc, which provides support for solving and visualizing column models for the relevant 238U^{238}U and 235U^{235}U decay chains. The general interface for the UserCalc class is:

''' A class for constructing solutions for 1-D, steady-state, open-system  U-series transport calculations
        as in Spiegelman (2000) and Elkins and Spiegelman (2021).

    Usage:
    ------
     
        us = UserCalc(df,dPdz = 0.32373,n = 2.,tol=1.e-6,phi0 = 0.008,
                  W0 =3.,model=EquilTransport,Da=None,stable=False,method='Radau')

    Parameters:
    -----------
        df     : A pandas dataframe with columns ['P','F', Kr','DU','DTh','DRa','DPa']
        dPdz   : float -- Pressure gradient, to convert pressure P to depth z
        n      : float -- Permeability exponent
        tol    : float -- Tolerance for the ODE solver
        phi0   : float -- Reference melt porosity
        W0     : float -- Upwelling velocity (cm/yr)
        model  : class -- A U-series transport model class (one of EquilTransport or DisequilTransport)
        Da     : float -- Optional Da number for disequilibrium transport model
        stable : bool
             True:  calculates concentrations for non-radiogenic nuclides with same chemical properties 
                    (i.e. sets lambda=0)
             False: calculates the full radiogenic problem
        method: string
                ODE time-stepping method to pass to solve_ivp 
                      (usually one of 'Radau', 'BDF', or 'RK45')
'''

The principal required data input is a spreadsheet containing the degree of melting F(P)F(P), relative permeability Kr(P)K_r(P), and bulk partition coefficients for the elements DUD_U, DThD_{Th}, DRaD_{Ra} and DPaD_{Pa} as functions of pressure PP. The structure of the input data spreadsheet is the same as that described in Spiegelman (2000), and is illustrated in Table 2 below. Because the user provides F(z)F(z), Kr(z)K_r(z), and bulk solid Di(z)D_i(z) input information to the model directly, any considerations such as mineral modes, mineral/melt DiD_i values, and productivity variations are external to this model and must be developed by the user separately. Once given this input spreadsheet by the user, the code routine initializes the decay constants for the isotopic decay chains and provides functions to interpolate F(z)F(z) and Di(z)D_i(z) and calculate the porosity ϕ(z)\phi(z). Once thus initialized, the UserCalc class further provides the following methods:

'''           
    Principal Methods:
    --------
        phi                   :  returns porosity as a function of column height
        set_column_parameters :  resets principal column parameters phi0, n, W0
        solve_1D              :  1D column solution for a single Decay chain 
                                    with arbitrary D, lambda, alpha_0
        solve_all_1D          : Solves a single column model for both 238U and 235U chains.  
                                    returns a pandas dataframe
        solve_grid            : Solves multiple column models for a grid of porosities and upwelling rates
                                    returns a 3-D array of activity ratios    
'''

Of these, the principal user-facing methods are:

  • UserCalc.solve_all_1D, which returns a pandas.Dataframe table that contains, at each depth, solutions for the porosity (ϕ), the log concentrations of the specified nuclides in the 238U^{238}U and 235U^{235}U decay chains in both the melt and the solid, and the U-series activity ratios.
  • UserCalc.solve_grid, which solves for a grid of one-dimensional solutions for different reference porosities (phi0phi_0) and solid upwelling rates (W0W_0) and returns arrays of U-series activity ratios at a specified depth (usually the top of the column), as described in Spiegelman & Elliott (1993).

3.1.1Visualization Functions

In addition to the principal classes for calculating U-series activity ratios in partial melts, the UserCalc package also provides functions for visualizing model inputs and outputs. The primary plotting functions include:

  • UserCalc.plot_inputs(df): Visualizes the input dataframe to show F(P)F(P), Kr(P)K_r(P), and Di(P)D_i(P).
  • UserCalc.plot_1Dcolumn(df): Visualizes the output dataframe for a single one-dimensional melting column.
  • UserCalc.plot_contours(phi0,W0,act): Visualizes the output of UserCalc.solve_grid by generating contour plots of activity ratios at a specific depth as functions of the porosity (ϕ0\phi_0) and solid upwelling rate (W0W_0).
  • UserCalc.plot_mesh_Ra(Th,Ra,W0,phi0) and UserCalc.plot_mesh_Pa(Th,Pa,W0,phi0): Generates ‘mesh’ plots showing results for different ϕ0\phi_0 and W0W_0 values on (226Ra/230Th) vs. (230Th/238U) and (231Pa/235U) vs. (230Th/238U) activity diagrams.

Both the primary solver routines and visualization routines will be demonstrated in detail below.

3.1.2Miscellaneous Convenience Functions

Finally, the UserCalc module also provides a simple input spreadsheet generator similar to the one provided with the original UserCalc program of Spiegelman (2000). This tool is described more fully in the accompanying Jupyter notebook twolayermodel.ipynb in the Supplemental Materials, and has the interface:

  • df = UserCalc.twolayermodel(P, F, Kr, D_lower, D_upper, N=100, P_lambda=1)

3.2An example demonstrating pyUserCalc functionality for a single melting column

The Python code cells embedded below provide an example problem that demonstrates the use and behavior of the model for a simple, two-layer upwelling mantle column, with a constant melting rate within each layer and constant Kr=1K_r=1. This example is used to compare the outcomes from the original UserCalc equilibrium model Spiegelman, 2000 to various other implementations of the code, such as pure disequilibrium transport and scaled reactivity rates, as described above.

To run the example code and use this article as a functioning Jupyter notebook, while in a web-enabled browser the user should select each embedded code cell below by mouse-click and then simultaneously type the ‘Shift’ and ‘Enter’ keys to run the cell, after which selection will automatically advance to the following cell. The first cell below imports necessary code libraries to access the Python toolboxes and functions that will be used in the rest of the program:

# Select this cell by mouse click, and run the code by simultaneously typing the 'Shift' + 'Enter' keys.
# If the browser is able to run the Jupyter notebook, a number [1] will appear to the left of the cell.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# Import UserCalc:
import UserCalc

3.2.1Entering initial input information and viewing input data

In the full Jupyter notebook code available in the Git repository and provided here as Supplementary Materials, the user can edit a notebook copy and indicate their initial input data, as has been done for the sample data set below. The name for the user’s input data file should be set in quotes (i.e., replacing the word ‘sample’ in the cell below with the appropriate filename, minus the file extension). This name will be used both to find the input file and to label any output files produced. Our sample file can likewise be downloaded and used as a formatting template for other input files (see Supplementary Materials), and is presented as a useful example below. The desired input file should be saved to a ‘data’ folder in the notebook directory prior to running the code. If desired, a similarly simple two-layer input file can also be generated using the calculator tool provided in the supplementary code.

Once the cell has been edited to contain the correct input file name, the user should run the cell using the technique described above:

runname='sample'

The next cell below will read in the input data using the user filename specified above:

Table 2:Input data table for example tested here, showing pressures in kbar (PP), degree of melting (FF), permeability coefficient (KrK_r), and bulk solid/melt partition coefficients (DiD_i) for the elements of interest, U, Th, Ra, and Pa. This table illustrates the format required for input files for this model.

input_file = 'data/{}.csv'.format(runname)
df = pd.read_csv(input_file,skiprows=1,dtype=float)
df

The next cell will visualize the input dataframe in Figure 1, using the utility function plot_inputs:

fig = UserCalc.plot_inputs(df)
<Figure size 800x600 with 3 Axes>

Figure 1:Diagrams showing example input parameters FF, KrK_r, and DiD_i as a function of pressure, for the sample input file tested here.

3.2.2Single column equilibrium transport model

In its default mode, UserCalc solves the one-dimensional steady-state equilibrium transport model described in Spiegelman (2000). Below we will initialize the model, solve for a single column and plot the results.

First we set the physical parameters for the upwelling column and initial conditions:

# Maximum melt porosity:
phi0 = 0.008

# Solid upwelling rate in cm/yr. (to be converted to km/yr. in the driver function):
W0 = 3.

# Permeability exponent:
n = 2.

# Solid and liquid densities in kg/m3:
rho_s = 3300.
rho_f = 2800.

# Initial activity values (default is 1.0):
alpha0_238U = 1.
alpha0_235U = 1.
alpha0_230Th = 1.
alpha0_226Ra = 1.
alpha0_231Pa = 1.
alpha0_all = np.array([alpha0_238U, alpha0_230Th, alpha0_226Ra, alpha0_235U, alpha0_231Pa])

Next, we initialize the default equilibrium model:

us_eq = UserCalc.UserCalc(df)

and run the model for the input code and display the results for the final predicted melt composition in List 1:

df_out_eq = us_eq.solve_all_1D(phi0,n,W0,alpha0_all)
df_out_eq.tail(n=1)

List 1:Model output results for the equilibrium melting scenario tested above.

The cell below produces Figure 2, which shows the model results with depth:

fig = UserCalc.plot_1Dcolumn(df_out_eq)
<Figure size 800x600 with 3 Axes>

Figure 2:Equilibrium model output results for the degree of melting, residual melt porosity, and activity ratios (230Th/238U), (226Ra/230Th), and (231Pa/235U) as a function of pressure.

3.2.3Single column disequilibrium transport model

For comparison, we can repeat the calculation using the disequilibrium transport model, and compare the results to the equilibrium model. We first initialize a new model with Da=0Da=0, which will calculate full disequilibrium transport:

us_diseq = UserCalc.UserCalc(df, model=UserCalc.DisequilTransport, Da=0.)

The cells below calculate solutions for this pure disequilibrium scenario, as shown in List 2:

df_out = us_diseq.solve_all_1D(phi0,n,W0,alpha0_all)
df_out.tail(n=1)

List 2:Model output results for the disequilibrium melting scenario tested above.

Next we compare the results to our equilibrium calculation above:

fig, axes = UserCalc.plot_1Dcolumn(df_out)
axes[2].set_prop_cycle(None)
for s in ['(230Th/238U)','(226Ra/230Th)','(231Pa/235U)']:
        axes[2].plot(df_out_eq[s],df_out['P'],'-')
        axes[2].plot(df_out_eq[s],df_out['P'],'.',color='grey')

axes[2].set_title('Da = {}'.format(us_diseq.Da))
plt.show()
<Figure size 800x600 with 3 Axes>

Figure 3:Disequilibrium model output results for the degree of melting, residual melt porosity, and activity ratios (230Th/238U), (226Ra/230Th), and (231Pa/235U) as a function of pressure, for the Damköhler number shown (Da=0Da = 0). For comparison, the curves with gray dots show solutions for the equilibrium transport model.

The dashed grey curves in Figure 3 illustrate the equilibrium transport solution, which is significantly different from the disequilibrium solution. If we increase the value of DaDa, however, the disequilibrium transport solution should converge towards the equilibrium scenario. To illustrate this, below we calculate the result for Da=1Da=1:

# Reset the Da number in the reactive transport model to 1:
us_diseq.Da=1.

# Recalculate the model:
df_out = us_diseq.solve_all_1D(phi0,n,W0,alpha0_all)
df_out.tail(n=1)

List 3:Model output results for the disequilibrium melting scenario tested above, where Da=1Da = 1.

fig, axes = UserCalc.plot_1Dcolumn(df_out)
axes[2].set_prop_cycle(None)
for s in ['(230Th/238U)','(226Ra/230Th)','(231Pa/235U)']:
    axes[2].plot(df_out_eq[s],df_out['P'],'-')
    axes[2].plot(df_out_eq[s],df_out['P'],'.',color='grey')
axes[2].set_title('Da = {}'.format(us_diseq.Da))
plt.show()
<Figure size 800x600 with 3 Axes>

Figure 4:Disequilibrium model output as in Figure 3, but for Da=1Da = 1.

The outcome of the above calculation (Figure 4, List 3) approaches the equilibrium scenario more closely, as predicted. Below is an additional comparison for Da=10Da=10:

# Reset the Da number in the reactive transport model to 10:
us_diseq.Da=10.

# Recalculate and plot the model:
df_out = us_diseq.solve_all_1D(phi0,n,W0,alpha0_all)

fig, axes = UserCalc.plot_1Dcolumn(df_out)
axes[2].set_prop_cycle(None)
for s in ['(230Th/238U)','(226Ra/230Th)','(231Pa/235U)']:
        axes[2].plot(df_out_eq[s],df_out['P'],'-')
        axes[2].plot(df_out_eq[s],df_out['P'],'.',color='grey')

axes[2].set_title('Da = {}'.format(us_diseq.Da))
plt.show()
<Figure size 800x600 with 3 Axes>

Figure 5:Disequilibrium model output as in Figure 3, but for Da=10Da = 10.

For Da=10Da=10 (Figure 5), the activity ratios in the melt are indistinguishable from the equilibrium calculation, suggesting that a Damköhler number of 10 is sufficiently high for a melting system to approach chemical equilibrium, and illustrating that the equilibrium model of Spiegelman & Elliott (1993) and Spiegelman (2000) is the limiting case for the more general disequilibrium model presented here. For this problem, equilibrium transport always provides an upper bound on activity ratios.

3.2.4Stable element concentrations

For a stable element, i.e., λi=0\lambda_i = 0, Spiegelman & Elliott (1993) showed that the equilibrium melting model reduces identically to simple batch melting Shaw, 1970, while the disequilibrium model with Da=0Da=0 is equivalent to true fractional melting. This presents a useful test of the calculator that verifies the program is correctly calculating stable concentrations. To simulate stable element concentrations for U, Th, Ra, and Pa during equilibrium melting, we can use the same input file example as above and simply test the scenario where λi\lambda_i values are equal to zero.

First, we impose a “stable” condition that changes all decay constants λi=0\lambda_i = 0:

us_eq = UserCalc.UserCalc(df,stable=True)
df_out_eq = us_eq.solve_all_1D(phi0,n,W0,alpha0_all)
df_out_eq.tail(n=1)

List 4:Model output results for equilibrium porous flow melting where λi=0\lambda_i = 0, simulating stable element behavior for U, Th, Ra, and Pa and thus true (instantaneous) batch melting.

For comparison with the results in List 4, we can use the batch melting equation Shaw, 1970 to calculate the concentrations of U, Th, Ra, and Pa using the input values in Table 2 for F(z)F(z) and DiD_i, where:

cifci0=1F+Di(1F)\frac{c_i^f}{c_i^0} = \frac{1}{F + D_i(1-F)}

and determine radionuclide activities for the batch melt using the definition of the activity aa for a nuclide ii:

ai=λicifa_i = \lambda_i c_i^f

and the initial nuclide activities ai0a_i^0, such that:

ai=ai0F+Di(1F)a_i = \frac{a_i^0}{F + D_i (1 - F)}

As the activity ratios in List 5 illustrate, the outcomes of this simple batch melting equation are identical to those produced by the model for equilibrium transport and λ=0\lambda=0.

df_batch=df[['P','F','DU','DTh','DRa','DPa']]
df_batch['(230Th/238U)'] = (alpha0_all[1]/(df_batch.F-df_batch.F*df_batch.DTh+df_batch.DTh))/(alpha0_all[0]/(df_batch.F-df_batch.F*df_batch.DU+df_batch.DU))
df_batch['(226Ra/230Th)'] = (alpha0_all[2]/(df_batch.F-df_batch.F*df_batch.DRa+df_batch.DRa))/(alpha0_all[1]/(df_batch.F-df_batch.F*df_batch.DTh+df_batch.DTh))
df_batch['(231Pa/235U)'] = (alpha0_all[4]/(df_batch.F-df_batch.F*df_batch.DPa+df_batch.DPa))/(alpha0_all[3]/(df_batch.F-df_batch.F*df_batch.DU+df_batch.DU))

# Extract columns and concatenate dataframes
cols = ['P', 'F', '(230Th/238U)', '(226Ra/230Th)', '(231Pa/235U)']
df_compare = pd.concat([ df_batch[cols].tail(1), df_out_eq[cols].tail(1)])
df_compare['model'] = ['Batch Melting', 'Equilibrium Transport: stable elements']
df_compare.set_index('model')

List 5:Simple batch melting calculation results using the methods of Shaw (1970), demonstrating identical activity ratio results to those calculated using the equilibrium transport model with λi=0\lambda_i=0.

Similarly, we can also determine pure disequilibrium melting using the disequilibrium transport model with λi=0\lambda_i=0. A simple fractional melting problem is easiest to test using constant melt productivity and partitioning behavior, so here we test a simplified, one-layer scenario with constant dF/dzdF/dz and DiD_i values:

input_file_2 = 'data/simple_sample.csv'
df_test = pd.read_csv(input_file_2,skiprows=1,dtype=float)
UserCalc.plot_inputs(df_test)
df_test.tail(n=1)

Figure 6:Simple alternative input file with constant melt productivity and constant solid/melt partitioning, used here to test pure fractional melting outputs.

We note that numerical ordinary differential equation (ODE) solvers may not successfully solve for pure fractional melting with Da=0Da=0 and stable elements, because the resulting extreme changes in solid concentrations for highly incompatible elements are difficult to resolve using numerical methods. Stable solutions can nonetheless be obtained for very small values of DaDa that approach Da=0Da=0, and such solutions still provide a useful test of the disequilibrium transport model. Here we use Da=1010Da=10^{-10}; for such low DaDa values, the liquid closely approaches the composition of an accumulated fractional melt, and although the liquid and solid outcomes are slightly different from pure fractional melting, the solid is still essentially depleted of all incompatible nuclides.

us_diseq_test = UserCalc.UserCalc(df_test, model=UserCalc.DisequilTransport,stable=True,Da=1.e-10)
df_diseq_test = us_diseq_test.solve_all_1D(phi0,n,W0,alpha0_all)

Similar to our approach for equilibrium and batch melting, we can compare the results of disequilibrium transport for stable elements with pure fractional melting for constant partition coefficients using the definition of aggregated fractional melt concentrations (Figure 7):

ciscis,0=(1F)1/Di1\frac{c_i^s}{c_i^{s,0}} = (1 - F)^{1/D_i-1}
cifcif,0=DiF(1(1F)1/Di)\frac{c_i^f}{c_i^{f,0}} = \frac{D_i}{F} \left(1 - (1 - F)^{1/D_i}\right)

or in log units:

Uis=(1/Di1)log(1F)U_i^s = (1/D_i-1)\log(1 - F)
Uif=log(1(1F)1/Di)+log(DiF)U_i^f = \log\left(1 - (1 - F)^{1/D_i}\right) + \log\left(\frac{D_i}{F}\right)
df_frac=df_test[['P','F','DU','DTh','DRa','DPa']]
df_frac['(230Th/238U)'] = ((alpha0_all[1]/df_frac.F)*(1.-(1.-df_frac.F)**(1./df_frac.DTh))) / ((alpha0_all[0]/df_frac.F)*(1.-(1.-df_frac.F)**(1./df_frac.DU)))
df_frac['(226Ra/230Th)'] = ((alpha0_all[2]/df_frac.F)*(1.-(1.-df_frac.F)**(1./df_frac.DRa))) / ((alpha0_all[1]/df_frac.F)*(1.-(1.-df_frac.F)**(1./df_frac.DTh)))
df_frac['(231Pa/235U)'] = ((alpha0_all[4]/df_frac.F)*(1.-(1.-df_frac.F)**(1./df_frac.DPa))) / ((alpha0_all[3]/df_frac.F)*(1.-(1.-df_frac.F)**(1./df_frac.DU)))
fig, axes = UserCalc.plot_1Dcolumn(df_diseq_test)
for s in ['(230Th/238U)','(226Ra/230Th)','(231Pa/235U)']:
        axes[2].plot(df_frac[s],df_diseq_test['P'],'--',color='black')
plt.show()
<Figure size 800x600 with 3 Axes>

Figure 7:Model output results for the degree of melting, residual melt porosity, and activity ratios (230Th/238U), (226Ra/230Th), and (231Pa/235U) as a function of pressure. The solid curves plot the results of pure fractional melting for stable elements, while the dashed black curves illustrate the outcomes of the disequilibrium transport model with Da=1010Da=10^{-10} and λi=0\lambda_i=0. The outcomes of the two methods are indistinguishable.

3.2.5Considering lithospheric transport scenarios

In mantle decompression melting scenarios, melting is expected to cease in the shallow, colder part of the regime where a lithospheric layer is present. The effects of cessation of melting prior to reaching the surface can be envisioned as affecting magma compositions in a number of ways, some of which could be calculated using the models presented here by setting dF=0dF=0.

There are, however, several limitations when using our transport models to simulate lithospheric melt transport in this way, as the model equations are written to track steady-state decompression and melting. The first limitation is thus the underlying assumption that the solid is migrating and experiences progressive melt depletion in the model, while the solid lithosphere should in fact behave as a rigid matrix that does not experience upwelling. For the disequilibrium transport model with Da=0Da=0, no chemical reequilibration occurs while dF=0dF=0, so the lack of solid migration after the cessation of melting does not pose a problem; instead, in the pure disequilibrium transport case, imposing dF=0dF=0 simply allows for radioactive decay and ingrowth during transport through the lithospheric layer.

The equilibrium transport model, on the other hand, permits full equilibration even if dF=0dF=0, but the liquid composition does not directly depend on the solid concentration, cis(z)c_i^s(z), so ongoing chemical reequilibration between the liquid and a modified lithospheric solid could be simulated by modifying the bulk solid/liquid partition coefficients DiD_i. However, the underlying model assumes that the liquid with mass proportion FmaxF_{max} reequilibrates with the solid matrix in a steady-state transport regime, at the maximum reference porosity, which may not accurately simulate the transport regime through the fixed lithosphere with no melting. Because it does not directly consider mineral abundances or compositions, the model also does not account for complexities such as low temperature melt-rock reaction or mineral growth.

The case of the scaled disequilibrium transport model with Da>0Da>0 is the most complex, since the model directly calculates reequilibration of the liquid with a progressively melting solid layer, and thus may not accurately simulate transport through the fixed solid lithosphere. We advise that if the model is used in this way, the results must be interpreted with additional caution.

Finally, calculating a given transport model through the upwelling asthenosphere and into a fixed overlying lithospheric layer neglects an additional, significant limitation: namely that melt-rock interactions, and thus the magma transport style, may be different in the lithosphere than in the melting asthenosphere. As noted above, this could also include low-temperature reactions and the growth of new mineral phases. While it is not possible to change transport models during a single 1D run in the current implementation, one alternative approach is to change the relative permeability, KrK_r, in the lithosphere, in addition to modifying the bulk partition coefficients to reflect lithospheric values. It may also be possible to run a separate, second-stage lithospheric calculation with modified input parameters and revised liquid porosity constraints, but this option is not currently implemented and would require an expansion of the current model.

Despite these caveats, there are some limited scenarios where users may wish to simulate equilibrium or disequilibrium magma transport through a capping layer with constant dF=0dF=0, constant ϕ=ϕ0\phi = \phi_0, and revised DiD_i values for a modified layer mineralogy. The cells below provide options for modifying the existing input data table to impose such a layer. The first cell identifies a final melting pressure PLithosP_{Lithos}, which is defined by the user in kbar. This value can be set to 0.0 if no lithospheric cap is desired; in the example below, it has been set at 5.0 kbar. There are two overall options for how this final melting pressure could be used to modify the input data. One option (implemented in the Supplementary Materials but not tested here) simply deletes all lines in the input dataframe for depths shallower than PLithosP_{Lithos}. This is a straightforward option for a one-dimensional column scenario, where melting simply stops at the base of the lithosphere and the composition of the melt product is observed in that position. This is an effective way to limit further chemical interactions after melting has ceased; it fails to account for additional radioactive decay during lithospheric melt transport, but subsequent isotopic decay over a fixed transport time interval could then be calculated using the radioactive decay equations for U-series nuclides.

A second option, shown here to demonstrate outcomes, changes the degree of melting increments (dFdF) to a value of 0 for all depths shallower than PLithosP_{Lithos}, but allows model calculations to continue at shallower depths. This is preferable if the user aims to track additional radioactive decay and/or chemical exchange after melting has ceased and during subsequent transport through the lithospheric layer, and shall be explored further below.

Plithos = 5.0

Pfinal = df.iloc[(df['P']-Plithos).abs().idxmin()]
F_max = Pfinal[1].tolist()
df.loc[(df['P'] < Plithos),['F']] = F_max

For equilibrium transport scenarios, the cell below offers one possible option for modifying lithospheric solid/melt bulk partition coefficients. We note that if the disequilibrium transport model is used with Da=0Da = 0 (i.e., pure chemical disequilibrium), this cell is not necessary.

The option demonstrated below imposes new, constant melt-rock partition coefficients during lithospheric transport. These values are assumed to be fixed. An alternative choice, included in the Supplementary Materials, instead fixes the shallower lithospheric solid/melt bulk partition coefficients such that they are equal to DiD_i values at the depth where melting ceased (i.e., PLithosP_{Lithos}).

# Define new bulk solid/liquid partition coefficients for the lithospheric layer:
D_U_lith = 0.002
D_Th_lith = 0.006
D_Ra_lith = 0.00002
D_Pa_lith = 0.00001

# Implement the changed values defined above:
df.loc[(df['P'] < Plithos),['DU']] = D_U_lith
df.loc[(df['P'] < Plithos),['DTh']] = D_Th_lith
df.loc[(df['P'] < Plithos),['DRa']] = D_Ra_lith
df.loc[(df['P'] < Plithos),['DPa']] = D_Pa_lith

Following any changes implemented above, the cells below will process and display the refined input data (Figure 8, Table 3).

fig=UserCalc.plot_inputs(df)
<Figure size 800x600 with 3 Axes>

Figure 8:Diagrams showing input parameters FF, KrK_r, and DiD_i as a function of pressure, for the example input file and modified lithospheric conditions.

Table 3:Input data table for an example scenario with modified lithospheric transport conditions, showing pressures in kbar (PP), degree of melting (FF), permeability coefficient (KrK_r), and bulk solid/melt partition coefficients (DiD_i) for the elements of interest, U, Th, Ra, and Pa.

df

The cells below will rerun the end member models for the modified lithospheric input file. First, for equilibrium transport:

us_eq = UserCalc.UserCalc(df,stable=False)
df_out_eq = us_eq.solve_all_1D(phi0,n,W0,alpha0_all)

And second, for disequilibrium transport with Da=0Da=0:

us_diseq = UserCalc.UserCalc(df,model=UserCalc.DisequilTransport,Da=0,stable=False)
df_out_diseq = us_diseq.solve_all_1D(phi0,n,W0,alpha0_all)

List 6 below displays the activity ratios determined for the final melt compositions at the end of the two simulations (i.e., the tops of the one-dimensional melting columns).

df_compare = pd.concat([df_out_eq.tail(n=1), df_out_diseq.tail(n=1)])
df_compare['model'] = ['Equilibrium Transport', 'Disequilbrium Transport']
df_compare.set_index('model')

List 6:Model output results for the disequilibrium (Da=0Da = 0) melting scenarios tested here, with modified lithospheric input conditions.

The following cell generates Figure 9, which illustrates outcomes with depth for the equilibrium and disequilibrium transport models. The model outcomes for the two transport scenarios are notably different, particularly for the shorter-lived isotopic pairs.

fig, axes = UserCalc.plot_1Dcolumn(df_out_diseq)
axes[2].set_prop_cycle(None)
for s in ['(230Th/238U)','(226Ra/230Th)','(231Pa/235U)']:
        axes[2].plot(df_out_eq[s],df_out['P'],'--')
axes[2].set_title('Da = {}'.format(us_diseq.Da))
plt.show()
<Figure size 800x600 with 3 Axes>

Figure 9:Comparison of equilibrium (dashed) and disequilibrium (Da=0Da = 0; solid) transport model output results for the degree of melting, residual melt porosity, and activity ratios (230Th/238U), (226Ra/230Th), and (231Pa/235U) as a function of pressure, for the modified lithospheric transport scenario explored above. Symbols and lines as in Figure 3.

3.3Batch operations

For many applications, it is preferable to calculate an ensemble of model scenarios over a range of input parameters directly related to questions about the physical constraints on melt generation, such as the maximum residual or reference melt porosity (ϕ0\phi_0) and the solid mantle upwelling rate (W0W_0). The cells below determine a series of one-dimensional column results for the equilibrium transport model and the parameters defined above (that is, the input conditions shown in Table 3 with n=2n = 2, ρs=3300\rho_s = 3300 kg/m3, and ρf=2800\rho_f = 2800 kg/m3), but over a range of values for ϕ0\phi_0 and W0W_0; these results are then shown in a series of figures. The user can select whether to define the specific ϕ0\phi_0 and W0W_0 values as evenly spaced log grid intervals (Option 1) or with manually specified values (Option 2). As above, all upwelling rates are entered in units of cm/yr. We note that because some of these models tend to be stiff and the Radau ODE solver is relatively computationally expensive, the batch operations below may require a few minutes of computation time for certain scenarios. Here we show the results for the default equilibrium model over a range of selected ϕ0\phi_0 and W0W_0 values of interest:

# Option 1 (evenly spaced log grid intervals):
# phi0 = np.logspace(-3,-2,11)
# W0 = np.logspace(-1,1,11)
# Option 2 (manual selection of values):
phi0 = np.array([0.001, 0.002, 0.005, 0.01])
W0 = np.array([0.5, 1., 2., 5., 10., 20., 50.])
import time
tic = time.perf_counter()
toc = time.perf_counter()

# Calculate the U-238 decay chain grid values:
act = us_eq.solve_grid(phi0, n, W0, us_eq.D_238, us_eq.lambdas_238, us_eq.alphas_238)
Th = act[0]
Ra = act[1]
df = pd.DataFrame(Th)
df = pd.DataFrame(Ra)

W = 0.5 . . . . 
W = 1.0 . . . . 
W = 2.0 . . . . 
W = 5.0 . . . . 
W = 10.0 . . . . 
W = 20.0 . . . . 
W = 50.0 . . . . 
# Calculate the U-235 decay chain grid values:
act_235 = us_eq.solve_grid(phi0, n, W0, us_eq.D_235, us_eq.lambdas_235, us_eq.alphas_235)
Pa = act_235[0]
df = pd.DataFrame(Pa)

W = 0.5 . . . . 
W = 1.0 . . . . 
W = 2.0 . . . . 
W = 5.0 . . . . 
W = 10.0 . . . . 
W = 20.0 . . . . 
W = 50.0 . . . . 

The figures below illustrate the batch model results in a variety of ways. First, each isotopic activity ratio is contoured in ϕ0\phi_0 vs. W0W_0 space (Figure 10), using figures similar to the contour plots of Spiegelman (2000). The model outcomes for W0W_0 and ϕ0\phi_0 values are also contoured as mesh “grids” in activity ratio-activity ratio plots (Figure 11). These diagrams show the outcomes for model runs with a given W0W_0 and ϕ0\phi_0 value at each grid intersection point, and each curve shows outcomes for a constant W0W_0 value with variable ϕ0\phi_0 or vice versa, as indicated in the figure legend. Because this particular example shows results for the equilibrium transport model, and the input values for the shallow, spinel peridotite layer of the sample input file define DUD_U < DThD_{Th}, we note that some of the results exhibit (230Th/238U) < 1.0 in Figure 11.

UserCalc.plot_contours(phi0,W0,act, figsize=(8,10))
<Figure size 1600x1000 with 4 Axes>

Figure 10:Diagrams of upwelling rate (W0W_0) vs. maximum residual melt porosity (ϕ) showing contoured activity ratios for (230Th/238U) (top panel), (226Ra/230Th) (middle panel), and (231Pa/235U) (bottom panel).

UserCalc.plot_contours(phi0,W0,act_235,figsize=(8,10))
<Figure size 800x1000 with 2 Axes>
UserCalc.plot_mesh_Ra(Th,Ra,W0,phi0)
<Figure size 640x480 with 1 Axes>

Figure 11:Diagrams showing (226Ra/230Th) vs. (230Th/238U) (top) and (231Pa/235U) vs. (230Th/238U) (bottom) for the gridded upwelling rate (W0W_0) and maximum residual porosity (ϕ) values defined above.

UserCalc.plot_mesh_Pa(Th,Pa,W0,phi0)
<Figure size 640x480 with 1 Axes>

4Summary

We present pyUserCalc, an expanded, publicly available, open-source version of the UserCalc code for determining U-series disequilibria generated in basalts by one-dimensional, decompression partial melting. The model has been developed from conservation of mass equations with two-phase (solid and liquid) porous flow and permeability governed by Darcy’s Law. The model reproduces the functionality of the original UserCalc equilibrium porous flow calculator Spiegelman, 2000 in pure Python code, and implements a new disequilibrium transport model. The disequilibrium transport code includes reactivity rate-limited chemical equilibration calculations controlled by a Damköhler number, DaDa. For stable elements with decay constants equal to zero, the equilibrium model reduces to batch melting and the disequilibrium transport model with DaDa = 0 to pure fractional melting. The method presented here can be extended to other applications in geochemical porous flow calculations in future work.

Acknowledgments

We thank three anonymous reviewers for thoughtful feedback that strengthened this manuscript. We also thank K.W.W. Sims and P. Kelemen for initiating early discussions about creating a new porous flow disequilibrium transport calculator back in 2008, and M. Ghiorso for inviting L. Elkins to join the ENKI working group and thereby catalyzing this fresh effort. We further thank the ENKI working group for their helpful suggestions and feedback. L. Elkins received ENKI working group travel assistance that contributed to this research effort, and was supported by NSF award OCE-1658011. M. Spiegelman was supported by the ENKI NSF SI2 award NSF-ACI1550337. This work is dedicated to the memory of our colleague, mentor, and friend Peter Fox, who encouraged our work on this project, expertly handled this manuscript, and spearheaded exciting innovations like publishing executable code in Earth and Space Science.

Data Availability

The data set for this research consists of a code package, which is available in several ways: 1) in the supporting information, 2) through a binder container (at https://mybinder.org/v2/gl/ENKI-portal%2FpyUsercalc/master?filepath=pyUserCalc_manuscript.ipynb), and 3) in the ENKI GitLab data repository (https://gitlab.com/ENKI-portal/pyUsercalc), which can also be accessed at the ENKI cloud server (https://server.enki-portal.org/hub/login) with a free GitLab account (register at https://gitlab.com/ENKI-portal). The primary source for pyUserCalc is also hosted in the ENKI GitLab repository, and any future issues and merge requests will be handled there.

References
  1. McKenzie, D. (1985). Th-230-U-238 disequilibrium and the melting processes beneath ridge axes. Earth and Planetary Science Letters, 72(2–3), 149–157. 10.1016/0012-821x(85)90001-9
  2. Spiegelman, M., & Elliott, T. (1993). Consequences of melt transport for uranium series disequilibrium in young lavas. Earth and Planetary Science Letters, 118(1–4), 1–20. 10.1016/0012-821x(93)90155-3
  3. Spiegelman, M. (2000). UserCalc: A web-based uranium series calculator for magma migration problems. Geochemistry, Geophysics, Geosystems, 1(8), 1016. 10.1029/1999gc000030
  4. Elkins, L. J., & Spiegelman, M. (2021). pyUserCalc: A Revised Jupyter Notebook Calculator for Uranium‐Series Disequilibria in Basalts. Earth and Space Science, 8(12). 10.1029/2020ea001619
  5. Sims, K. W. W., DePaolo, D. J., Murrell, M. T., Baldridge, W. S., Goldstein, S., Clague, D., & Jull, M. (1999). Porosity of the melting zone and variations in the solid mantle upwelling rate beneath Hawaii: Inferences from U-238-Th-230-Ra-226 and U-235-Pa-231 disequilibria. Geochimica et Cosmochimica Acta, 63(23–24), 4119–4138. 10.1016/s0016-7037(99)00313-0
  6. Zou, H., & Zindler, A. (2000). Theoretical studies of 238U-230Th-226Ra and 235U-231Pa disequilibria in young lavas produced by mantle melting. Geochimica et Cosmochimica Acta, 64(10), 1809–1817. 10.1016/s0016-7037(00)00350-1
  7. Stracke, A., Zindler, A., Salters, V. J. M., McKenzie, D., & Gronvold, K. (2003). The dynamics of melting beneath Theistareykir, northern Iceland. Geochemistry, Geophysics, Geosystems, 4, 8513. 10.1029/2002gc000347
  8. Jull, M., Kelemen, P., & Sims, K. (2002). Consequences of diffuse and channelled porous melt migration on uranium series disequilibria. Geochimica et Cosmochimica Acta, 66, 4133–4148. 10.1016/s0016-7037(02)00984-5
  9. Lundstrom, C., Gill, J., & Williams, Q. (2000). A geochemically consistent hypothesis for MORB generation. Chemical Geology, 162(2), 105–126. 10.1016/s0009-2541(99)00122-9
  10. Sims, K. W. W., Goldstein, S. J., Blichert-toft, J., Perfit, M. R., Kelemen, P., Fornari, D. J., & others. (2002). Chemical and isotopic constraints on the generation and transport of magma beneath the East Pacific Rise. Geochimica et Cosmochimica Acta, 66(19), 3481–3504. 10.1016/s0016-7037(02)00909-2
  11. Bourdon, B., Turner, S. P., & Ribe, N. M. (2005). Partial melting and upwelling rates beneath the Azores from a U-series isotope perspective. Earth and Planetary Science Letters, 239, 42–56. 10.1016/j.epsl.2005.08.008
  12. Elkins, L. J., Bourdon, B., & Lambart, S. (2019). Testing pyroxenite versus peridotite sources for marine basalts using U-series isotopes. Lithos, 332–333, 226–244. 10.1016/j.lithos.2019.02.011
  13. Miller, K. J., Zhu, W. L., Montési, L. G., & Gaetani, G. A. (2014). Experimental quantification of permeability of partially molten mantle rock. Earth and Planetary Science Letters, 388, 273–282.
  14. Spiegelman, M., & Kenyon, P. (1992). The requirements for chemical disequilibrium during magma migration. Earth and Planetary Science Letters, 109(3–4), 611–620. 10.1016/0012-821x(92)90119-g
  15. Grose, C. J., & Afonso, J. C. (2019). Chemical disequilibria, lithospheric thickness, and the source of ocean island basalts. Journal of Petrology, 60(4), 755–790. 10.1093/petrology/egz012