1
2C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
3C
4C EQSTAT - New front end to equation of state routines
5C Provided to allow numerical differentiation of
6C current EOS routines by calling old EOS routine
7C (now called EQSTAT2) at aappropriated P and T
8C points in support of numerical differentiation.
9C LLP 10-22-06
10C
11C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
12
13 SUBROUTINE EQSTAT(TL,T,PL,P,DL,D,X,Z,BETA,BETAI,BETA14,FXION,RMU,
14 * *AMU,EMU,ETA,QDT,QDP,QCP,DELA,QDTT,QDTP,QAT,QAP,QCPT,QCPP,LDERIV,
15 * *LATMO,KSAHA)
16C 10/14/26 MHP via Claude
17C code changed to convert COMMON/NMLPAR/ into NMLPAR_MOD.
18 USE NMLPAR_MOD
19C 10/14/26 MHP via Claude
20C code changed to convert COMMON/CONSTANTS/ into CONSTANTS_MOD.
21 USE CONSTANTS_MOD
22C 10/20/26 MHP via Claude
23C code changed to replace implicit typing with IMPLICIT NONE and
24C explicit declarations.
25 IMPLICIT NONE
26 REAL*8 AMU,BETA,BETA14,BETAI,D,D1,DELA,DELA1,DELA2,DL,DL1,DPL,
27 * * DPL2,DTL,DTL2,EMU,ETA,FXION,P,PL,PLO,PPL,QAP,QAP1,QAPX,QAT,
28 * * QAT1,QATX,QCP,QCP1,QCP2,QCPP,QCPP1,QCPPX,QCPT,QCPT1,QCPTX,
29 * * QDP,QDP1,QDP2,QDT,QDT1,QDT2,QDTP,QDTP1,QDTPX,QDTT,QDTT1,
30 * * QDTTX,RMU,T,TL,TLO,TTL,X,Z
31 INTEGER KSAHA,KSAHAX,KSSAHAX
32 LOGICAL*4 LATMO,LATMOX,LDERIV,LDERIV2
33c
34c Input Arguments: TL, PL, X, Z, LDERIV, LATMO
35c
36c Output Arguments: T, P, D, DL, BETA, BETAI, BETA14, FXION, RMU, AMU,
37c EMU, QDT, QDP, QCP, DELA, QDTT, QDTP, QAT, QAP, QCPT, QCPP,
38c
39c Update (Input and Output) Arguments: KSAHA
40c
41
42C 10/13/26 MHP via Claude: removed the unused COMMON/IOUNITS/ declaration
43C (no member referenced here; residue of the obsolete-option removals).
44C DIMENSION ATOMWT(4),FXION(3),ATOMW2(12) ! FX(12) ! KC 2025-05-31
45 DIMENSION FXION(3)
46C 10/13/26 MHP via Claude: removed the unused COMMON/COMP/ declaration
47C (no member referenced here; residue of the obsolete-option removals).
48C 10/12/26 MHP via Claude
49C code changed to extend COMMON/NMLPAR/ to 234 members, adding the
50C namelist variables that the physics routines read from 27 COMMON
51C blocks which also held derived or working members. Those blocks keep
52C only their non-namelist members; five of them -- NEWMX, VMULT,
53C PMMWIND, CCOUT1 and ENVGEN -- had nothing left and are gone.
54C Derivation-only namelist variables from the same blocks went to
55C COMMON/NMLBYPASS/ instead, not here.
56C
57C Declared identically in all 121 files that hold any of these members.
58C Generated from one member list, so every copy is byte-identical; the
59C linker's 'size of symbol changed' warning is the check that they are.
60C 10/13/26 MHP via Claude
61C code changed to drop COMMON/CTLIM/, declared here but never used.
62C 9/25/26 MHP via Claude
63C code changed to merge COMMON/CONST/, COMMON/CONST1/, COMMON/CONST2/,
64C and COMMON/CONST3/ into a single COMMON/CONSTANTS/
65C 10/13/26 MHP via Claude: removed the unused COMMON/DEBHU/ declaration
66C (no member referenced here; residue of the obsolete-option removals).
67C 10/13/26 MHP via Claude: removed the unused COMMON/SCVEOS/ declaration
68C (no member referenced here; residue of the obsolete-option removals).
69C DATA NZP1/12/
70C DATA ATOMWT/0.9921D0,0.24975D0,0.08322D0,0.4995D0/
71C DATA SCALET,SCALES/0.500D0, 2.000D0/
72C DATA ATOMW2/23.0D0,26.99D0,24.32D0,55.86D0,28.1D0,12.015D0,
73C * 1.008D0,16.0D0,14.01D0,39.96D0,20.19D0,4.004D0/
74 SAVE
75
76c LDERIV: if true, provide derivatives needed for relaxation, else don't
77
78c LNumDeriv - if true derivatives are calculated numerically. else get
79c them the old way using Yale EOS.
80c LNumDeriv is part of the PARMIN PHYSICS namelist.
81
82 PLO = PL
83 TLO = TL
84 LATMOx = LATMO
85 KSSAHAx = KSAHA
86
87 IF(LDERIV .AND. LNumDeriv) THEN
88c Get Numerical Derivatives of Current EOS LLP 8/5/07
89c If both derivatives and numerical derivatives are requested.
90
91c A central difference approximation using values on both sides of
92c TL and then PL is used. The error term in this approximation of
93c the derivatives is of order h**2, where h is the interval. The
94c error term when one sided derivatives are used is of order h.
95
96 dpl = .150d0 ! the approximate table intervals
97 dtl = .030d0
98
99 dpl = dpl * .01d0 ! Empirically, scaling the intervals down
100 dtl = dtl * .01d0 ! by 100 seemed to work best.
101
102 dpl2 = 2D0 * dpl
103 dtl2 = 2D0 * dtl
104
105 DL1 = DL ! Initialize DL1 and D1 to called values.
106 D1 = D ! This can avoid problems in initial table lookups
107
108 TTL = TL + dtl ! Get derivatives wrt T.
109 T = 10.0D0**TTL
110 LDERIV2=.FALSE.
111 CALL EQSTAT2(TTL,T,PL,P,DL1,D1,X,Z,BETA,BETAI,BETA14,
112 * * FXION,RMU,AMU,EMU,ETA,QDT1,QDP1,QCP1,DELA1,QDTTx,
113 * * QDTPx,QATx,QAPx,QCPTx,QCPPx,LDERIV2,LATMOx,KSAHAx)
114
115 TTL = TL - dtl
116 T = 10.0D0**TTL
117 LDERIV2=.FALSE.
118 CALL EQSTAT2(TTL,T,PL,P,DL1,D1,X,Z,BETA,BETAI,BETA14,
119 * * FXION,RMU,AMU,EMU,ETA,QDT2,QDP2,QCP2,DELA2,QDTTx,
120 * * QDTPx,QATx,QAPx,QCPTx,QCPPx,LDERIV2,LATMOx,KSAHAx)
121 QDTT1 = (QDT1-QDT2)/dtl2/CLN
122 QCPT1 = (DLOG10(QCP1)-DLOG10(QCP2))/dtl2
123 QAT1 = (DLOG10(DELA1)-DLOG10(DELA2))/dtl2
124 TL = TLO
125 T = 10.0D0**TLO ! Restore original T
126
127 PPL = PL + dpl
128 P = 10.0D0**PPL
129 LDERIV2=.FALSE.
130 CALL EQSTAT2(TL,T,PPL,P,DL1,D1,X,Z,BETA,BETAI,BETA14,
131 * * FXION,RMU,AMU,EMU,ETA,QDT1,QDP1,QCP1,DELA1,QDTTx,
132 * * QDTPx,QATx,QAPx,QCPTx,QCPPx,LDERIV2,LATMOx,KSAHAx)
133 PPL = PL - dpl
134 P = 10.0D0**PPL
135 LDERIV2=.FALSE.
136 CALL EQSTAT2(TL,T,PPL,P,DL1,D1,X,Z,BETA,BETAI,BETA14,
137 * * FXION,RMU,AMU,EMU,ETA,QDT2,QDP2,QCP2,DELA2,QDTTx,
138 * * QDTPx,QATx,QAPx,QCPTx,QCPPx,LDERIV2,LATMOx,KSAHAx)
139 PL = PLO
140 P = 10.0D0**PLO ! Restore original P
141 QDTP1 = (QDT1-QDT2)/dpl2/CLN
142 QCPP1 = (DLOG10(QCP1)-DLOG10(QCP2))/dpl2
143 QAP1 = (DLOG10(DELA1)-DLOG10(DELA2))/dpl2
144
145 QDTT = QDTT1
146 QDTP = QDTP1
147 QAT = QAT1
148 QAP = QAP1
149 QCPP = QCPP1
150 QCPT = QCPT1
151 ENDIF
152
153 TL = TLO ! Restore original TL and PL.
154 PL = PLO
155
156 IF (LDERIV .AND. .NOT. LNumDeriv) THEN
157 LDERIV2=.TRUE. ! Need derivatives and have no numerical ones.
158 ! Call eqstat2 and request derivatives
159 CALL EQSTAT2(TL,T,PL,P,DL,D,X,Z,BETA,BETAI,BETA14,FXION,RMU,
160 * * AMU,EMU,ETA,QDT,QDP,QCP,DELA,QDTT,QDTP,QAT,QAP,QCPT,QCPP,
161 * * LDERIV2,LATMO,KSAHA)
162 ELSE
163 LDERIV2=.FALSE. ! We either already have numerical derivatives
164 ! or do not need any derivatives.
165 ! Call eqstat2 and request no derivatives
166 CALL EQSTAT2(TL,T,PL,P,DL,D,X,Z,BETA,BETAI,BETA14,FXION,RMU,
167 * * AMU,EMU,ETA,QDT,QDP,QCP,DELA,QDTTx,QDTPx,QATx,QAPx,QCPTx,
168 * * QCPPx,LDERIV2,LATMO,KSAHA)
169c Note that the QDTT,QDTP,QAT,QAP,QCPT,QCPP OUTPUTS are to
170c dummy variables so they can not affect the previously
171c calculated second derivatives.
172 ENDIF
173
174 RETURN
175 END
1"""Python version of eqstat.f (YREC 7be4133): the front end to the
2equation of state, with numerical second derivatives."""
3
4import math
5import numpy as np
6
7from pyrec import constants_mod
8from pyrec import nmlpar_mod
9from pyrec.eqstat2 import eqstat2
10
11# SAVE: the counters of the derivative calls. ksahax is never set from
12# ksaha, because of the misspelled kssahax (see the page's suspected bug).
13ksahax = 0
14kssahax = 0
15
16
17def eqstat(tl: float, t: float, pl: float, p: float, dl: float, d: float, x: float, z: float, beta: float, betai: float,
18 beta14: float, fxion: np.ndarray, rmu: float, amu: float, emu: float, eta: float, qdt: float, qdp: float,
19 qcp: float, dela: float, qdtt: float, qdtp: float, qat: float, qap: float, qcpt: float, qcpp: float,
20 lderiv: bool, latmo: bool, ksaha: int) -> tuple[float, float, float, float, float, float, float, float,
21 float, float, float, float, float, float, float, float, float, float, float, float, float, int]:
22 """Call EQSTAT2, the equation of state, at the point; with lderiv and
23 nmlpar_mod.lnumderiv, first compute the second derivatives by central
24 differences from four calls at log T +- 3e-4 and log P +- 1.5e-3.
25
26 Translates: eqstat.f#EQSTAT (7be4133)
27
28 Arguments: tl, pl (log T, log P), x, z, lderiv (second derivatives
29 wanted), latmo (atmosphere), ksaha (EQSAHA's iteration counter,
30 updated); dl, d are starting guesses. t, p, beta ... qcpp are set.
31 Array changed in place: fxion (index 0 unused).
32
33 Returns (t, p, dl, d, beta, betai, beta14, rmu, amu, emu, eta, qdt,
34 qdp, qcp, dela, qdtt, qdtp, qat, qap, qcpt, qcpp, ksaha). The Fortran
35 also assigns TL and PL, but only back to their own values, so they are
36 not returned. Without lderiv, qdtt ... qcpp come back as passed in.
37
38 """
39
40 global ksahax, kssahax
41 cln = constants_mod.cln
42
43 # Keep the point's TL and PL (Fortran lines 82–85).
44 plo = pl
45 tlo = tl
46 latmox = latmo
47 # Suspected bug (see the page): the misspelled kssahax leaves ksahax
48 # unset.
49 kssahax = ksaha
50
51 if lderiv and nmlpar_mod.lnumderiv:
52 # Central differences, error of order h**2: the steps (Fortran
53 # lines 87–106).
54 dpl = 0.150 # the approximate table intervals
55 dtl = 0.030
56 dpl = dpl * 0.01 # empirically, scaling the intervals down by 100
57 dtl = dtl * 0.01 # seemed to work best
58 dpl2 = 2.0 * dpl
59 dtl2 = 2.0 * dtl
60
61 dl1 = dl # start the table lookups from the called values
62 d1 = d
63
64 # Derivatives with respect to T (Fortran lines 108–125).
65 ttl = tl + dtl
66 t = 10.0 ** ttl
67 lderiv2 = False
68 (t, p, dl1, d1, beta, betai, beta14, rmu, amu, emu, eta, qdt1,
69 qdp1, qcp1, dela1, _, _, _, _, _, _, ksahax) = eqstat2(
70 ttl, t, pl, p, dl1, d1, x, z, beta, betai, beta14, fxion, rmu,
71 amu, emu, eta, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
72 0.0, lderiv2, latmox, ksahax)
73
74 ttl = tl - dtl
75 t = 10.0 ** ttl
76 lderiv2 = False
77 (t, p, dl1, d1, beta, betai, beta14, rmu, amu, emu, eta, qdt2,
78 qdp2, qcp2, dela2, _, _, _, _, _, _, ksahax) = eqstat2(
79 ttl, t, pl, p, dl1, d1, x, z, beta, betai, beta14, fxion, rmu,
80 amu, emu, eta, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
81 0.0, lderiv2, latmox, ksahax)
82 qdtt1 = (qdt1 - qdt2) / dtl2 / cln
83 qcpt1 = (math.log10(qcp1) - math.log10(qcp2)) / dtl2
84 qat1 = (math.log10(dela1) - math.log10(dela2)) / dtl2
85 tl = tlo
86 t = 10.0 ** tlo # restore the original T
87
88 # Derivatives with respect to P (Fortran lines 127–143).
89 ppl = pl + dpl
90 p = 10.0 ** ppl
91 lderiv2 = False
92 (t, p, dl1, d1, beta, betai, beta14, rmu, amu, emu, eta, qdt1,
93 qdp1, qcp1, dela1, _, _, _, _, _, _, ksahax) = eqstat2(
94 tl, t, ppl, p, dl1, d1, x, z, beta, betai, beta14, fxion, rmu,
95 amu, emu, eta, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
96 0.0, lderiv2, latmox, ksahax)
97 ppl = pl - dpl
98 p = 10.0 ** ppl
99 lderiv2 = False
100 (t, p, dl1, d1, beta, betai, beta14, rmu, amu, emu, eta, qdt2,
101 qdp2, qcp2, dela2, _, _, _, _, _, _, ksahax) = eqstat2(
102 tl, t, ppl, p, dl1, d1, x, z, beta, betai, beta14, fxion, rmu,
103 amu, emu, eta, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
104 0.0, lderiv2, latmox, ksahax)
105 pl = plo
106 p = 10.0 ** plo # restore the original P
107 qdtp1 = (qdt1 - qdt2) / dpl2 / cln
108 qcpp1 = (math.log10(qcp1) - math.log10(qcp2)) / dpl2
109 qap1 = (math.log10(dela1) - math.log10(dela2)) / dpl2
110
111 # Fortran lines 145–150.
112 qdtt = qdtt1
113 qdtp = qdtp1
114 qat = qat1
115 qap = qap1
116 qcpp = qcpp1
117 qcpt = qcpt1
118
119 # Restore the original TL and PL (Fortran lines 153–154).
120 tl = tlo
121 pl = plo
122
123 # The call at the point (Fortran lines 156–172).
124 if lderiv and not nmlpar_mod.lnumderiv:
125 # Derivatives wanted, none numerical: EQSTAT2's analytic ones.
126 lderiv2 = True
127 (t, p, dl, d, beta, betai, beta14, rmu, amu, emu, eta, qdt, qdp,
128 qcp, dela, qdtt, qdtp, qat, qap, qcpt, qcpp, ksaha) = eqstat2(
129 tl, t, pl, p, dl, d, x, z, beta, betai, beta14, fxion, rmu,
130 amu, emu, eta, qdt, qdp, qcp, dela, qdtt, qdtp, qat, qap,
131 qcpt, qcpp, lderiv2, latmo, ksaha)
132 else:
133 # Numerical derivatives already in hand, or none needed: the
134 # second derivatives go to dummies so they cannot overwrite them.
135 lderiv2 = False
136 (t, p, dl, d, beta, betai, beta14, rmu, amu, emu, eta, qdt, qdp,
137 qcp, dela, _, _, _, _, _, _, ksaha) = eqstat2(
138 tl, t, pl, p, dl, d, x, z, beta, betai, beta14, fxion, rmu,
139 amu, emu, eta, qdt, qdp, qcp, dela, 0.0, 0.0, 0.0, 0.0, 0.0,
140 0.0, lderiv2, latmo, ksaha)
141
142 return (t, p, dl, d, beta, betai, beta14, rmu, amu, emu, eta, qdt, qdp,
143 qcp, dela, qdtt, qdtp, qat, qap, qcpt, qcpp, ksaha)
144