grsett.f
273 lines · 1 definitions · on GitHub (7be4133)
Python version: grsett.py, 206 lines, on the right (switch to Python above the code).
Variables on this page (11)
c4pi, ecod1z_h, eqcod1z_h, ez, ez_h, grtol, iowr, ishort, json, ldifz, niter_gs
Purpose
GRSETT computes the gravitational settling of helium (and, with LDIFZ, of the metals) over one time step, "using the formalism of Bahcall and Loeb 1989" (its comment). The code solves for the divergence of a flux, in mass (as LAX_WENDROF1 differences it),
$$\frac{\partial X}{\partial t} = 4\pi\,\frac{\partial}{\partial m}\Bigl[D_1 + D_2\,\frac{\partial X}{\partial r}\Bigr],\qquad D_1 = f\,\frac{r^2T^{5/2}}{\ln\Lambda}\,X\,(A_P + A_T\nabla)\,\frac{d\ln P}{dr},\qquad D_2 = f\,\frac{r^2T^{5/2}}{\ln\Lambda}\,A_C,$$
where $r$ is in $R_\odot$ and $T$ in $10^7$ K (Bahcall and Loeb's units), $A_P$, $A_T$, $A_C$ are the coefficients of the pressure, temperature and concentration gradients (AP, AT/$\nabla$, AC, from Thoul et al. with LTHOUL; without it, Bahcall and Loeb's fits in $X$), $f$ the user's factor (below), and $D_1$, $D_2$ are COD1, COD2 from SETUP_GRSETT. The first term is advective (settling at a velocity proportional to $d\ln P/dr$), the second diffusive; the code's comment, a first term "that depends on DLNP/DR*DX/DR" and a second "on D^2X/DR^2," describes them only loosely. The factor $f$ is FGRY for helium and FGRZ for the metals (namelist settings, default 1), and with LTHOUL also CSTMIXING (and CSTDIFFMIX on the metals' settling term), factors added in 10/09 to "mimic mixing to reduce settling" (their $X$-derivatives are not scaled: setup_grsett.f, suspected bug 1). GRSETT treats the first term explicitly with a two-step Lax–Wendroff scheme and the second implicitly, iterating on the coefficients.
How it fits
- Who calls it:
MIXandROTMIX, the mixing drivers of the evolution step. - Inputs:
DT0(time step, s),HCOMP(mass fractions;HCOMP(1)= $X$,HCOMP(2)= $Y$,HCOMP(3)= $Z$),HQPR0= $d\ln P/dr$,HR= $\log r$,HD= $\log\rho$ (passed on toSETUP_GRSETT, which needs it for the Coulomb logarithm),HS10= $m$ (g, not logged),HT= $\log T$,LC(convective or not),M,STOT0.GRSETTcopiesDT0,STOT0,HS10andHQPR0into its ownDT,STOT,HS1, andHQPR, whichSETUP_GRSETTconverts to Bahcall and Loeb's units in place andEQUAL_TO_MODELconverts back, so the caller's values are not changed. - Output: the new
HCOMP(1)andHCOMP(2), and withLDIFZHCOMP(3)($Z$, the CNO rows scaled with it) (viaEQUAL_TO_MODEL, which uses $X+Y+Z=1$). - Calls, in order:
SETUP_GRSETT(the coefficientsCOD1,COD2and their $X$-derivatives, the convection-zone edges, and whether the model is fully convective),MODEL_TO_EQUAL(to an equally spaced grid in radius),LAX_WENDROF1andLAX_WENDROF2, then in each iterationGET_IMP_DIFFCOandTRIDIAG_GS, and at the endEQUAL_TO_MODEL. - Modules (COMMON blocks until
rp_1):NMLPAR_MODfor the settingsGRTOL,NITER_GS(formerly/GRAVST/, set byPARMIN) andLDIFZ(formerly/GRAVS3/);GRAVEZ_MOD(the metals on the equal grid, with the Lax–Wendroff pair and the regridding routines);CONSTANTS_MOD;IOUNITS_MOD(the output units). The metals' coefficientsCOD1Z,COD2Z,QCOD1Z,QCOD2Z(formerly/GRAVSZ/) are nowGRSETT's own arrays, filled bySETUP_GRSETTand passed toMODEL_TO_EQUAL.
The method
- Set up the coefficients; return at once for a fully convective model. Settling is computed only between the edge of a convective core (or the centre, or the first shell with $X >$
XMIN) and the base of the surface convection zone (or, if helium is too scarce there, the first shell below it with $Y >$YMIN):IBEGINandIEND. What lies beyond them is not on the grid; each side acts as one reservoir at the grid's end (the boundary cells ofLAX_WENDROF1hold all the mass beyond the grid), and every shell belowIBEGIN(a convective core, or a hydrogen-exhausted region, which may be radiative) and aboveIENDgets the change found at the grid's end point (step 5); for a convective zone this keeps it homogeneous. - Move to $N$ (
NPT) equally spaced radii $r_i$ (step $\Delta r$), with values at the points (E…) and at the midpoints (E…_H). Keep the starting $X_i^0$ (EX_ORIG). -
Explicit term.
LAX_WENDROF1gives the half-step changes $\Delta X_{i+1/2}$; the midpoint coefficients are linearized in them, $$F_{i+1/2} \leftarrow F_{i+1/2} + \Delta X_{i+1/2}\,\frac{\partial F}{\partial X}\Big|_{i+1/2},$$ andLAX_WENDROF2applies the full step, giving $X'_i$ (EX_PRIME). -
Implicit term. With $\alpha_i = 4\pi\Delta t/(\Delta r\,\Delta m_i)$ (
ALPHA; $\Delta m_i$ the mass between midpoints, $m_{1/2}$ at the centre and $M - m_{N-1/2}$ at the surface), iterate up toNITER_GStimes: take the change at the midpoints $\tfrac12(X_i + X_{i-1} - X^p_i - X^p_{i-1})$ since the previous iterate $X^p$; letGET_IMP_DIFFCObuild the tridiagonal coefficients $a_i, b_i, c_i$ fromECOD2_Hcorrected by that change; solve $$a_i X_{i-1} + b_i X_i + c_i X_{i+1} = X'_i$$ withTRIDIAG_GS. With $D_{i+1/2}$ =ECOD2_H(I)(the coefficient between points $i$ and $i+1$), the rows are those of an implicit (backward Euler) step: $$\bigl(1+\alpha_i(D_{i-1/2}+D_{i+1/2})\bigr)X_i - \alpha_i D_{i-1/2}X_{i-1} - \alpha_i D_{i+1/2}X_{i+1} = X'_i,$$ with $D_{1/2} = D_{N+1/2} = 0$ (no flux through the centre or the surface). Stop when $\max_i (X_i - X^p_i) <$GRTOL, the largest signed change (suspected bug 2); print each iteration's largest change. The change at the midpoints is stored one index off from the coefficients it corrects (suspected bug 1).
Design
Each iteration writes a line to the short output (ISHORT). The metals get the explicit term only; the implicit iteration is for $X$ alone.
From YREC's documentation
Physics Options: GRTOL (default 1.0D-8) is the numerical tolerance on the abundance changes from diffusion, and NITER_GS (10) the largest number of iterations of the solver.
Suspected bugs, checked by running the code
Each bug is marked by what running the code showed: (changes results, large) or (small), the fix moving a run clearly beyond a null change; (stops the run); (wrong output), the fix changing only what is written; (harmless in the runs), within the noise; (never occurred), the code ran but the fault did not; (not reached); (cannot be reached), a guard prevents it; (withdrawn), the suspicion was wrong.
1. (not reached) The coefficient correction is one midpoint off.
(lines 213–215↗; get_imp_diffco.f, lines 19–21↗) In the iteration, loop 30 sets EX_H(I), for I = 2..NPT, to the change at the midpoint between points I-1 and I; but GET_IMP_DIFFCO adds EQCOD2X_H(I)*EX_H(I) to ECOD2_H(I), the coefficient between points I and I+1 (its matrix uses ECOD2_H(I-1) on the left of point I and ECOD2_H(I) on the right, and MODEL_TO_EQUAL puts ECOD2_H(1) at ER(1) + $\Delta r/2$: model_to_equal.f, line 76↗). ECOD2_H(1) also gets EX_H(1), which loop 30 never sets: it keeps the half-step change from LAX_WENDROF1 (lax_wendrof1.f, lines 45–47↗), and is added again at every iteration. What follows: Each diffusion coefficient is corrected with its neighbour's change in $X$, and the first one drifts by the same amount per iteration; the size of the effect was not estimated. Only with LDIFY (off by default; set by LDIFZ and LDIFLI) and LNEWDIF false (the default, but LDIFLI sets it true).
A possible fix:
grsett.f
@@ lines 210–217 @@
DO 100 ITER=1,NITER_GS
C FIND CHANGE IN X AT THE ZONE MIDPOINTS, GIVEN CHANGE IN X AT
C THE ZONE CENTERS.
+C 11/09/26 KC via Claude
+C code changed to store the change at the midpoint between points I-1
+C and I in EX_H(I-1), the index of that midpoint's coefficient
+C ECOD2_H(I-1) in GET_IMP_DIFFCO.
DO 30 I = 2,NPT
- EX_H(I) = 0.5D0*(EX(I)+EX(I-1)-EX_P(I)-EX_P(I-1))
+ EX_H(I-1) = 0.5D0*(EX(I)+EX(I-1)-EX_P(I)-EX_P(I-1))
30 CONTINUE
C STORE CURRENT RUN OF HYDROGEN ABUNDANCES IN VECTOR EX_P. THE ITERATION
C LOOP IS COMPLETED ONCE EX - EX_P IS EVERYWHERE LESS THAN THE TOLERANCE
Checked by running YREC (Nov 9, 2026; a line added at each suspected path counted how often it was taken, the outputs otherwise unchanged): not reached in any of 34 runs (26 of YREC's cases, from 0.04 to 9 $M_\odot$, with rotation, settling, and helium burning, and 8 made-up ones). These lines belong to the old settling routines (LNEWDIF false), which no case uses: Every case with settling sets LNEWDIF and LDIFLI, and the documentation lists LNEWDIF among the legacy options (docs/source/namelist_physics.md, line 285). Reaching them takes LDIFY or LDIFZ with LNEWDIF false and LDIFLI off, since LDIFLI forces LNEWDIF on (parmin.f, lines 1239–1251↗); the size of the effect is still to be measured in such a run. Intent: none found.
2. (not reached) The convergence test ignores negative changes.
(lines 233–246↗) DXMAX is the largest signed change $X_i - X^p_i$, starting from 0, not the largest $|X_i - X^p_i|$; so an iteration whose changes are all negative, or whose positive changes are below GRTOL, is taken as converged however large its negative changes are, and the message prints the largest positive change. What follows: The iteration can stop before it has converged. Same path as suspected bug 1: the old settling routines, with LDIFY (set by LDIFZ and LDIFLI) and LNEWDIF false.
A possible fix:
grsett.f
@@ lines 232–239 @@
C ENOUGH TO EXIT.
DXMAX = 0.0D0
IMAX = 0
+C 11/09/26 KC via Claude
+C code changed to test the size of the change, not its sign.
DO 50 I = 1,NPT
- DX = EX(I)-EX_P(I)
+ DX = ABS(EX(I)-EX_P(I))
IF(DX.GT.DXMAX)THEN
DXMAX = DX
IMAX = I
Checked by running YREC (Nov 9, 2026; a line added at each suspected path counted how often it was taken, the outputs otherwise unchanged): not reached in any of the 34 runs, for the reason given in suspected bug 1: No case uses the old settling routines (LNEWDIF false), and LDIFY or LDIFZ with LNEWDIF false and LDIFLI off would reach it. Intent: none found.
Definitions
subroutineGRSETT
Settling of He (and metals) in one step: regrid to equal radii, explicit Lax–Wendroff for the settling term, implicit iterated tridiagonal solve for the diffusion term, regrid the change back (above).
Uses modules CONSTANTS_MOD, DIMENSIONS_MOD, GRAVEZ_MOD, IOUNITS_MOD, NMLPAR_MOD
Flowchart of its calls, in order (7)
Change log
The changes since rp_1
Changed since rp_1. The metals' coefficient arrays (formerly /GRAVSZ/) are local and passed to SETUP_GRSETT and MODEL_TO_EQUAL as new arguments; otherwise declarations only: COMMON blocks to modules, IMPLICIT NONE. No question settled.
Changed since a8a5950. The arguments DT, HQPR, HS1, and STOT are now DT0, HQPR0, HS10, and STOT0, copied on entry, so that the unit conversion and its inverse (which need not restore a value to the last bit) no longer change the structure's mass coordinate or MIX's sub-step.