1C SUBROUTINE FOR INTERPOLATING IN THE SURFACE ABUNDANCE TABLE OF THE SCV
2C EQUATION OF STATE.
3C SUBROUTINE EQSCVE(TL,T,PL,P,DL,D,X,Z,BETA,BETAI,BETA14,FXION,RMU,
4C *AMU,EMU,ETA,QDT,QDP,QCP,DELA,LCALC) ! KC 2025-05-31
5 SUBROUTINE EQSCVE(TL,T,P,DL,D,X,Z,BETA,FXION,QDT,QDP,QCP,DELA,LCALC)
6C 10/14/26 MHP via Claude
7C code changed to convert COMMON/SCVEOS/ into SCVEOS_MOD.
8 USE SCVEOS_MOD
9C 10/14/26 MHP via Claude
10C code changed to convert COMMON/COMP/ into COMP_MOD.
11 USE COMP_MOD
12C 10/14/26 MHP via Claude
13C code changed to convert COMMON/CONSTANTS/ into CONSTANTS_MOD.
14 USE CONSTANTS_MOD
15C 10/20/26 MHP via Claude
16C code changed to replace implicit typing with IMPLICIT NONE and
17C explicit declarations.
18 IMPLICIT NONE
19 REAL*8 BETA,BETM1,CHGT1,CHGT2,D,DELA,DL,DPABOVE,DPBELOW,DTABOVE,
20 * * DTBELOW,FP,FPD,FSP,FST,FT,FTD,FXION,P,PGAS,PP,PRAD,QCP,
21 * * QCP0,QDP,QDP0,QDT,QDT0,QPT,QPT0,QR,QUT,T,TEMP,TEMPT,TEMPT1,
22 * * TEMPT1T,TEMPT2,TEMPT2P,TEMPT3,TL,TOLSP,TOLST,UR,X,XHEP,
23 * * XHEPP,XHP,XTF_H1,XTF_H2,XTF_HE,XTF_HEP,XTF_HEPP,XTF_HE_E,
24 * * XTF_HP,XTF_H_E,Y,Y0,Y2,YTAB,Z
25 INTEGER I,II,ISMP,ISMT,J,JJ,JJJ,K,NTS
26 LOGICAL*4 LCALC,LSMP,LSMT
27 PARAMETER (NTS=63)
28C 9/25/26 MHP via Claude
29C code changed to merge COMMON/CONST/, COMMON/CONST1/, COMMON/CONST2/,
30C and COMMON/CONST3/ into a single COMMON/CONSTANTS/
31C 10/13/26 MHP via Claude: removed the unused COMMON/NMLPAR/ declaration
32C (no member referenced here; residue of the obsolete-option removals).
33C REPLACE 4-PT LAGRANGE INTERPOLATE WITH 4-PT NATURAL SPLINE
34C DIMENSION ATOMWT(4),QR(4),FT(4),FTD(4),
35C * FP(4),FPD(4),TEMPT(5),TEMP(4,5),FXION(3)
36 DIMENSION FP(4),FPD(4),FT(4),FTD(4)
37C DIMENSION ATOMWT(4),QR(4),YTAB(4),Y2(4), ! KC 2025-05-31
38 DIMENSION QR(4),YTAB(4),Y2(4),
39 * * TEMPT(5),TEMP(4,5),FXION(3),TEMPT1(5),TEMPT2(5),
40 * * TEMPT3(5),TEMPT1T(5),TEMPT2P(5)
41C DATA ATOMWT/0.9921D0,0.24975D0,0.08322D0,0.4995D0/
42C DATA CMH,CMHE,CBOLTZ/1.67357D-24,6.646442D-24,1.380658D-16/
43 DATA TOLSP,TOLST/0.08D0,0.032D0/
44 SAVE
45 IF(ABS(X-XENV).GT.1.0D-5 .OR. ABS(Z-ZENV).GT.1.0D-5)THEN
46C CALL EQSCVG(TL,T,PL,P,DL,D,X,Z,BETA,BETAI,BETA14,FXION,RMU,
47C * AMU,EMU,ETA,QDT,QDP,QCP,DELA,LCALC) ! KC 2025-05-31
48 CALL EQSCVG(TL,T,P,DL,D,X,Z,BETA,FXION,QDT,QDP,QCP,DELA,LCALC)
49 RETURN
50 ENDIF
51C FIND GAS PRESSURE (WHICH IS THE QUANTITY WHICH IS TABULATED).
52 BETM1 = 1.0D0 - BETA
53 PP = DLOG10(BETA*P)
54C CHECK IF THE POINT IS WITHIN THE TABLE
55 IF(TL.LT.TLOGX(1) .OR. TL.GT.TLOGX(NTS) .OR. PP.LT.4.0D0)THEN
56 LCALC = .FALSE. ! Error exit - no valid table entry
57 RETURN
58 ENDIF
59C FIND NEAREST POINTS IN TEMPERATURE.
60 IF(TL.LT.TLOGX(IDTSCV+1))THEN
61C SEARCH DOWN TO FIND NEAREST 4 TABLE ELEMENTS
62 DO I = IDTSCV,1,-1
63 IF(TL.GT.TLOGX(I))THEN
64 II = I - 1
65 GOTO 10
66 ENDIF
67 END DO
68 II = 1
69 10 10 CONTINUE
70 ELSE
71C SEARCH UP FOR NEAREST 4 TABLE ELEMENTS
72 DO I = IDTSCV+2,NTS
73 IF(TL.LT.TLOGX(I))THEN
74 II = I - 2
75 GOTO 20
76 ENDIF
77 END DO
78 II = NTS - 3
79 20 20 CONTINUE
80 ENDIF
81 IDTSCV = MAX(1,II)
82 IDTSCV = MIN(NTS-3,IDTSCV)
83 LSMT = .FALSE.
84 IF(IDTSCV.EQ.II)THEN
85 DTABOVE = TL - TLOGX(IDTSCV+1)
86 DTBELOW = TLOGX(IDTSCV+2) - TL
87 IF(DTABOVE.LT.TOLST)THEN
88 IF(IDTSCV.GT.1)THEN
89 LSMT = .TRUE.
90 ISMT = -1
91 FST = (DTABOVE+TOLST)/(2.0D0*TOLST)
92 ENDIF
93 ELSE IF(DTBELOW.LT.TOLST)THEN
94 IF(IDTSCV.LT.NTS-3)THEN
95 LSMT = .TRUE.
96 ISMT = 1
97 FST = 0.5D0*DTBELOW/TOLST
98 ENDIF
99 ENDIF
100 ENDIF
101C FIND NEAREST POINTS IN PRESSURE.
102 JJJ = MIN(NPTSX(IDTSCV)-3,IDP)
103 IF(PP.LT.TABLENV(IDTSCV,JJJ+1,1))THEN
104C SEARCH DOWN TO FIND NEAREST 4 TABLE ELEMENTS
105 DO J = JJJ,1,-1
106 IF(PP.GT.TABLENV(IDTSCV,J,1))THEN
107 JJ = J - 1
108 GOTO 30
109 ENDIF
110 END DO
111 JJ = 1
112 30 30 CONTINUE
113 IDP = MAX(1,JJ)
114 IDP = MIN(NPTSX(IDTSCV)-3,IDP)
115 ELSE
116C SEARCH UP FOR NEAREST 4 TABLE ELEMENTS. NOTE SEARCH IS DONE AT LOWEST
117C TEMPERATURE POINT (WITH THE MINIMUM RANGE IN P).
118 DO J = JJJ+2,NPTSX(IDTSCV)
119 IF(PP.LT.TABLENV(IDTSCV,J,1))THEN
120 JJ = J - 2
121 GOTO 40
122 ENDIF
123 END DO
124C POINT IS OUTSIDE TABLE; RETURN.
125 LCALC = .FALSE. ! Error exit - no valid table entry
126 RETURN
127 40 40 CONTINUE
128 IDP = MIN(NPTSX(IDTSCV)-3,JJ)
129 ENDIF
130 LCALC = .TRUE.
131 LSMP = .FALSE.
132 IF(IDP.EQ.JJ)THEN
133 DPABOVE = PP - TABLENV(IDTSCV,IDP+1,1)
134 DPBELOW = TABLENV(IDTSCV,IDP+2,1) - PP
135 IF(DPABOVE.LT.TOLSP)THEN
136 IF(IDP.GT.1)THEN
137 LSMP = .TRUE.
138 ISMP = -1
139 FSP = (DPABOVE+TOLSP)/(2.0D0*TOLSP)
140 ENDIF
141 ELSE IF(DPBELOW.LT.TOLSP)THEN
142 IF(IDP.LT.NPTSX(IDTSCV)-3)THEN
143 LSMP = .TRUE.
144 ISMP = 1
145 FSP = 0.5D0*DPBELOW/TOLSP
146 ENDIF
147 ENDIF
148 ENDIF
149C DO K = 1,4
150C QR(K) = TLOGX(K+IDTSCV-1)
151C END DO
152C CALL INTERP(QR,FT,FTD,TL)
153C DO K = 1,4
154C QR(K) = TABLENV(IDTSCV,IDP+K-1,1)
155C END DO
156C CALL INTERP(QR,FP,FPD,PP)
157 Y = 1.0D0 - X - Z
158C INCLUDE RADIATION PRESSURE IN THE EQUATION OF STATE.
159 PRAD = BETM1*P
160 PGAS = BETA*P
161C INTERPOLATE IN PRESSURE AT 4 DIFFERENT TEMPERATURE POINTS.
162 DO I = 1,4
163 II = IDTSCV+I-1
164 DO K = 1,4
165 QR(K) = TABLENV(IDTSCV,IDP+K-1,1)
166 END DO
167 DO J = 1,5
168 DO K = 1,4
169 YTAB(K) = TABLENV(II,IDP+K-1,J+1)
170 END DO
171 CALL KSPLINE(QR,YTAB,Y2)
172 CALL KSPLINT(QR,YTAB,Y2,PP,Y0)
173 TEMP(I,J) = Y0
174C TEMP(I,J)=FP(1)*TABLENV(II,IDP,J+1) +
175C * FP(2)*TABLENV(II,IDP+1,J+1) + FP(3)*TABLENV(II,IDP+2,J+1)
176C * + FP(4)*TABLENV(II,IDP+3,J+1)
177 END DO
178 END DO
179C INTERPOLATE IN TEMPERATURE
180 DO K = 1,4
181 QR(K) = TLOGX(K+IDTSCV-1)
182 END DO
183 DO J = 1,5
184 DO K = 1,4
185 YTAB(K) = TEMP(K,J)
186 END DO
187 CALL KSPLINE(QR,YTAB,Y2)
188 CALL KSPLINT(QR,YTAB,Y2,TL,Y0)
189 TEMPT(J) = Y0
190 END DO
191C PERFORM INTERPOLATION AT ADJACENT TEMPERATURE TABLE POINTS
192 IF(LSMT)THEN
193C INTERPOLATE IN PRESSURE AT 4 DIFFERENT TEMPERATURE POINTS.
194 DO I = 1,4
195 II = IDTSCV+I-1+ISMT
196 DO K = 1,4
197 QR(K) = TABLENV(IDTSCV,IDP+K-1,1)
198 END DO
199 DO J = 1,5
200 DO K = 1,4
201 YTAB(K) = TABLENV(II,IDP+K-1,J+1)
202 END DO
203 CALL KSPLINE(QR,YTAB,Y2)
204 CALL KSPLINT(QR,YTAB,Y2,PP,Y0)
205 TEMP(I,J) = Y0
206C TEMP(I,J)=FP(1)*TABLENV(II,IDP,J+1) +
207C * FP(2)*TABLENV(II,IDP+1,J+1) + FP(3)*TABLENV(II,IDP+2,J+1)
208C * + FP(4)*TABLENV(II,IDP+3,J+1)
209 END DO
210 END DO
211C INTERPOLATE IN TEMPERATURE
212 DO K = 1,4
213 QR(K) = TLOGX(K+IDTSCV+ISMT-1)
214 END DO
215 DO J = 1,5
216 DO K = 1,4
217 YTAB(K) = TEMP(K,J)
218 END DO
219 CALL KSPLINE(QR,YTAB,Y2)
220 CALL KSPLINT(QR,YTAB,Y2,TL,Y0)
221 TEMPT1(J) = Y0
222 END DO
223 IF(ISMT.EQ.-1)THEN
224 DO J = 1,5
225 TEMPT1T(J) = TEMPT1(J)+FST*(TEMPT(J)-TEMPT1(J))
226 END DO
227 ELSE
228 DO J = 1,5
229 TEMPT1T(J) = TEMPT(J)+FST*(TEMPT1(J)-TEMPT(J))
230 END DO
231 ENDIF
232 ENDIF
233C PERFORM INTERPOLATION AT ADJACENT PRESSURE TABLE POINTS
234 IF(LSMP)THEN
235C INTERPOLATE IN PRESSURE AT 4 DIFFERENT TEMPERATURE POINTS.
236 DO I = 1,4
237 II = IDTSCV+I-1
238 DO K = 1,4
239 QR(K) = TABLENV(IDTSCV,IDP+K-1+ISMP,1)
240 END DO
241 DO J = 1,5
242 DO K = 1,4
243 YTAB(K) = TABLENV(II,IDP+K-1+ISMP,J+1)
244 END DO
245 CALL KSPLINE(QR,YTAB,Y2)
246 CALL KSPLINT(QR,YTAB,Y2,PP,Y0)
247 TEMP(I,J) = Y0
248C TEMP(I,J)=FP(1)*TABLENV(II,IDP,J+1) +
249C * FP(2)*TABLENV(II,IDP+1,J+1) + FP(3)*TABLENV(II,IDP+2,J+1)
250C * + FP(4)*TABLENV(II,IDP+3,J+1)
251 END DO
252 END DO
253C INTERPOLATE IN TEMPERATURE
254 DO K = 1,4
255 QR(K) = TLOGX(K+IDTSCV-1)
256 END DO
257 DO J = 1,5
258 DO K = 1,4
259 YTAB(K) = TEMP(K,J)
260 END DO
261 CALL KSPLINE(QR,YTAB,Y2)
262 CALL KSPLINT(QR,YTAB,Y2,TL,Y0)
263 TEMPT2(J) = Y0
264 END DO
265 IF(ISMP.EQ.-1)THEN
266 DO J = 1,5
267 TEMPT2P(J) = TEMPT2(J)+FSP*(TEMPT(J)-TEMPT2(J))
268 END DO
269 ELSE
270 DO J = 1,5
271 TEMPT2P(J) = TEMPT(J)+FSP*(TEMPT2(J)-TEMPT(J))
272 END DO
273 ENDIF
274 ENDIF
275C PERFORM INTERPOLATION AT ADJACENT T+P TABLE POINTS
276 IF(LSMP .AND. LSMT)THEN
277C INTERPOLATE IN PRESSURE AT 4 DIFFERENT TEMPERATURE POINTS.
278 DO I = 1,4
279 II = IDTSCV+I-1 + ISMT
280 DO K = 1,4
281 QR(K) = TABLENV(IDTSCV,IDP+K-1+ISMP,1)
282 END DO
283 DO J = 1,5
284 DO K = 1,4
285 YTAB(K) = TABLENV(II,IDP+K-1+ISMP,J+1)
286 END DO
287 CALL KSPLINE(QR,YTAB,Y2)
288 CALL KSPLINT(QR,YTAB,Y2,PP,Y0)
289 TEMP(I,J) = Y0
290C TEMP(I,J)=FP(1)*TABLENV(II,IDP,J+1) +
291C * FP(2)*TABLENV(II,IDP+1,J+1) + FP(3)*TABLENV(II,IDP+2,J+1)
292C * + FP(4)*TABLENV(II,IDP+3,J+1)
293 END DO
294 END DO
295C INTERPOLATE IN TEMPERATURE
296 DO K = 1,4
297 QR(K) = TLOGX(K+IDTSCV-1 + ISMT)
298 END DO
299 DO J = 1,5
300 DO K = 1,4
301 YTAB(K) = TEMP(K,J)
302 END DO
303 CALL KSPLINE(QR,YTAB,Y2)
304 CALL KSPLINT(QR,YTAB,Y2,TL,Y0)
305 TEMPT3(J) = Y0
306 END DO
307 ENDIF
308 IF(LSMT)THEN
309C ADD CHANGES FOR BOTH T AND P INTERPOLATION
310 IF(LSMP)THEN
311C WRITE(*,911)(TEMPT(J),J=1,5)
312C 911 FORMAT(1X,'ORIG ',1P5E16.7)
313C WRITE(*,912)(TEMPT1(J)-TEMPT(J),J=1,5)
314C 912 FORMAT(1X,'INT T',1P5E16.7)
315C WRITE(*,913)(TEMPT2(J)-TEMPT(J),J=1,5)
316C 913 FORMAT(1X,'INT P',1P5E16.7)
317C WRITE(*,914)(TEMPT3(J)-TEMPT(J),J=1,5)
318C 914 FORMAT(1X,'INT PT',1P5E16.7)
319 DO J = 1,5
320 IF(ISMT.EQ.-1)THEN
321C INTERPOLATE IN T AT FIXED P
322 CHGT1 = TEMPT1(J)+FST*(TEMPT(J)-TEMPT1(J))
323C INTERPOLATE IN T AT DIFFERENT P
324 CHGT2 = TEMPT3(J)+FST*(TEMPT2(J)-TEMPT3(J))
325 ELSE
326 CHGT1 = TEMPT(J)+FST*(TEMPT1(J)-TEMPT(J))
327 CHGT2 = TEMPT2(J)+FST*(TEMPT3(J)-TEMPT2(J))
328 ENDIF
329 IF(ISMP.EQ.-1)THEN
330 TEMPT(J) = CHGT2+FSP*(CHGT1-CHGT2)
331C CHANGE IN P AT FIXED T
332C CHGP1 = TEMPT2+FSP*(TEMPT(J)-TEMPT2(J))
333C CHANGE IN P AT DIFFERENT T
334C CHGP2 = TEMPT3(J)+FSP*(TEMPT1(J)-TEMPT3(J))
335 ELSE
336 TEMPT(J) = CHGT1+FSP*(CHGT2-CHGT1)
337C CHGP1 = TEMPT(J)+FSP*(TEMPT2(J)-TEMPT(J))
338C CHGP2 = TEMPT1(J)+FSP*(TEMPT3(J)-TEMPT1(J))
339 ENDIF
340C WRITE(*,915)ISMP,ISMT,TEMPT(J),CHGT1,CHGT2,FSP,FST
341C 915 FORMAT(2I2,1P5E16.7)
342 END DO
343 ELSE
344C ADD T INTERPOLATION CHANGES ONLY
345C WRITE(*,911)(TEMPT(J),J=1,5)
346C WRITE(*,912)(TEMPT1(J)-TEMPT(J),J=1,5)
347 DO J = 1,5
348 TEMPT(J) = TEMPT1T(J)
349 END DO
350 ENDIF
351 ELSE IF(LSMP)THEN
352C ADD P INTERPOLATION CHANGES ONLY
353C WRITE(*,911)(TEMPT(J),J=1,5)
354C WRITE(*,912)(TEMPT2(J)-TEMPT(J),J=1,5)
355 DO J = 1,5
356 TEMPT(J) = TEMPT2P(J)
357 END DO
358 ENDIF
359C DO J = 1,5
360C TEMPT(J)=FT(1)*TEMP(1,J) + FT(2)*TEMP(2,J) + FT(3)*TEMP(3,J)
361C * + FT(4)*TEMP(4,J)
362C END DO
363C DENSITY
364 DL = TEMPT(1)
365 D = EXP(CLN*DL)
366C D LN RHO/ D LN P = QDP
367C FOR GAS PRESSURE QDP(TOT) = QDP(X)*X*RHO/RHO(X)+QDP(Y)*Y*RHO/RHO(Y)
368C FOR RADIATION PRESSURE QDP = 0, SO QDP(TOT) = QDP(GAS)*P/PGAS
369 QDP0 = TEMPT(3)
370 QDP = QDP0/BETA
371C D LN RHO/ D LN T = QDT (NOTE : D LN P/ D LN T = QPT)
372C FOR GAS PRESSURE, CORRECT AS PER QDP
373C FOR RADIATION PRESSURE, USE QDT = -QDP*QPT. CORRECT QPT FOR
374C RADIATION PRESSURE AND USE THE CORRECTED QDP, QPT TO GET QDT.
375 QDT0 = TEMPT(2)
376 QPT0 = -QDT0/QDP0
377 QPT = QPT0*BETA + 4.0D0*BETM1
378 QDT = -QPT*QDP
379C CP = S*(D LN S/ D LN T)|P IS TABULATED. USE
380C CP = DU/DT + P*(D LN RHO/D LN T)**2/RHO/T/(D LN RHO/ D LN P)
381C TO FIND DU/DT. THEN INCLUDE THE EFFECTS OF RADIATION PRESSURE
382C ON DU/DT AND THE OTEHR TERMS AND GET A CORRECTED CP.
383C CP (GAS PRESSURE ONLY).
384 QCP0 = TEMPT(4)
385C NOW FIND DU/DT FROM THE ORIGINAL TABLE.
386 QUT = TEMPT(5)
387C QUT = QCP0 - PGAS*QDT0**2/QDP0/D/T
388C CORRECT DU/DT FOR RADIATION
389 UR = 3.0D0*PRAD/D
390 QUT = QUT + 4.0D0*UR/T
391C CORRECT CP FOR RADIATION PRESSURE
392 QCP = QUT - P*QDT*QPT/D/T
393C ADIABATIC TEMPERATURE GRADIENT
394 DELA = -P*QDT/D/T/QCP
395
396C Get fractions of total particles (including electrons), as follows:
397C XTF_H2 the fraction that is neutral hydrogen molecules, and
398C XTF_He the fraction thst is neutral helium).
399C These are in column 2 respectively of the SCV hydrogen and
400C Helium tables.
401C INTERPOLATE IN PRESSURE AT 4 DIFFERENT TEMPERATURE POINTS.
402
403 DO K = 1,4
404 QR(K) = TLOGX(K+IDTSCV-1)
405 END DO
406 CALL INTERP(QR,FT,FTD,TL)
407 DO K = 1,4
408 QR(K) = TABLENV(IDTSCV,IDP+K-1,1)
409 END DO
410 CALL INTERP(QR,FP,FPD,PP)
411
412 DO I = 1,4
413 II = IDTSCV+I-1
414 TEMP(I,1)=FP(1)*TABLEX(II,IDP,2) +
415 * * FP(2)*TABLEX(II,IDP+1,2) + FP(3)*TABLEX(II,IDP+2,2)
416 * * + FP(4)*TABLEX(II,IDP+3,2)
417 TEMP(I,2)=FP(1)*TABLEY(II,IDP,2) +
418 * * FP(2)*TABLEY(II,IDP+1,2) + FP(3)*TABLEY(II,IDP+2,2)
419 * * + FP(4)*TABLEY(II,IDP+3,2)
420 END DO
421C INTERPOLATE IN TEMPERATURE
422 XTF_H2 = FT(1)*TEMP(1,1) + FT(2)*TEMP(2,1) + FT(3)*TEMP(3,1)
423 * * + FT(4)*TEMP(4,1)
424 XTF_He = FT(1)*TEMP(1,2) + FT(2)*TEMP(2,2) + FT(3)*TEMP(3,2)
425 * * + FT(4)*TEMP(4,2)
426
427C Get more fractions of total particles (including electrons), as follows:
428C XTF_H1 the fraction that is neutral hydrogen atoms, and
429C XTF_HeP the fraction thst is singly ionized helium).
430C These are in column 3 respectively of the SCV hydrogen and
431C Helium tables.
432C INTERPOLATE IN PRESSURE AT 4 DIFFERENT TEMPERATURE POINTS.
433 DO I = 1,4
434 II = IDTSCV+I-1
435 TEMP(I,1)=FP(1)*TABLEX(II,IDP,3) +
436 * * FP(2)*TABLEX(II,IDP+1,3) + FP(3)*TABLEX(II,IDP+2,3)
437 * * + FP(4)*TABLEX(II,IDP+3,3)
438 TEMP(I,2)=FP(1)*TABLEY(II,IDP,3) +
439 * * FP(2)*TABLEY(II,IDP+1,3) + FP(3)*TABLEY(II,IDP+2,3)
440 * * + FP(4)*TABLEY(II,IDP+3,3)
441 END DO
442C INTERPOLATE IN TEMPERATURE
443 XTF_H1 = FT(1)*TEMP(1,1) + FT(2)*TEMP(2,1) + FT(3)*TEMP(3,1)
444 * * + FT(4)*TEMP(4,1)
445 XTF_HeP = FT(1)*TEMP(1,2) + FT(2)*TEMP(2,2) + FT(3)*TEMP(3,2)
446 * * + FT(4)*TEMP(4,2)
447
448C At tis time we can calculate the ramaining particles fractions using conservation
449C of particles and coservation of charge. The variable names are:
450C XTF_H_e the hycrogen related fraction that is electrons
451C XTF_HP the hydrogen ralated fraction that is H+ ions
452C XTF_He_e the helium related fraction that is electrons
453C XTF_HePP the helium related fraction that is doubly ionized helium (He++)
454C Particle and charge conservation yields:
455 XTF_H_e = .5D0*(1D0 - XTF_H2 - XTF_H1)
456 XTF_HP = XTF_H_e
457 XTF_He_e = (1D0/3D0)*(2D0 - 2D0*XTF_He - XTF_HeP)
458 XTF_HePP = 1D0 - XTF_He - XTF_HeP - XTF_He_e
459
460C We are seeking the fraction XHP of hydrogen nuclei that are singly ionized,
461C the fraction XHeP of helium huclei that are singly ionized and XHePP, the
462C helium fraction that is doubly ionized.
463 XHP = XTF_HP / (XTF_H2 + XTF_H1 + XTF_HP)
464 XHeP = XTF_HeP / (XTF_He + XTF_HeP + XTF_HePP)
465 XHePP = XTF_HePP / (XTF_He + XTF_HeP + XTF_HePP)
466
467C In the same order, these are the desired elements of array XION(3)
468 FXION(1) = XHP
469 FXION(2) = XHeP
470 FXION(3) = XHePP
471
472 RETURN ! Normal exit. Valid table entry. LCALC is true.
473
474 END
1"""Python version of eqscve.f (YREC development, 7be4133): the SCV
2equation of state at the envelope composition."""
3
4import math
5
6import numpy as np
7
8from pyrec import comp_mod
9from pyrec import constants_mod
10from pyrec import scveos_mod
11from pyrec.eqscvg import eqscvg
12from pyrec.interp import interp
13from pyrec.kspline import kspline
14from pyrec.ksplint import ksplint
15
16NTS = 63 # temperatures in the SCV tables
17TOLSP = 0.08 # smoothing zone in log P (DATA)
18TOLST = 0.032 # smoothing zone in log T (DATA; Fortran line 43)
19
20
21def _stencil_spline(ismt, ismp, pp, tl):
22 """The interpolation block the Fortran writes out four times: for the
23 4x4 stencil shifted by ismt rows in T and ismp columns in P, a natural
24 spline in log Pgas at each of the 4 temperatures, then one in log T,
25 for columns 2-6 of TABLENV. The pressure abscissae always come from
26 the row idtscv (unshifted), as in the Fortran. Returns tempt[1..5]."""
27
28 scv = scveos_mod
29 idt = scv.idtscv
30 idp = scv.idp
31 qr = np.zeros(5)
32 ytab = np.zeros(5)
33 y2 = np.zeros(5)
34 temp = np.zeros((5, 6))
35 tempt = np.zeros(6)
36
37 # Interpolate in pressure at 4 different temperature points (Fortran
38 # lines 162–178, and 194–210, 236–252, 278–294 for the shifted
39 # stencils).
40 for i in range(1, 5):
41 ii = idt + i - 1 + ismt
42 for k in range(1, 5):
43 # Question (see the page): row idt's pressures for all 4 rows.
44 qr[k] = scv.tablenv[idt, idp + k - 1 + ismp, 1]
45 for j in range(1, 6):
46 for k in range(1, 5):
47 ytab[k] = scv.tablenv[ii, idp + k - 1 + ismp, j + 1]
48 kspline(qr, ytab, y2)
49 temp[i, j] = ksplint(qr, ytab, y2, pp, 0.0)
50
51 # Interpolate in temperature (Fortran lines 180–190, and 212–222,
52 # 254–264, 296–306).
53 for k in range(1, 5):
54 qr[k] = scv.tlogx[k + idt - 1 + ismt]
55 for j in range(1, 6):
56 for k in range(1, 5):
57 ytab[k] = temp[k, j]
58 kspline(qr, ytab, y2)
59 tempt[j] = ksplint(qr, ytab, y2, tl, 0.0)
60 return tempt
61
62
63def eqscve(tl: float, t: float, p: float, dl: float, d: float, x: float, z: float, beta: float, fxion: np.ndarray,
64 qdt: float, qdp: float, qcp: float, dela: float, lcalc: bool) -> tuple[float, float, float, float, float,
65 float, bool]:
66 """Interpolate the Saumon-Chabrier-Van Horn equation of state in its
67 table for the envelope composition (2D splines, smoothed where the
68 stencil changes), add radiation, and get the H and He ionization
69 fractions; any other composition goes to eqscvg.
70
71 Translates: eqscve.f#EQSCVE (7be4133)
72
73 Arguments: tl, t (log T, T), p (total pressure), x, z, beta (Pgas/P);
74 dl ... dela and lcalc are only set. Array changed in place: fxion (H+,
75 He+, He++ fractions, index 0 unused).
76
77 Returns (dl, d, qdt, qdp, qcp, dela, lcalc). Outside the table, lcalc
78 is False and the others are returned as passed in.
79
80 """
81
82 c = constants_mod
83 scv = scveos_mod
84
85 # Other compositions go to eqscvg (Fortran lines 45–50).
86 if (abs(x - comp_mod.xenv) > 1.0e-5
87 or abs(z - comp_mod.zenv) > 1.0e-5):
88 dl, d, qdt, qdp, qcp, dela, lcalc = eqscvg(
89 tl, t, p, dl, d, x, z, beta, fxion, qdt, qdp, qcp, dela, lcalc)
90 return dl, d, qdt, qdp, qcp, dela, lcalc
91
92 # The gas pressure is the tabulated quantity (Fortran lines 52–58).
93 betm1 = 1.0 - beta
94 pp = math.log10(beta * p)
95 # Is the point within the table?
96 if tl < scv.tlogx[1] or tl > scv.tlogx[NTS] or pp < 4.0:
97 lcalc = False
98 return dl, d, qdt, qdp, qcp, dela, lcalc
99
100 # Nearest 4 points in temperature, searching from the last stencil
101 # (Fortran lines 60–82).
102 if tl < scv.tlogx[scv.idtscv + 1]:
103 for i in range(scv.idtscv, 0, -1):
104 if tl > scv.tlogx[i]:
105 ii = i - 1
106 break # go to 10
107 else:
108 ii = 1
109 else:
110 for i in range(scv.idtscv + 2, NTS + 1):
111 if tl < scv.tlogx[i]:
112 ii = i - 2
113 break # go to 20
114 else:
115 ii = NTS - 3
116 scv.idtscv = max(1, ii)
117 scv.idtscv = min(NTS - 3, scv.idtscv)
118 idt = scv.idtscv
119
120 # Smoothing in T near the edges of the middle interval (Fortran lines
121 # 83–100).
122 lsmt = False
123 ismt = 0
124 fst = 0.0
125 if idt == ii:
126 dtabove = tl - scv.tlogx[idt + 1]
127 dtbelow = scv.tlogx[idt + 2] - tl
128 if dtabove < TOLST:
129 if idt > 1:
130 lsmt = True
131 ismt = -1
132 fst = (dtabove + TOLST) / (2.0 * TOLST)
133 elif dtbelow < TOLST:
134 if idt < NTS - 3:
135 lsmt = True
136 ismt = 1
137 fst = 0.5 * dtbelow / TOLST
138
139 # Nearest 4 points in pressure, searched in the row idt (the lowest
140 # temperature of the stencil, with the smallest range in P; Fortran
141 # lines 102–130).
142 nptsx = scv.nptsx[idt]
143 jjj = min(nptsx - 3, scv.idp)
144 if pp < scv.tablenv[idt, jjj + 1, 1]:
145 for j in range(jjj, 0, -1):
146 if pp > scv.tablenv[idt, j, 1]:
147 jj = j - 1
148 break # go to 30
149 else:
150 jj = 1
151 scv.idp = max(1, jj)
152 scv.idp = min(nptsx - 3, scv.idp)
153 else:
154 for j in range(jjj + 2, nptsx + 1):
155 if pp < scv.tablenv[idt, j, 1]:
156 jj = j - 2
157 break # go to 40
158 else:
159 # The point is above the table: return.
160 lcalc = False
161 return dl, d, qdt, qdp, qcp, dela, lcalc
162 scv.idp = min(nptsx - 3, jj) # label 40
163 idp = scv.idp
164 lcalc = True
165
166 # Smoothing in P near the edges of the middle interval (Fortran lines
167 # 131–148).
168 lsmp = False
169 ismp = 0
170 fsp = 0.0
171 if idp == jj:
172 dpabove = pp - scv.tablenv[idt, idp + 1, 1]
173 dpbelow = scv.tablenv[idt, idp + 2, 1] - pp
174 if dpabove < TOLSP:
175 if idp > 1:
176 lsmp = True
177 ismp = -1
178 fsp = (dpabove + TOLSP) / (2.0 * TOLSP)
179 elif dpbelow < TOLSP:
180 if idp < nptsx - 3:
181 lsmp = True
182 ismp = 1
183 fsp = 0.5 * dpbelow / TOLSP
184
185 # Radiation pressure (Fortran lines 157–160).
186 y = 1.0 - x - z # noqa: F841 (unused in the Fortran too)
187 prad = betm1 * p
188 pgas = beta * p # noqa: F841 (unused in the Fortran too)
189
190 # Columns 2-6 of TABLENV: log rho, Q_rhoT, Q_rhoP, cP, dU/dT, on the
191 # stencil and, near its edges, on the shifted ones (Fortran lines
192 # 162–307).
193 tempt = _stencil_spline(0, 0, pp, tl)
194 tempt1t = np.zeros(6)
195 tempt2p = np.zeros(6)
196 if lsmt:
197 # Interpolate at the stencil shifted in T.
198 tempt1 = _stencil_spline(ismt, 0, pp, tl)
199 if ismt == -1:
200 tempt1t[1:] = tempt1[1:] + fst * (tempt[1:] - tempt1[1:])
201 else:
202 tempt1t[1:] = tempt[1:] + fst * (tempt1[1:] - tempt[1:])
203 if lsmp:
204 # Interpolate at the stencil shifted in P.
205 tempt2 = _stencil_spline(0, ismp, pp, tl)
206 if ismp == -1:
207 tempt2p[1:] = tempt2[1:] + fsp * (tempt[1:] - tempt2[1:])
208 else:
209 tempt2p[1:] = tempt[1:] + fsp * (tempt2[1:] - tempt[1:])
210 if lsmp and lsmt:
211 # Interpolate at the stencil shifted in both.
212 tempt3 = _stencil_spline(ismt, ismp, pp, tl)
213
214 # Blend the shifted stencils' results (Fortran lines 308–358).
215 if lsmt:
216 if lsmp:
217 # Blend in T at both pressures, then in P.
218 for j in range(1, 6):
219 if ismt == -1:
220 chgt1 = tempt1[j] + fst * (tempt[j] - tempt1[j])
221 chgt2 = tempt3[j] + fst * (tempt2[j] - tempt3[j])
222 else:
223 chgt1 = tempt[j] + fst * (tempt1[j] - tempt[j])
224 chgt2 = tempt2[j] + fst * (tempt3[j] - tempt2[j])
225 if ismp == -1:
226 tempt[j] = chgt2 + fsp * (chgt1 - chgt2)
227 else:
228 tempt[j] = chgt1 + fsp * (chgt2 - chgt1)
229 else:
230 tempt[1:] = tempt1t[1:]
231 elif lsmp:
232 tempt[1:] = tempt2p[1:]
233
234 # Density (Fortran lines 364–365).
235 dl = tempt[1]
236 d = math.exp(c.cln * dl)
237 # Q_rhoP: radiation adds pressure but no density, so divide by beta;
238 # then Q_rhoT, c_P and nabla_ad with radiation (Fortran lines
239 # 369–394).
240 qdp0 = tempt[3]
241 qdp = qdp0 / beta
242 # Q_rhoT = -chi_T Q_rhoP, with chi_T (qpt) corrected for radiation.
243 qdt0 = tempt[2]
244 qpt0 = -qdt0 / qdp0
245 qpt = qpt0 * beta + 4.0 * betm1
246 qdt = -qpt * qdp
247 qcp0 = tempt[4] # noqa: F841 (unused in the Fortran too)
248 # dU/dT from the table, corrected for radiation.
249 qut = tempt[5]
250 ur = 3.0 * prad / d
251 qut = qut + 4.0 * ur / t
252 # cP corrected for radiation pressure, and the adiabatic gradient.
253 qcp = qut - p * qdt * qpt / d / t
254 dela = -p * qdt / d / t / qcp
255
256 # Ionization: particle fractions from the pure-H (TABLEX) and pure-He
257 # (TABLEY) tables, by 4-point Lagrange interpolation on the same stencil
258 # (Fortran lines 403–445).
259 # Question (see the page): Lagrange here, splines above.
260 qr = np.zeros(5)
261 ft = np.zeros(5)
262 ftd = np.zeros(5)
263 fp = np.zeros(5)
264 fpd = np.zeros(5)
265 temp = np.zeros((5, 3))
266 for k in range(1, 5):
267 qr[k] = scv.tlogx[k + idt - 1]
268 interp(qr, ft, ftd, tl)
269 for k in range(1, 5):
270 # Question (see the page): TABLENV's pressures for TABLEX, TABLEY.
271 qr[k] = scv.tablenv[idt, idp + k - 1, 1]
272 interp(qr, fp, fpd, pp)
273
274 def lagrange(col):
275 """Columns col of TABLEX and TABLEY at (tl, pp)."""
276 for i in range(1, 5):
277 ii = idt + i - 1
278 temp[i, 1] = sum(fp[k] * scv.tablex[ii, idp + k - 1, col]
279 for k in range(1, 5))
280 temp[i, 2] = sum(fp[k] * scv.tabley[ii, idp + k - 1, col]
281 for k in range(1, 5))
282 return (sum(ft[k] * temp[k, 1] for k in range(1, 5)),
283 sum(ft[k] * temp[k, 2] for k in range(1, 5)))
284
285 # Column 2: fractions of all particles that are H2, and neutral He.
286 xtf_h2, xtf_he = lagrange(2)
287 # Column 3: fractions that are neutral H atoms, and He+.
288 xtf_h1, xtf_hep = lagrange(3)
289
290 # Particle and charge conservation give the rest (Fortran lines
291 # 455–458).
292 xtf_h_e = 0.5 * (1.0 - xtf_h2 - xtf_h1)
293 xtf_hp = xtf_h_e
294 xtf_he_e = (1.0 / 3.0) * (2.0 - 2.0 * xtf_he - xtf_hep)
295 xtf_hepp = 1.0 - xtf_he - xtf_hep - xtf_he_e
296
297 # Fractions of H and He nuclei in each stage (Fortran lines 463–470).
298 # Suspected bug (see the page): H2 counted once in the denominator.
299 xhp = xtf_hp / (xtf_h2 + xtf_h1 + xtf_hp)
300 xhep = xtf_hep / (xtf_he + xtf_hep + xtf_hepp)
301 xhepp = xtf_hepp / (xtf_he + xtf_hep + xtf_hepp)
302 fxion[1] = xhp
303 fxion[2] = xhep
304 fxion[3] = xhepp
305 return dl, d, qdt, qdp, qcp, dela, lcalc