1C
2C
3C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
4C ENVINT
5C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
6 SUBROUTINE ENVINT(B,FPL,FTL,GL,HSTOT,IE,LPRT,LSBC,PLIM,RL,TEFFL,
7 * * X,Z,ESTORE,ISTORE,KATM,KENV,KSAHA,PS,RS,TS,
8 * * LPULPT,CMIXL)
9C 10/15/26 MHP via Claude
10C code changed to take JSON from DIMENSIONS_MOD instead of declaring
11C PARAMETER(JSON=5000) here.
12 USE DIMENSIONS_MOD
13C 10/14/26 MHP via Claude
14C code changed to convert COMMON/ACDPTH/ into ACDPTH_MOD.
15 USE ACDPTH_MOD
16C 10/14/26 MHP via Claude
17C code changed to convert COMMON/QENVMIX/ into QENVMIX_MOD.
18 USE QENVMIX_MOD
19C 10/14/26 MHP via Claude
20C code changed to convert COMMON/PULSE2/ into PULSE2_MOD.
21 USE PULSE2_MOD
22C 10/14/26 MHP via Claude
23C code changed to convert COMMON/PULSE1/ into PULSE1_MOD.
24 USE PULSE1_MOD
25C 10/14/26 MHP via Claude
26C code changed to convert COMMON/PUALPHA/ into PUALPHA_MOD.
27 USE PUALPHA_MOD
28C 10/14/26 MHP via Claude
29C code changed to convert COMMON/OVRTRN/ into OVRTRN_MOD.
30 USE OVRTRN_MOD
31C 10/14/26 MHP via Claude
32C code changed to convert COMMON/ENVSTRUCT/ into ENVSTRUCT_MOD.
33 USE ENVSTRUCT_MOD
34C 10/14/26 MHP via Claude
35C code changed to convert COMMON/ENVPRT/ into ENVPRT_MOD.
36 USE ENVPRT_MOD
37C 10/14/26 MHP via Claude
38C code changed to convert COMMON/COMP/ into COMP_MOD.
39 USE COMP_MOD
40C 10/14/26 MHP via Claude
41C code changed to convert COMMON/ATMSTRUCT/ into ATMSTRUCT_MOD.
42 USE ATMSTRUCT_MOD
43C 10/14/26 MHP via Claude
44C code changed to convert COMMON/ATMPRT/ into ATMPRT_MOD.
45 USE ATMPRT_MOD
46C 10/14/26 MHP via Claude
47C code changed to convert COMMON/ATMOS/ into ATMOS_MOD.
48 USE ATMOS_MOD
49C 10/14/26 MHP via Claude
50C code changed to convert COMMON/NMLPAR/ into NMLPAR_MOD.
51 USE NMLPAR_MOD
52C 10/14/26 MHP via Claude
53C code changed to convert COMMON/CONSTANTS/ into CONSTANTS_MOD.
54 USE CONSTANTS_MOD
55C 10/14/26 MHP via Claude
56C code changed to convert COMMON/INTPAR/ into INTPAR_MOD.
57 USE INTPAR_MOD
58C 10/14/26 MHP via Claude
59C code changed to convert COMMON/IOUNITS/ into IOUNITS_MOD.
60 USE IOUNITS_MOD
61C 10/20/26 MHP via Claude
62C code changed to replace implicit typing with IMPLICIT NONE and
63C explicit declarations.
64 IMPLICIT NONE
65 REAL*8 AMU,ATMDX,B,BETA,BETA14,BETAI,CHDELD,CHDELJ,CHRH,CHT,CMIXL,
66 * * CV,D,DD1,DD2,DE,DEL,DELA,DELR,DELTR,DL,DN,DP,DR,DUM1,DYDX,
67 * * EG,EMU,ENVCZL,EPS,ERR,ERRSUM,ESTORE,ETA,FPL,FTL,FX,FXION,
68 * * GAM1,GL,H,HDID,HMAX,HMIN,HNEXT,HRA,HSTOT,O,OL,ON,OP,P,
69 * * PELPF,PL,PLIM,PS,QAP,QAT,QCP,QCPP,QCPT,QDP,QDT,QDTP,QDTT,
70 * * QESUM,QOD,QOT,RL,RMU,RS,RSURF,SDIF,SEST,SGL,SL,SRAD,STOLER,
71 * * T,TAUCZENV,TAUN,TAUP,TEFFL,TENV,TINY,TL,TS,WT0,X,X0,XLIM,
72 * * XSTART,XYZ,Y,YSCAL,YSTART,Z
73 INTEGER I,I1,I2,IE,ISTORE,J,JENV,K,KATM,KENV,KSAHA,MAXSTP,NBAD,
74 * * NOK,NSTEP,NV
75 LOGICAL*4 LALFAIL,LATMO,LDERIV,LDUM,LLSTORE,LOCOND,LPRT,LPULPT,
76 * * LSBC,LSURCZ,LTEST
77
78C PARAMETERS NT AND NG FOR TABULATED SURFACE PRESSURES OF KURUCZ.
79C JNT 06/14 ADDED NTC/NGC FOR KTTAU=5
80C MHP 8/97 ADDED NTA AND NGA FOR ALLARD ATMOSPHERE TABLES
81
82 REAL*8 ENVS1(JSON),ENVS2(JSON),ENVG(JSON),EDEL1(JSON),
83 * * EDEL2(JSON)
84C 9/25/26 MHP via Claude
85C code changed to consolidate all I/O logical unit numbers, previously
86C scattered across COMMON/LUOUT/, COMMON/LUNUM/, COMMON/ALEX06/,
87C COMMON/ACDPTH/, COMMON/NEWOPAC/, COMMON/LOPAL95/, COMMON/ATMOS2/,
88C COMMON/ALATM03/, COMMON/OPALEOS/, and COMMON/SCV2/, into a single
89C COMMON/IOUNITS/
90C 9/24/26 MHP via Claude
91C code changed to remove dead ILLDAT, IKUR from COMMON/LUNUM/
92C 9/24/26 MHP via Claude
93C code changed to remove dead IDYN from COMMON/LUNUM/
94C 9/25/26 MHP via Claude
95C code changed to remove dead COMMON/LUNUM/ISCOMP (declared,
96C assigned a unit number, but never opened or read anywhere)
97C DBG CHANGED MAXSTEP FROM 200 TO 2000 TO GIVE ATMOSPHERE INTEGRATER A CHANCE.
98 PARAMETER (MAXSTP = 2000,TINY=1.0D-30)
99 EXTERNAL QATM,QENV
100C MHP 8/25 Removed character file names from common block
101C 9/24/26 MHP via Claude
102C code changed to merge COMMON /MISCOPAC/ and COMMON/NWLAOL/ into
103C COMMON /NEWOPAC/
104C 10/12/26 MHP via Claude
105C code changed to extend COMMON/NMLPAR/ to 234 members, adding the
106C namelist variables that the physics routines read from 27 COMMON
107C blocks which also held derived or working members. Those blocks keep
108C only their non-namelist members; five of them -- NEWMX, VMULT,
109C PMMWIND, CCOUT1 and ENVGEN -- had nothing left and are gone.
110C Derivation-only namelist variables from the same blocks went to
111C COMMON/NMLBYPASS/ instead, not here.
112C
113C Declared identically in all 121 files that hold any of these members.
114C Generated from one member list, so every copy is byte-identical; the
115C linker's 'size of symbol changed' warning is the check that they are.
116C 10/13/26 MHP via Claude: removed the unused COMMON/NEWOPAC/ declaration
117C (no member referenced here; residue of the obsolete-option removals).
118C DBG PULSE: CONSTANTS NEEDED FOR DEL AND DELA CALCULATION
119C 9/25/26 MHP via Claude
120C code changed to merge COMMON/CONST/, COMMON/CONST1/, COMMON/CONST2/,
121C and COMMON/CONST3/ into a single COMMON/CONSTANTS/
122C 9/24/26 MHP via Claude
123C code changed to pass CMIXL as an explicit argument instead of via
124C common block, to reduce implicit global state ahead of the F90
125C module conversion
126C 10/13/26 MHP via Claude
127C code changed to drop COMMON/CTLIM/. TENV is simply 0.5*(TENV0+TENV1)
128C and parmin.f was computing it there purely to share it with this, its
129C only reader. Both TENV0 and TENV1 are members of the consolidated
130C namelist block declared here, so TENV is derived locally below
131C instead, which removes the block without touching any signature.
132C 10/12/26 MHP via Claude
133C code changed to remove COMMON/OPTAB/ entirely. Once OPTOL moved to the
134C consolidated namelist block, every remaining member was write-only:
135C ZSI appeared only in a DATA statement in parmin.f, and IDT and IDD were
136C assigned the constants 15 and 5 in coefft.f, envint.f, physic.f and
137C starin.f and read nowhere in the code. They are leftovers from the
138C removed opacity-table interpolation schemes.
139C DBG PULSE
140C 10/13/26 MHP via Claude
141C code changed to drop COMMON/PULSE/, declared here but never used.
142C 10/13/26 MHP via Claude: removed the unused COMMON/ATMOS2/ declaration
143C (no member referenced here; residue of the obsolete-option removals).
144C JNT 6/14 ADD FOR KURUCZ/CASTELLI 2004 ATMOSPHERES
145C 10/13/26 MHP via Claude: removed the unused COMMON/ATMOS2C/ declaration
146C (no member referenced here; residue of the obsolete-option removals).
147C MHP 6/97 ADDED ALLARD MODEL ATMOSPHERES
148C COMMON/ATMOS3/ATMZA,ATMPLA(NTA,NGA),
149C *ATMTLA(NTA),ATMGLA(NGA),FALLARD,IOATMA
150C MHP 8/25 Removed character file names from common block
151C 10/13/26 MHP via Claude
152C code changed to remove COMMON/ALATM04/: its four members DUMMY1..DUMMY4
153C are placeholders that no file references in executable code. Removing
154C it also retires the hazard behind the earlier alfilein.f fix, where a
155C mis-indented EXTERNAL was absorbed into this declaration and renamed
156C DUMMY4.
157C DBG 7/95 To store variables for pulse output
158C MHP 07/02 STORE CONTENTS OF ENVELOPE INTEGRATION INTO A
159C SET OF VECTORS, WHICH ARE FLIPPED AND CONVERTED INTO AN ASCENDING
160C SERIES AFTER THE INTEGRATION IS DONE.
161C KC 2025-05-30 reordered common block elements
162C JvS 08/25 Updated with new elements
163C JvS SAVE ATMOSPHERE STRUCTURE TO MAKE PROFILE OUTPUT EASIER
164C JVS 08/13 IF THE CZ IS BEYOND THE FITTING POINT, STORE ITS LOCATION
165C 10/13/26 MHP via Claude
166C code changed to remove COMMON/ENVCZ/: ENVRCZ was written once in
167C envint.f and read nowhere, and RINT was never referenced at all. Its
168C only reader was the commented-out JVS block deleted from wrtout.f
169C earlier in this work, so the block died with it.
170
171C MHP 08/02 ADDED VECTOR FOR STORING THE OVERTURN TIMESCALE OF THE
172C SURFACE CONVECTION ZONE
173C 10/13/26 MHP via Claude: removed the unused COMMON/DEUTER/ declaration
174C (no member referenced here; residue of the obsolete-option removals).
175 DIMENSION FXION(3),PS(3),RS(3),TS(3),ESTORE(4)
176 DIMENSION Y(3),DYDX(3),YSCAL(3),YSTART(3),ERRSUM(3),ERR(3)
177
178C JVS Acoustic depth common block
179C KC 2025-05-30 reordered common block elements
180C COMMON/ACDPTH/TAUCZN,DELADJ(JSON),TAUHE, TNORM, TCZ, WHE, ICLCD,
181C 9/25/26 MHP via Claude
182C code changed to remove dead IACAT, IJVS, IJENT, IJDEL unit
183C reservations from COMMON/ACDPTH/ (declared but never opened anywhere)
184C *LCLCD, AGEOUT(5), IACAT, IJLAST, LJLAST, LJWRT, LADON,LAOLY, IJVS,
185
186C 10/13/26 MHP via Claude: removed the unused COMMON/CCOUT/ declaration
187C (no member referenced here; residue of the obsolete-option removals).
188C G Somers END
189
190C G Somers 3/17, ADDING NEW TAUCZ COMMON BLOCK
191
192C MHP 1/01 CHANGED END OF FILE INDICATOR IN ATMOSPHERE/ENVELOPE FILES TO
193C VECTOR FROM SCALAR
194 DIMENSION XYZ(22)
195 DATA XYZ/22*99.99D0/
196C 9/24/26 MHP via Claude
197C QENV cannot receive CMIXL as a normal argument (it is invoked
198C polymorphically as the DERIV callback by MMID/BSSTEP with a fixed
199C argument list -- see EXTERNAL QENV below). Relay CMIXL to it via a
200C small dedicated common block instead, kept current on every entry to
201C ENVINT so it is correct however QENV ends up being reached.
202 SAVE
203
204 CMIXLQ = CMIXL
205C DBG PULSE TURN ON DERIVATIVE CALCULATOR
206 IF (LPULPT.AND.LPRT) THEN
207 LPUMOD = .TRUE.
208 LDERIV = .TRUE.
209 LATMO = .FALSE.
210 ELSE
211 LPUMOD = .FALSE.
212 LDERIV = .FALSE.
213 LATMO = .TRUE.
214 END IF
215 LOCOND = .FALSE.
216
217C JVS 10/07/13 Always calculate derivatives
218 LDERIV = .TRUE.
219
220C G Somers 11/14 WRITE ATMOSHPHERE HEADER TO .STORE FILE, AND ADDED
221C I/O FLAGS TO THE ATMOSPHERE CALLS
222 IF(LPRT.AND.LSTATM)THEN
223 IF(.NOT.LSTCH)WRITE(ISTOR,60)
224 ENDIF
225 60 60 FORMAT(/,'******** ATMOSPHERE BEGIN ********')
226
227C GET PRESSURE AT T=Teff BY INTERPOLATION IN TABLE ATMPL.
228 IF (KTTAU .EQ. 3) THEN
229C KURUCZ ATMOSPHERES
230 IF(LSTCH) LSTATM=.FALSE.
231 CALL SURFP(TEFFL,GL,LPRT.AND.LSTATM)
232 GOTO 200
233C JNT 06/14
234C GET PRESSURE AT T=Teff BY INTERPOLATION IN TABLE ATMPLC.
235 ELSE IF (KTTAU .EQ. 5) THEN
236C KURUCZ ATMOSPHERES
237 IF(LSTCH) LSTATM=.FALSE.
238 CALL KCSURFP(TEFFL,GL,LPRT.AND.LSTATM)
239 GOTO 200
240C We have Kurucz atmosphere boundary conditions
241 ELSE IF (KTTAU .EQ. 4) THEN
242C ALLARD & HAUSCHILDT ATMOSPHERES
243 IF(LSTCH) LSTATM=.FALSE.
244 CALL ALSURFP(TEFFL,GL,LPRT.AND.LSTATM,LAlFail)
245C Changed to Allard atmosphere code
246 IF(LAlFail) then
247 KTTAU=0
248 LTTAU = .TRUE.
249C Set to gray atmosphere (KTTAU=0), as
250C TeffL is above Allard max, or GL is out of range.
251 WRITE(*,*) 'ENVINT: Change to gray atmosphere (KTTAU=0)'
252 WRITE(ISHORT,*)'ENVINT: Change to gray atmosphere (KTTAU=0)'
253 goto 2
254 ENDIF
255 GOTO 200
256C We have Allard atmosphere boundary conditions
257 ENDIF
258 2 2 CONTINUE
259C Start gray atmosphere bounary conditions
260C GUESS THE TEMPERATURE FOR AN OPTICAL DEPTH NEAR ZERO.
261 ERRSUM(1) = 0.0D0
262 IF(KTTAU .EQ. 0) THEN
263 TL = TEFFL - 0.031235D0 + 0.25D0*DLOG10(CC23)
264 ELSE IF (KTTAU .EQ. 1) THEN
265 TL = TEFFL - 0.031235D0 + 0.25D0*DLOG10(0.550D0)
266 ELSE IF (KTTAU .EQ. 2) THEN
267 TL = TEFFL + HRA(CC23) - HRAS
268 END IF
269C For kttau = 0,1,or 2, very occasionally the integration
270C fails because the starting point (X0) is past the end
271C point (XLIM). When this happens, we divide the effective
272C starting density (atmd0) by 10 and retry.
273 atmdx = atmd0
274 1998 1998 continue
275C Return point if X0 > XLIM
276 T = DEXP(CLN*TL)
277C FIND THE PRESSURE CORRESPONDING TO THIS T AND THE DENSITY CHOSEN
278C FOR THE START OF THE ATMOSPHERE INTEGRATION.
279
280c PL = DLOG10((CGAS*ATMD0 + CA3*T**3)*T)
281 PL = DLOG10((CGAS*ATMDX + CA3*T**3)*T)
282
283C NOW FIND THE OPTICAL DEPTH(X0) WHERE THE ATMOSPHERE INTEGRATION BEGINS.
284C 9/22/26 MHP via Claude
285C code changed to remove the MHD equation of state
286 CALL EQSTAT(TL,T,PL,P,DL,D,X,Z,BETA,BETAI,BETA14,FXION,RMU,
287 * * AMU,EMU,ETA,QDT,QDP,QCP,DELA,QDTT,QDTP,QAT,QAP,QCPT,
288 * * QCPP,LDERIV,LATMO,KSAHA)
289C DBG 12/95 GET OPACITY
290 CALL GETOPAC(DL, TL, X, Z, O, OL, QOD, QOT, FXION)
291 X0 = PL - GL + DLOG10(O)
292 Y(1) = PL
293C 10/29/26 MHP via Claude
294C code changed to include FPL in the starting slope. QATM, which
295C supplies every later slope, uses G = g*FPL; without it the first
296C Bulirsch-Stoer step of a rotating star began from slope 1, not FPL.
297 DYDX(1) = DEXP(CLN*(GL+X0-OL-PL))*FPL
298c G Somers 11/14 ADDED I/O FLAG AND CHANGED WRITE OUTS TO .STORE.
299 IF(LPRT.AND.LSTATM) THEN
300 IF(.NOT.LSTCH) WRITE(ISTOR,10)
301 BETA = 1.0D0 - CA3*(T**2)**2/P
302 CHRH = 1.0D0/QDP
303 CHT = -CHRH*QDT
304 CV = QCPP - EXP(CLN*(PL-DL-TL))*CHT**2/CHRH
305 GAM1 = CHRH*QCPP/CV
306 IF(.NOT.LSTCH)THEN
307 WRITE(ISTOR,20)X0,PL,TL,DL,O,(FXION(I),I=1,3),KSAHA,KATM,
308 * * GAM1,QDP,QDT,BETA,QCPP,CV
309 ENDIF
310 10 10 FORMAT(6X,'TAU',9X,'P',10X,'T',10X,'D',11X,'O',9X,
311 * * 'HII HEII HEIII SAHA KATM')
312 20 20 FORMAT(1X,4F11.7,1PE14.7,0P3F6.3,2I7,4F12.8,1P2E12.5)
313 ENDIF
314C DBG PULSE INITIAL POINT FOR PULSATION
315 IF (LPULPT.AND.LPRT) THEN
316 QQED = 0.0D0
317 QESUM = 0.0D0
318 QQET = 0.0D0
319 QFS = 1.0D0
320 TAUP = DEXP(CLN*X0)
321 OP = O
322 DP = DEXP(CLN*DL)
323 DELTR = 0.0D0
324 PELPF = CGAS * T * D * EMU
325C FROM FIRST LINES OF TPGRAD
326 DELR = O*B*DEXP(CLN*(PL-HSTOT-4.0D0*TL+CLSUNL-CGL+
327 * * CDELRL))*FTL/FPL
328 IF (DELR-DELA .LE. 1.0D-6) THEN
329 DEL = DELR
330 ELSE
331 DEL = DELA
332 END IF
333 IF(IPVER.EQ.1) THEN
334C MHP 10/02 TYPO - QED SHOULD HAVE BEEN QQED
335C * PL, QESUM,O,QDP,QED,
336 WRITE(IOPATM,5001)DELTR,QFS,B,TL,DL,
337 * * PL, QESUM,O,QDP,QQED,
338 * * QQET,QOD,QOT,DEL,DELA,
339 * * QCP,RMU,QDT,PELPF
340C * PL, TAUP ,O,QDP,QED,
341 ELSE IF (IPVER.EQ.2 .OR. IPVER.EQ.3) THEN
342 WRITE(IOPATM,6001)DELTR,QFS,B,TL,DL,
343 * * PL, TAUP ,O,QDP,QQED,
344 * * QQET,QOD,QOT,DEL,DELA,
345 * * QCP,RMU,QDT,PELPF
346 END IF
347
348 5001 5001 FORMAT(5E16.9,/,5E16.9,/,5E16.9,/,4E16.9)
349 6001 6001 FORMAT(5E23.16,/,5E23.16,/,5E23.16,/,4E23.16)
350 6003 6003 FORMAT(6E23.16,/,6E23.16,/,6E23.16,/,4E23.16)
351 END IF
352
353
354
355C INTEGRATE DP/DTAU FROM THIS STARTING TAU TO TAU = 2/3.
356C SET NUMERICAL PARAMETERS UP.
357 NV = 1
358 EPS = ATMERR
359 IF (KTTAU .EQ. 1) THEN
360C KRISHNA-SWAMY T TAU HAS DIFFERENT ZERO THAN EDDINGTON T TAU
361C TAU = 0.312156330 AT TEFF.
362 XLIM = -0.505627854D0
363 ELSE
364C TAU = 2/3 AT TEFF.
365 XLIM = -0.176091259D0
366 END IF
367
368 if (x0 .gt. xlim) then ! Check that starting point is before endpoint
369 write(ishort,*)"ENVINT: X0>XLIM, X0,XLIM:",x0,xlim
370 write(ishort,*)"ENVINT: get new X0 by dividing ATMD0 by 10"
371 atmdx = atmdx / 10D0 ! If not before, divide starting density by 10
372 goto 1998 ! and retry.
373 endif
374
375 HMAX = ATMMAX
376 HMIN = ATMMIN
377 H = ATMBEG
378 STOLER = STOLR0
379 NOK = 0
380 NBAD = 0
381
382C INTEGRATION LOOP.
383 DO 40 NSTEP = 1,MAXSTP
384C YSCAL IS THE ARRAY THAT THE NUMERICAL ERRORS ARE SCALED AGAINST.
385 DO 30 I = 1,NV
386 YSCAL(I) = DABS(Y(I)) + DABS(H*DYDX(I))+TINY
387 30 30 CONTINUE
388C ENSURE THAT STEP DOESN'T EXCEED MAXIMUM STEP SIZE OR GO PAST
389C THE LIMIT OF THE INTEGRATION.
390 IF((X0-XLIM)*(X0+H-XLIM).LT.0.0D0) H = XLIM - X0
391 IF(H.GT.HMAX) H = HMAX
392C INTEGRATE THE ATMOSPHERE FROM TAU TO TAU + H
393C H IS THE ATTEMPTED STEP,HDID IS THE ONE PERFORMED, AND HNEXT IS THE
394C PREDICTED NEXT STEP.
395 CALL BSSTEP(Y,DYDX,NV,X0,H,EPS,YSCAL,HDID,
396 x x HNEXT,QATM, B,FPL,FTL,GL,LATMO,LDERIV,LOCOND,LPRT,RL,
397 X X TEFFL,X,Z,KATM,KSAHA,ERR)
398C FIND DP/DTAU AT THE START OF THE NEXT STEP.
399 ERRSUM(1) = ERRSUM(1) + ERR(1)
400 CALL QATM(X0,Y,DYDX,B,FPL,FTL,GL,LATMO,LDERIV,LOCOND,
401 * * LPRT,RL,TEFFL,X,Z,KATM,KSAHA)
402c G Somers 11/14 ADDED I/O FLAG AND CHANGED WRITE OUTS TO .STORE.
403 IF(LPRT.AND.LSTATM) THEN
404 BETA = 1.0D0 - CA3*EXP(CLN*(4.0D0*AT-AP))
405 CHRH = 1.0D0/QQDP
406 CHT = -CHRH*QQDT
407 CV = QQCP - EXP(CLN*(AP-AD-AT))*CHT**2/CHRH
408 GAM1 = CHRH*QQCP/CV
409 IF(.NOT.LSTCH)THEN
410 WRITE(ISTOR,20)TAUL,AP,AT,AD,AO,(AFXION(I),I=1,3),
411 * * KSAHA,KATM,GAM1,QQDP,QQDT,BETA,QQCP,CV
412 ENDIF
413C JvS: SAVE STRUCTURE TO COMMON BLOCK
414 ATMOP(NSTEP) = AP
415 ATMOT(NSTEP) = AT
416 ATMOD(NSTEP) = AD
417 ABETAS(NSTEP) = BETA
418 AGAM1(NSTEP) = GAM1
419 AQDT(NSTEP) = QQDT
420 AFXIONS(1,NSTEP) = AFXION(1)
421 AFXIONS(2,NSTEP) = AFXION(2)
422 AFXIONS(3,NSTEP) = AFXION(3)
423 ATMOO(NSTEP) = AO
424 ATMOCP(NSTEP) = QQCP
425 ENDIF
426 IF(HDID.EQ.H) THEN
427 NOK = NOK + 1
428 ELSE
429 NBAD = NBAD + 1
430 ENDIF
431C DBG PULSE ATMOSPHERE VALUES FOR PULSATION
432C JVS 02/11 - Added LCLCD option to IF statement
433 IF ((LPULPT.AND.LPRT) .OR. LCLCD .OR. LSTCH) THEN
434 QQED = 0.0D0
435 QESUM = 0.0D0
436 QQET = 0.0D0
437 QFS = 1.0D0
438 ON = QO
439 DN = DEXP(CLN*QDL)
440 TAUN = DEXP(CLN*TAUL)
441C SEE J.P. COX PRINC. OF STELL. STRUC. P590
442 DELTR = (TAUN - TAUP)/(((DN*ON)+(DP*OP))/2)
443 PELPF = CGAS * QT * QD * QEMU
444 OP = ON
445CFROM FIRST LINES OF TPGRAD
446 DELR = QO*B*DEXP(CLN*(QPL-HSTOT-4.0D0*QTL+CLSUNL-CGL+
447 * * CDELRL))*FTL/FPL
448 IF (DELR-QDELA .LE. 1.0D-6) THEN
449 DEL = DELR
450 ELSE
451 DEL = QDELA
452 END IF
453 IF (LPULPT.AND.LPRT) THEN
454 IF (IPVER.EQ.1) THEN
455 WRITE(IOPATM,5001)DELTR,QFS,B,QTL,QDL,
456 * * QPL, QESUM ,QO,QQDP,QQED,
457 * * QQET,QQOD,QQOT,DEL,QDELA,
458 * * QQCP,QRMU,QQDT,PELPF
459 ELSE IF (IPVER.EQ.2.OR.IPVER.EQ.3) THEN
460 WRITE(IOPATM,6001)DELTR,QFS,B,QTL,QDL,
461 * * QPL, TAUN ,QO,QQDP,QQED,
462 * * QQET,QQOD,QQOT,DEL,QDELA,
463 * * QQCP,QRMU,QQDT,PELPF
464 END IF
465 END IF
466C JvS SAVE TO COMMON ATMSTRUCT COMMON BLOCK
467 ATMOR(NSTEP) = DELTR
468 ADELS(1,NSTEP) = DELR
469 ADELS(2,NSTEP) = DEL
470 ADELS(3,NSTEP) = QDELA
471 NUMATM = NSTEP
472
473 TAUP = TAUN
474 DP = DN
475 END IF
476C DBG END
477
478
479C CHECK IF INTEGRATION COMPLETE
480 IF(DABS(X0 - XLIM).LE.STOLER) THEN
481 WRITE(ISHORT,35)NOK,NBAD,ERRSUM(1)
482 35 35 FORMAT(1X,'ATMOSPHERE INTEGRATION COMPLETE',1X,
483 * * 'NUMBER OF STEPS ACCEPTED',I5,' REJECTED',
484 * * I5/5X,'MAXIMUM RELATIVE ERROR IN P ',1PE22.13)
485 GOTO 200
486 ENDIF
487 IF(HNEXT.LT.HMIN) HNEXT = HMIN
488 H = HNEXT
48940 40 CONTINUE
490C INTEGRATION HAS FAILED TO FINISH IN MAXSTP STEPS;
491C PRINT NASTY MESSAGE AND QUIT.
492 WRITE(IOWR,50)
493 WRITE(ISHORT,50)
494 50 50 FORMAT(5X,'ATMOSPHERE INTEGRATION FAILED AFTER MAXSTP',1X,
495 * * 'INTEGRATIONS.I QUIT.')
496C 10/30/26 MHP via Claude: bare STOP -> STOP 1, the run-time error code (issue 38).
497 STOP 1
498C ENVELOPE INTEGRATION
499C HERE P IS THE INDEPENDENT VARIABLE AND M,R,AND T ARE
500C DEPENDENT VARIABLES. INTEGRATE FROM TAU = 2/3 TO THE LAST
501C MASS POINT IN THE MODEL.
502
503
504 200 200 CONTINUE ! Kurucz and Allard (KTTAU=3 and 4) bypass atmosphere
505 ! integration and come here
506C G Somers 3/17, IF INTERESTED ONLY IN PPHOT, BREAK HERE.
507 PPHOT = AP
508 IF(.NOT.LCALCENV) GOTO 555
509
510C G Somers 11/14 WRITE ENVELOPE HEADER
511 IF(LPRT.AND.LSTENV)THEN
512 IF(.NOT.LSTCH) WRITE(ISTOR,61)
513 ENDIF
514 61 61 FORMAT(/,'******** ENVELOPE BEGIN ********')
515
516C DBG PULSE WRITE END OF DATA INDICATOR
517 IF (LPULPT.AND.LPRT) THEN
518C XYZ = 99.99D0
519 IF(IPVER.EQ.1) THEN
520 WRITE(IOPATM, 5001) (XYZ(I),I=1,19)
521 ELSE IF (IPVER.EQ.2.OR.IPVER.EQ.3) THEN
522 WRITE(IOPATM, 6001) (XYZ(I),I=1,19)
523 END IF
524C 5002 FORMAT(E16.9)
525C 6002 FORMAT(E23.16)
526 ENDIF
527C DBG
528C IF ENVELOPE MASS(SENV) SMALL ENOUGH,SKIP ENVELOPE INTEGRATION.
529C DBG 2/92 CHANGED FROM 1.0D-10 to 1.0D-12
530 IF(SENV.GT.-1.0D-12) THEN
531 IF(LSBC) THEN
532 PS(IE) = AP
533 RS(IE) = RL
534 TS(IE) = AT
535 IF(LPRT)THEN
536 IF(.NOT.LSTCH)WRITE(ISTOR,230)PS(IE),TS(IE),RS(IE),SENV
537 ENDIF
538 ENDIF
539 230 230 FORMAT(4X,3F16.12,8X,F16.12)
540 GOTO 300
541 ENDIF
542C INITIALIZE VARIABLES AND SET NUMERICAL PARAMETERS.
543 LATMO = .FALSE.
544 DO 235 I = 1,3
545 ERRSUM(I) = 0.0D0
546 235 235 CONTINUE
547 NOK = 0
548 NBAD = 0
549 X0 = AP
550 SL = 0.0D0
551 Y(1) = SL
552 Y(2) = AT
553 Y(3) = RL
554 NV = 3
555 EPS = ENVERR
556 HMAX = ENVMAX
557 HMIN = ENVMIN
558 H = ENVBEG
559 STOLER = DABS(STOLR0*SENV)
560 RSURF = DEXP(CLN*RL)
561 IF(ISTORE.EQ.IE) ISTORE = 0
562 LLSTORE = .FALSE.
563 IF (LSBC) THEN
564 LTEST = .FALSE.
565 ELSE
566 LTEST = .TRUE.
567 END IF
568C FIND DY/DX AT THE START OF THE STEP.
569 CALL QENV(X0,Y,DYDX,B,FPL,FTL,GL,LATMO,LDERIV,LOCOND,
570 * * LPRT,RL,TEFFL,X,Z,KENV,KSAHA)
571C DBG PULSE WRITE FIRST POINT OF ENVELOPE
572 IF (LPULPT.AND.LPRT) THEN
573C REWIND IOPENV
574 QQED = 0.0D0
575 QESUM = 0.0D0
576 QQET = 0.0D0
577 PELPF = CGAS * QT * QD * QEMU
578 IF(IPVER.EQ.1) THEN
579 WRITE(IOPENV,5001)RL,QFS,B,QTL,QDL,
580 * * QPL, QESUM,QO,QQDP,QQED,
581 * * QQET,QQOD,QQOT,QDEL,QDELA,
582 * * QQCP,QRMU,QQDT,PELPF
583 ELSE IF (IPVER.EQ.2) THEN
584 WRITE(IOPENV,6001)RL,QFS,B,QTL,QDL,
585 * * QPL, QESUM,QO,QQDP,QQED,
586 * * QQET,QQOD,QQOT,QDEL,QDELA,
587 * * QQCP,QRMU,QQDT,PELPF
588 ELSE IF (IPVER.EQ.3) THEN
589C DBG 7/95 Appended mixing length info at end of first three lines
590 WRITE(IOPENV,6003)RL,QFS,B,QTL,QDL,ALFMLT,
591 * * QPL, QESUM,QO,QQDP,QQED,PHMLT,
592 * * QQET,QQOD,QQOT,QDEL,QDELA,CMXMLT,
593 * * QQCP,QRMU,QQDT,PELPF
594 END IF
595 END IF !uncertain!
596C DBG
597c G Somers 11/14 ADDED I/O FLAG AND CHANGED WRITE OUTS TO .STORE.
598 IF(LPRT.AND.LSTENV) THEN
599 IF(.NOT.LSTCH) WRITE(ISTOR,240) 'GRAV ','P ','T ','DEPTH ','M ',
600 * * 'D ','O ','BETA','DELR ','DELA','DEL ','HII ',
601 * * 'HEII','HEIII','V ','GAM1 ','QQDP '
602 240 240 FORMAT(1X,3A10,2A14,2A10,A7,A9,5A6,A9,2A12)
603c X' ',4X,'GRAV',7X,'P',8X,'T',8X,'DEPTH',6X,'M',
604c * 9X,'D',6X,'O',7X,'BETA',3X,'DELR',5X,'DELA',3X,
605c * 'DEL',3X,'HII',3X,'HEII',1X,'HEIII',3X,'V')
606
607c 260 FORMAT(1X,1PE10.3,0P2F10.7,1P2E14.7,0P1F10.7,
608c * 1PE10.2,0PF7.3,1PE9.2,0P3F6.3,2F6.3,1PE9.2,
609c * 0P2F12.8)
610
611
612 SRAD = DEXP(CLN*RL)
613 DE = (RSURF-SRAD)/RSURF
614 SGL = CGL+HSTOT-2.0D0*RL
615 EG = DEXP(CLN*SGL)
616 CHRH = 1.0D0/QQDP
617 CHT = -CHRH*QQDT
618 CV = QQCP - EXP(CLN*(EP-ED-ET))*CHT**2/CHRH
619 GAM1 = CHRH*QQCP/CV
620 IF(.NOT.LSTCH) WRITE(ISTOR,260)EG,EP,ET,DE,ES,ED,EO,EBETA,
621 * * (EDEL(K),K=1,3),(EFXION(K),K=1,3),EVEL,
622 * * GAM1,QQDP
623 ENDIF
624C STORE STARTING VALUES OF THE INTEGRATION
625C 07/02 INITIALIZE NUMBER OF STORED ENVELOPE POINTS TO 1
626 LSURCZ = .FALSE.
627 TAUCZENV = 0.0D0
628 ENVD(1) = ED
629 ENVP(1) = EP
630 ENVR(1) = ER
631 ENVS(1) = ES
632 ENVT(1) = ET
633 ENVX(1) = X
634 ENVZ(1) = Z
635 LCENV(1) = EVEL.GT.0.0D0
636C JVS 03/28
637 EDELS(1,1) = EDEL(1)
638 EDELS(2,1) = EDEL(2)
639 EDELS(3,1) = EDEL(3)
640 EVELS(1) = EVEL
641 EBETAS(1) = EBETA
642c JVS end
643C JVS 08/25
644C Always save these
645 CHRH = 1.0D0/QQDP
646 CHT = -CHRH*QQDT
647 CV = QQCP - EXP(CLN*(EP-ED-ET))*CHT**2/CHRH
648 EGAM1(1) = CHRH*QQCP/CV
649 EQCP(1) = QQCP
650 EFXIONS(1,1) = EFXION(1)
651 EFXIONS(2,1) = EFXION(2)
652 EFXIONS(3,1) = EFXION(3)
653 ENVO(1) = EO
654 ENVL(1) = B
655 EQDT(1) = QQDT
656C JVS 10/10
657 CHDELJ = EDEL(2)
658 CHDELD = QDELA
659 IF(LCENV(1))LSURCZ = .TRUE.
660 NUMENV = 1
661 DO 220 NSTEP = 1,MAXSTP
662 XSTART = X0
663 DO 210 I = 1,NV
664 YSTART(I) = Y(I)
665 YSCAL(I) = DABS(Y(I)) + DABS(H*DYDX(I))+TINY
666 210 210 CONTINUE
667 SEST = Y(1) + H*DYDX(1)
668 IF(SENV - Y(1).GT.0.0D0 .OR. SENV - SEST.GT.0.0D0) THEN
669C IF THE INTEGRATION HAS OVERSHOT THE FITTING POINT, OR THE NEXT
670C STEP WILL DO SO,LIMIT STEP SIZE OR INTEGRATE BACKWARDS TO THE
671C CORRECT MASS.
672 H = (SENV - Y(1))/DYDX(1)
673 ENDIF
674C ENSURE THAT STEP DOESN'T EXCEED MAXIMUM STEP SIZE
675 IF(H.LT.0.0D0) THEN
676 IF(H.LT.-HMAX) H = -HMAX
677 ELSE
678 IF(H.GT.HMAX) H = HMAX
679 ENDIF
680C PLIM IS AN ESTIMATE OF THE ENDING PRESSURE FOR THE INTEGRATION,
681C BASED ON THE PRESSURE OF THE LAST MODEL POINT.
682C THE FIRST TIME THE INTEGRATOR TRIES TO PASS IT,LIMIT THE STEP
683C IN PRESSURE.
684 IF(LTEST) THEN
685 IF(X0 + H.GT.PLIM) THEN
686 H = PLIM - X0
687 LTEST = .FALSE.
688 ENDIF
689 ENDIF
690C INTEGRATE THE EQUATIONS FROM X0 TO X0 + H
691C H IS THE ATTEMPTED STEP,HDID IS THE ONE PERFORMED, AND HNEXT IS THE
692C PREDICTED NEXT STEP.
693 CALL BSSTEP(Y,DYDX,NV,X0,H,EPS,YSCAL,HDID,HNEXT,QENV,
694 * * B,FPL,FTL,GL,LATMO,LDERIV,LOCOND,LPRT,RL,
695 * * TEFFL,X,Z,KENV,KSAHA,ERR)
696 DO 255 K = 1,3
697 ERRSUM(K) = ERRSUM(K) + ERR(K)
698 255 255 CONTINUE
699C FIND DY/DX AT THE START OF THE NEXT STEP.
700 CALL QENV(X0,Y,DYDX,B,FPL,FTL,GL,LATMO,LDERIV,LOCOND,
701 * * LPRT,RL,TEFFL,X,Z,KENV,KSAHA)
702C DBG PULSE
703 IF (LPULPT.AND.LPRT) THEN
704 QQED = 0.0D0
705 QESUM = 0.0D0
706 QQET = 0.0D0
707 PELPF = CGAS * QT * QD * QEMU
708 IF(IPVER.EQ.1) THEN
709 WRITE(IOPENV,5001)RL,QFS,B,QTL,QDL,
710 * * QPL, QESUM,QO,QQDP,QQED,
711 * * QQET,QQOD,QQOT,QDEL,QDELA,
712 * * QQCP,QRMU,QQDT,PELPF
713 ELSE IF (IPVER.EQ.2) THEN
714 WRITE(IOPENV,6001)RL,QFS,B,QTL,QDL,
715 * * QPL, QESUM,QO,QQDP,QQED,
716 * * QQET,QQOD,QQOT,QDEL,QDELA,
717 * * QQCP,QRMU,QQDT,PELPF
718 ELSE IF (IPVER.EQ.3) THEN
719C DBG 7/95 Appended mixing length info at end of first three lines
720 WRITE(IOPENV,6003)RL,QFS,B,QTL,QDL,ALFMLT,
721 * * QPL, QESUM,QO,QQDP,QQED,PHMLT,
722 * * QQET,QQOD,QQOT,QDEL,QDELA,CMXMLT,
723 * * QQCP,QRMU,QQDT,PELPF
724 END IF
725 END IF
726C DBG END
727c G Somers 11/14 ADDED I/O FLAG AND CHANGED WRITE OUTS TO .STORE.
728 IF(LPRT.AND.LSTENV) THEN
729 SRAD = DEXP(CLN*RL)
730 DE = (RSURF-SRAD)/RSURF
731 SGL = CGL+HSTOT-2.0D0*RL
732 EG = DEXP(CLN*SGL)
733 CHRH = 1.0D0/QQDP
734 CHT = -CHRH*QQDT
735 CV = QQCP - EXP(CLN*(EP-ED-ET))*CHT**2/CHRH
736 GAM1 = CHRH*QQCP/CV
737 IF(.NOT.LSTCH) WRITE(ISTOR,260)EG,EP,ET,DE,ES,ED,EO,EBETA,
738 * * (EDEL(K),K=1,3),(EFXION(K),K=1,3),EVEL,
739 * * GAM1,QQDP
740 260 260 FORMAT(1X,1PE10.3,0P2F10.7,1P2E14.7,0P1F10.7,
741 * * 1PE10.2,0PF7.3,1PE9.2,0P3F6.3,2F6.3,1PE9.2,
742 * * 0P2F12.8)
743 ENDIF
744 IF(HDID.EQ.H) THEN
745 NOK = NOK + 1
746 ELSE
747 NBAD = NBAD + 1
748 ENDIF
749C CHECK IF INTEGRATION COMPLETE
750 SDIF = SENV - Y(1)
751C 07/02 STORE ENVELOPE TERMS IF THE INTEGRATION
752C HAS NOT OVERSHOT THE FITTING POINT.
753 IF(SDIF.LE.STOLER)THEN
754 NUMENV = NUMENV + 1
755 ENVD(NUMENV) = ED
756 ENVP(NUMENV) = EP
757 ENVR(NUMENV) = ER
758 ENVS(NUMENV) = ES
759 ENVT(NUMENV) = ET
760 ENVX(NUMENV) = X
761 ENVZ(NUMENV) = Z
762 LCENV(NUMENV) = EVEL.GT.0.0D0
763C JVS 08/13 ADD RUN FOR CZ CALCULATION
764 EDELS(1,NUMENV) = EDEL(1)
765 EDELS(2,NUMENV) = EDEL(2)
766 EDELS(3,NUMENV) = EDEL(3)
767 EVELS(NUMENV) = EVEL
768 EBETAS(NUMENV) = EBETA
769C JVS 08/25
770C Always save these
771 CHRH = 1.0D0/QQDP
772 CHT = -CHRH*QQDT
773 CV = QQCP - EXP(CLN*(EP-ED-ET))*CHT**2/CHRH
774 EGAM1(NUMENV) = CHRH*QQCP/CV
775 EQCP(NUMENV) = QQCP
776 EFXIONS(1,NUMENV) = EFXION(1)
777 EFXIONS(2,NUMENV) = EFXION(2)
778 EFXIONS(3,NUMENV) = EFXION(3)
779 ENVO(NUMENV) = EO
780 ENVL(NUMENV) = B
781 EQDT(NUMENV) = QQDT
782
783 IF(.NOT.LSURCZ)THEN
784 IF(LCENV(NUMENV))THEN
785 LSURCZ = .TRUE.
786 ENDIF
787 ELSE IF(EVEL.GT.0.0D0)THEN
788 DR = 10.0D0**ENVR(NUMENV-1) -
789 * * 10.0D0**ENVR(NUMENV)
790 TAUCZENV = TAUCZENV + DR/EVEL
791 ENDIF
792 ENDIF
793 IF(DABS(SDIF).LE.STOLER)THEN
794C WRITE(*,*)TAUCZENV/CSECYR
795 IF(LSBC) THEN
796 WT0 = SDIF/(YSTART(1)-Y(1))
797 PS(IE) = X0 + WT0*(XSTART - X0)
798 RS(IE) = Y(3) + WT0*(YSTART(3) - Y(3))
799 TS(IE) = Y(2) + WT0*(YSTART(2) - Y(2))
800 IF(LPRT)WRITE(ISHORT,230)PS(IE),TS(IE),RS(IE),SENV
801 ENDIF
802 GOTO 300
803 ELSE IF(.NOT.LLSTORE) THEN
804C 10/13/26 MHP via Claude
805C TENV was COMMON/CTLIM/, set by parmin.f; derived here instead.
806 TENV = 0.5D0*(TENV0 + TENV1)
807 IF(Y(2).GE.TENV .AND. LSBC) THEN
808 LLSTORE = .TRUE.
809 ISTORE = IE
810 ESTORE(1) = X0
811 ESTORE(2) = Y(2)
812 ESTORE(3) = Y(3)
813 ESTORE(4) = Y(1)
814 ENDIF
815 ENDIF
816 IF(HNEXT.LT.HMIN) HNEXT = HMIN
817 H = HNEXT
818 220 220 CONTINUE
819C INTEGRATION HAS FAILED TO FINISH IN MAXSTP STEPS;
820C PRINT NASTY MESSAGE AND QUIT.
821 WRITE(IOWR,911)
822 WRITE(ISHORT,911)
823 911 911 FORMAT(5X,'ENVELOPE INTEGRATION FAILED AFTER MAXSTP TRIES.',1X,
824 * * 'I QUIT')
825C 10/30/26 MHP via Claude: bare STOP -> STOP 1, the run-time error code (issue 38).
826 STOP 1
827 300 300 CONTINUE
828C 07/02 NOW INVERT THE ENVELOPE VECTOR.
829C 10/30/26 MHP via Claude: .LT. -> .LE., to match the test that decides
830C to integrate (SENV.GT.-1.0D-12 skips); SENV = -1.0D-12 exactly was
831C integrated but not reversed.
832 IF(SENV.LE.-1.0D-12)THEN
833 DO I = 1,NUMENV
834 I1 = I
835 I2 = NUMENV - I + 1
836 IF(I1.GE.I2)GOTO 310
837 DUM1 = ENVD(I1)
838 ENVD(I1) = ENVD(I2)
839 ENVD(I2) = DUM1
840 DUM1 = ENVP(I1)
841 ENVP(I1) = ENVP(I2)
842 ENVP(I2) = DUM1
843 DUM1 = ENVR(I1)
844 ENVR(I1) = ENVR(I2)
845 ENVR(I2) = DUM1
846 DUM1 = ENVS(I1)
847 ENVS(I1) = ENVS(I2)
848 ENVS(I2) = DUM1
849 DUM1 = ENVT(I1)
850 ENVT(I1) = ENVT(I2)
851 ENVT(I2) = DUM1
852 DUM1 = ENVX(I1)
853 ENVX(I1) = ENVX(I2)
854 ENVX(I2) = DUM1
855 DUM1 = ENVZ(I1)
856 ENVZ(I1) = ENVZ(I2)
857 ENVZ(I2) = DUM1
858 LDUM = LCENV(I1)
859 LCENV(I1) = LCENV(I2)
860 LCENV(I2) = LDUM
861C 08/25 JVS
862 DUM1 = ENVO(I1)
863 ENVO(I1) = ENVO(I2)
864 ENVO(I2) = DUM1
865C 10/30/26 MHP via Claude
866C code changed to complete the ENVL swap; ENVL(I2) was never set, so
867C the reversal copied the outer half over the inner half. No output
868C changes while every envelope point holds the same luminosity B.
869 DUM1 = ENVL(I1)
870 ENVL(I1) = ENVL(I2)
871 ENVL(I2) = DUM1
872 DUM1 = EQDT(I1)
873 EQDT(I1) = EQDT(I2)
874 EQDT(I2) = DUM1
875
876C 08/13 JVS ADDED DEL VECTORS
877 DUM1 = EDELS(1,I1)
878 EDELS(1,I1) = EDELS(1,I2)
879 EDELS(1,I2) = DUM1
880 DUM1 = EDELS(2,I1)
881 EDELS(2,I1) = EDELS(2,I2)
882 EDELS(2,I2) = DUM1
883 DUM1 = EDELS(3,I1)
884 EDELS(3,I1) = EDELS(3,I2)
885 EDELS(3,I2) = DUM1
886 DUM1 = EVELS(I1)
887 EVELS(I1) = EVELS(I2)
888 EVELS(I2) = DUM1
889 DUM1 = EBETAS(I1)
890 EBETAS(I1) = EBETAS(I2)
891 EBETAS(I2) = DUM1
892C 08/25 JVS
893 DUM1 = EGAM1(I1)
894 EGAM1(I1) = EGAM1(I2)
895 EGAM1(I2) = DUM1
896 DUM1 = EQCP(I1)
897 EQCP(I1) = EQCP(I2)
898 EQCP(I2) = DUM1
899 DUM1 = EFXIONS(1,I1)
900 EFXIONS(1,I1) = EFXIONS(1,I2)
901 EFXIONS(1,I2) = DUM1
902 DUM1 = EFXIONS(2,I1)
903 EFXIONS(2,I1) = EFXIONS(2,I2)
904 EFXIONS(2,I2) = DUM1
905 DUM1 = EFXIONS(3,I1)
906 EFXIONS(3,I1) = EFXIONS(3,I2)
907 EFXIONS(3,I2) = DUM1
908
909 END DO
910 310 310 CONTINUE
911C JVS 07/12 Save the last envelope point pressure
912C PPHOT = ENVP(NUMENV) ! G Somers 3/17, MOVED PPHOT DEF HIGHER UP
913C END JVS
914 ENDIF
915C DBG PULSE WRITE END OF DATA INDICATOR
916 IF (LPULPT.AND.LPRT) THEN
917C XYZ = 99.99D0
918 IF(IPVER.EQ.1) THEN
919 WRITE(IOPENV, 5001) (XYZ(I),I=1,19)
920 ELSE IF (IPVER.EQ.2 .OR. IPVER.EQ.3)THEN
921 WRITE(IOPENV, 6003) (XYZ(I),I=1,22)
922 END IF
923 ENDIF
924
925 WRITE(ISHORT,215)NOK,NBAD,SDIF,Y(1),(ERRSUM(J),J=1,3)
926 215 215 FORMAT(1X,'ENVELOPE INTEGRATION COMPLETE',1X,
927 * * 'NUMBER OF STEPS ACCEPTED',I5,' REJECTED',
928 * * I5/5X,'SENV-LAST M=',1PE22.13,' LAST M=',E22.13/
929 * * 5X,'MAX RELATIVE ERRORS:M ',1PE14.5,' T ',E14.5,
930 * * ' R ',E14.5)
931
932C JVS 08/13
933C IF THE CZ IS IN THE ENVELOPE (.I.E. BEYOND THE FITTING POINT) TRACK ITS
934C LOCATION IN MASS AND RADIUS FOR USE WITH AM LOSS ROUTINES
935C
936C G Somers 3/17, SKIP TAUCAL CALL IF USING NEW TAUCZ ROUTINES
937 IF(LNEWTCZ)GOTO 555
938C G Somers END
939 JENV = 0.0
940 DO I=1,NUMENV
941 IF (JENV .EQ. 0.0 .AND. LCENV(I) ) JENV = I
942 END DO
943
944 IF (JENV .GT. 1) THEN
945 ! Calculate the the location of the base sof the surface CZ in radius
946 DD2 = EDELS(1,JENV-1)-EDELS(2,JENV-1)
947 DD1 = EDELS(1,JENV)-EDELS(2,JENV)
948 FX = DD2/(DD2-DD1)
949
950 ENVCZL = ENVR(JENV-1)+FX*(ENVR(JENV)-ENVR(JENV-1))-CRSUNL
951C 10/13/26 MHP via Claude
952C the ENVRCZ assignment is removed with COMMON/ENVCZ/; nothing read it.
953C ENVCZL above is still used below.
954
955 DO I=1,NUMENV
956 ENVS1(I) = DEXP(CLN*(ENVS(I)+HSTOT))
957
958 IF (I.EQ.NUMENV) THEN
959 ENVS2(I) = 0.0
960 ELSE
961 ENVS2(I)=DEXP(CLN*ENVS(I+1))-DEXP(CLN*ENVS(I))
962 ENDIF
963
964 EDEL1(I) = EDELS(1,I)
965 EDEL2(I) = EDELS(2,I)
966 END DO
967
968C CALL TAUCAL(ENVX,ENVS2,ENVS1,LCENV,ENVR,ENVP,ENVD,ENVG,NUMENV, ! KC 2025-05-31
969 CALL TAUCAL(ENVS2,ENVS1,LCENV,ENVR,ENVP,ENVD,ENVG,NUMENV,
970 * * EVELS, EDEL1,EDEL2)
971 ENDIF
972
973555 555 CONTINUE
974 RETURN
975 END
1"""Python version of envint.f (YREC development, 7be4133): the atmosphere
2and envelope integrations, from the surface down to the fitting point."""
3
4import math
5
6import numpy as np
7
8from pyrec import acdpth_mod
9from pyrec import atmos_mod
10from pyrec import atmprt_mod
11from pyrec import atmstruct_mod
12from pyrec import comp_mod
13from pyrec import constants_mod
14from pyrec import dimensions_mod
15from pyrec import envprt_mod
16from pyrec import envstruct_mod
17from pyrec import intpar_mod
18from pyrec import iounits_mod
19from pyrec import nmlpar_mod
20from pyrec import ovrtrn_mod
21from pyrec import pualpha_mod
22from pyrec import pulse1_mod
23from pyrec import pulse2_mod
24from pyrec import qenvmix_mod
25from pyrec.alsurfp import alsurfp
26from pyrec.bsstep import bsstep
27from pyrec.eqstat import eqstat
28from pyrec.getopac import getopac
29from pyrec.hra import hra
30from pyrec.kcsurfp import kcsurfp
31from pyrec.qatm import qatm
32from pyrec.qenv import qenv
33from pyrec.surfp import surfp
34from pyrec.taucal import taucal
35
36# Fortran line 98.
37MAXSTP = 2000 # the maximum number of integration steps (DBG)
38TINY = 1.0e-30
39JSON = dimensions_mod.json
40
41# End-of-data marker for the pulsation files (DATA XYZ/22*99.99D0/)
42# (Fortran line 195).
43XYZ = np.full(23, 99.99)
44
45# Locals kept between calls (SAVE) whose old values can be read: the
46# previous point's taup, op, dp (suspected bug 3 on the page), the step
47# counts, last mass difference and errors printed at the end (suspected
48# bug 4), and ENVG, which is never set (suspected bug 1) (Fortran line
49# 202).
50taup = 0.0
51op = 0.0
52dp = 0.0
53nok = 0
54nbad = 0
55sdif = 0.0
56y = np.zeros(4)
57errsum = np.zeros(4)
58envg = np.zeros(JSON + 1)
59envs1 = np.zeros(JSON + 1)
60envs2 = np.zeros(JSON + 1)
61edel1 = np.zeros(JSON + 1)
62edel2 = np.zeros(JSON + 1)
63
64
65def _pulse_lines(unit, values, per_line):
66 """Write values to a pulsation file, per_line to a line (FORMATs 5001:
67 5E16.9 and 6001: 5E23.16, as 5, 5, 5, 4; 6003: 6E23.16, as 6, 6, 6,
68 4). Python's E format prints d.ddd where Fortran's prints 0.ddd."""
69
70 width, digits = (16, 9) if per_line == 0 else (23, 16)
71 per_line = per_line or 5
72 lines = []
73 for k in range(0, len(values), per_line):
74 lines.append("".join(f"{v:{width}.{digits}E}"
75 for v in values[k:k + per_line]))
76 iounits_mod.write(unit, "\n".join(lines))
77
78
79def _fmt260(eg, de):
80 """FORMAT 260: one envelope point of the .store table."""
81
82 e = envprt_mod
83 q = pulse2_mod
84 chrh = 1.0 / q.qqdp
85 cht = -chrh * q.qqdt
86 cv = q.qqcp - math.exp(constants_mod.cln * (e.ep - e.ed - e.et)) \
87 * cht**2 / chrh
88 gam1 = chrh * q.qqcp / cv
89 return (f" {eg:10.3E}{e.ep:10.7f}{e.et:10.7f}{de:14.7E}{e.es:14.7E}"
90 f"{e.ed:10.7f}{e.eo:10.2E}{e.ebeta:7.3f}{e.edel[1]:9.2E}"
91 f"{e.edel[2]:6.3f}{e.edel[3]:6.3f}{e.efxion[1]:6.3f}"
92 f"{e.efxion[2]:6.3f}{e.efxion[3]:6.3f}{e.evel:9.2E}"
93 f"{gam1:12.8f}{q.qqdp:12.8f}")
94
95
96def envint(b: float, fpl: float, ftl: float, gl: float, hstot: float, ie: int, lprt: bool, lsbc: bool, plim: float,
97 rl: float, teffl: float, x: float, z: float, estore: np.ndarray, istore: int, katm: int, kenv: int,
98 ksaha: int, ps: np.ndarray, rs: np.ndarray, ts: np.ndarray, lpulpt: bool, cmixl: float) -> tuple[float, int,
99 int, int, int]:
100 """Integrate the atmosphere (gray T-tau, or read from a table) and the
101 envelope (in log P) down to the fitting mass; return the fitting
102 point's P, T, r, store the envelope profile in envstruct_mod, and find
103 the base of the convection zone and its overturn time.
104
105 Translates: envint.f#ENVINT (7be4133)
106
107 Arguments: b (L), fpl, ftl (rotational corrections), gl (log g),
108 hstot (log M), ie (the point of the envelope triangle), lprt, lsbc
109 (compute the boundary condition), plim (the expected end pressure), rl
110 (log R), teffl, x, z, lpulpt (pulsation output), cmixl (the mixing
111 length, relayed to qenv through qenvmix_mod); istore and the counters
112 katm, kenv, ksaha. Arrays changed in place: ps, rs, ts (log P, log T,
113 log r at the fitting point, entry ie, set only with lsbc), estore (the
114 state where T first reaches TENV).
115
116 Returns (rl, istore, katm, kenv, ksaha): rl as QENV last set it
117 (log r at the last envelope point; as passed in if QENV was not
118 called).
119
120 """
121
122 global taup, op, dp, nok, nbad, sdif
123 c = constants_mod
124 nml = nmlpar_mod
125 io = iounits_mod
126 q = pulse2_mod
127 apr = atmprt_mod
128 epr = envprt_mod
129 env = envstruct_mod
130 atm = atmstruct_mod
131 acd = acdpth_mod
132 # Fortran lines 175–176.
133 dydx = np.zeros(4)
134 yscal = np.zeros(4)
135 ystart = np.zeros(4)
136 err = np.zeros(4)
137 fxion = np.zeros(4)
138
139 # QENV is called through BSSTEP with a fixed argument list, so CMIXL
140 # reaches it through qenvmix_mod (Fortran line 204).
141 qenvmix_mod.cmixlq = cmixl
142
143 # Pulsation output: turn on the derivative calculator (DBG) (Fortran
144 # lines 205–218).
145 if lpulpt and lprt:
146 pulse1_mod.lpumod = True
147 lderiv = True
148 latmo = False
149 else:
150 pulse1_mod.lpumod = False
151 lderiv = False
152 latmo = True
153 locond = False
154 # Derivatives are always calculated (the profiles need them) (JVS 10/07/13).
155 lderiv = True
156
157 # Fortran lines 220–225.
158 if lprt and nml.lstatm:
159 if not nml.lstch:
160 io.write(io.istor, "\n******** ATMOSPHERE BEGIN ********") # 60
161
162 # Tabulated atmospheres give the photospheric pressure directly
163 # (Fortran lines 227–257).
164 tabulated = False
165 if nml.kttau == 3:
166 # Kurucz atmospheres.
167 if nml.lstch:
168 nml.lstatm = False
169 surfp(teffl, gl, lprt and nml.lstatm)
170 tabulated = True
171 elif nml.kttau == 5:
172 # Kurucz (Castelli-Kurucz 2004) atmospheres.
173 if nml.lstch:
174 nml.lstatm = False
175 kcsurfp(teffl, gl, lprt and nml.lstatm)
176 tabulated = True
177 elif nml.kttau == 4:
178 # Allard & Hauschildt atmospheres.
179 if nml.lstch:
180 nml.lstatm = False
181 lalfail = alsurfp(teffl, gl, lprt and nml.lstatm, False)
182 if lalfail:
183 # Gray atmospheres until SURFBC switches back. Question (see the
184 # page): a failure in log g.
185 nml.kttau = 0
186 nml.lttau = True
187 print(" ENVINT: Change to gray atmosphere (KTTAU=0)")
188 io.write(io.ishort, " ENVINT: Change to gray atmosphere (KTTAU=0)")
189 else:
190 tabulated = True
191
192 if not tabulated:
193 # label 2: gray atmosphere. Guess T at an optical depth near zero
194 # (Fortran lines 258–268).
195 errsum[1] = 0.0
196 if nml.kttau == 0:
197 tl = teffl - 0.031235 + 0.25 * math.log10(c.cc23)
198 elif nml.kttau == 1:
199 tl = teffl - 0.031235 + 0.25 * math.log10(0.550)
200 elif nml.kttau == 2:
201 tl = teffl + hra(c.cc23) - atmos_mod.hras
202
203 # Very occasionally the start (x0) lies past the end (xlim): the
204 # starting density is then divided by 10 and the start retried
205 # (Fortran lines 269–372). Suspected bug (see the page): no limit on
206 # retries.
207 atmdx = nml.atmd0
208 while True: # label 1998
209 t = math.exp(c.cln * tl)
210 # P at this T and the chosen starting density.
211 pl = math.log10((c.cgas * atmdx + c.ca3 * t**3) * t)
212 # The optical depth (x0 = log tau) where the integration begins.
213 (t, p, dl, d, beta, betai, beta14, rmu, amu, emu, eta,
214 qdt, qdp, qcp, dela, qdtt, qdtp, qat, qap, qcpt, qcpp,
215 ksaha) = eqstat(tl, t, pl, 0.0, 0.0, 0.0, x, z, 0.0, 0.0, 0.0,
216 fxion, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
217 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, lderiv, latmo,
218 ksaha)
219 o, ol, qod, qot = getopac(dl, tl, x, z, 0.0, 0.0, 0.0, 0.0, fxion)
220 x0 = pl - gl + math.log10(o)
221 y[1] = pl
222 # The slope includes fpl, as qatm's do (MHP 10/29/26).
223 dydx[1] = math.exp(c.cln * (gl + x0 - ol - pl)) * fpl
224 if lprt and nml.lstatm:
225 if not nml.lstch:
226 io.write(io.istor,
227 " TAU P T D"
228 " O HII HEII HEIII SAHA"
229 " KATM") # format 10
230 beta = 1.0 - c.ca3 * (t**2)**2 / p
231 chrh = 1.0 / qdp
232 cht = -chrh * qdt
233 # Suspected bug (see the page): qcpp where qcp is meant.
234 cv = qcpp - math.exp(c.cln * (pl - dl - tl)) * cht**2 / chrh
235 gam1 = chrh * qcpp / cv
236 if not nml.lstch:
237 io.write(io.istor,
238 f" {x0:11.7f}{pl:11.7f}{tl:11.7f}{dl:11.7f}"
239 f"{o:14.7E}{fxion[1]:6.3f}{fxion[2]:6.3f}"
240 f"{fxion[3]:6.3f}{ksaha:7d}{katm:7d}"
241 f"{gam1:12.8f}{qdp:12.8f}{qdt:12.8f}"
242 f"{beta:12.8f}{qcpp:12.5E}{cv:12.5E}") # 20
243 # Pulsation output: the initial point (DBG) (Fortran lines
244 # 314–354).
245 if lpulpt and lprt:
246 q.qqed = 0.0
247 qesum = 0.0
248 q.qqet = 0.0
249 q.qfs = 1.0
250 taup = math.exp(c.cln * x0)
251 op = o
252 dp = math.exp(c.cln * dl)
253 deltr = 0.0
254 pelpf = c.cgas * t * d * emu
255 # From the first lines of TPGRAD.
256 delr = o * b * math.exp(c.cln * (pl - hstot - 4.0 * tl
257 + c.clsunl - c.cgl
258 + c.cdelrl)) * ftl / fpl
259 del_ = delr if delr - dela <= 1.0e-6 else dela
260 if nml.ipver == 1:
261 # QQED, not QED, the atmosphere's energy generation (MHP 10/02).
262 _pulse_lines(io.iopatm,
263 [deltr, q.qfs, b, tl, dl, pl, qesum, o, qdp,
264 q.qqed, q.qqet, qod, qot, del_, dela, qcp,
265 rmu, qdt, pelpf], 0) # format 5001
266 elif nml.ipver in (2, 3):
267 _pulse_lines(io.iopatm,
268 [deltr, q.qfs, b, tl, dl, pl, taup, o, qdp,
269 q.qqed, q.qqet, qod, qot, del_, dela, qcp,
270 rmu, qdt, pelpf], 5) # format 6001
271 # Integrate dP/dtau from this tau to tau = 2/3 (Fortran lines
272 # 355–372).
273 nv = 1
274 eps = nml.atmerr
275 if nml.kttau == 1:
276 # Krishna Swamy: tau = 0.312156330 at Teff.
277 xlim = -0.505627854
278 else:
279 # tau = 2/3 at Teff.
280 xlim = -0.176091259
281 if x0 > xlim:
282 io.write(io.ishort, f" ENVINT: X0>XLIM, X0,XLIM: {x0} {xlim}")
283 io.write(io.ishort,
284 " ENVINT: get new X0 by dividing ATMD0 by 10")
285 atmdx = atmdx / 10.0
286 continue # go to 1998
287 break
288
289 # Fortran lines 375–380.
290 hmax = nml.atmmax
291 hmin = nml.atmmin
292 h = nml.atmbeg
293 stoler = intpar_mod.stolr0
294 nok = 0
295 nbad = 0
296
297 # The integration loop (Fortran lines 382–497).
298 for nstep in range(1, MAXSTP + 1):
299 for i in range(1, nv + 1):
300 yscal[i] = abs(y[i]) + abs(h * dydx[i]) + TINY
301 # Don't pass the end, nor exceed the maximum step.
302 if (x0 - xlim) * (x0 + h - xlim) < 0.0:
303 h = xlim - x0
304 if h > hmax:
305 h = hmax
306 # h is the attempted step, hdid the one taken, hnext the next.
307 x0, hdid, hnext, rl, katm, ksaha = bsstep(
308 y, dydx, nv, x0, h, eps, yscal, 0.0, 0.0, qatm, b, fpl, ftl, gl,
309 latmo, lderiv, locond, lprt, rl, teffl, x, z, katm, ksaha, err)
310 errsum[1] = errsum[1] + err[1]
311 # dP/dtau at the start of the next step.
312 katm, ksaha = qatm(x0, y, dydx, b, fpl, ftl, gl, latmo, lderiv,
313 locond, lprt, rl, teffl, x, z, katm, ksaha)
314 if lprt and nml.lstatm:
315 beta = 1.0 - c.ca3 * math.exp(c.cln * (4.0 * apr.at - apr.ap))
316 chrh = 1.0 / q.qqdp
317 cht = -chrh * q.qqdt
318 cv = q.qqcp - math.exp(c.cln * (apr.ap - apr.ad - apr.at)) \
319 * cht**2 / chrh
320 gam1 = chrh * q.qqcp / cv
321 if not nml.lstch:
322 io.write(io.istor,
323 f" {apr.taul:11.7f}{apr.ap:11.7f}{apr.at:11.7f}"
324 f"{apr.ad:11.7f}{apr.ao:14.7E}"
325 f"{apr.afxion[1]:6.3f}{apr.afxion[2]:6.3f}"
326 f"{apr.afxion[3]:6.3f}{ksaha:7d}{katm:7d}"
327 f"{gam1:12.8f}{q.qqdp:12.8f}{q.qqdt:12.8f}"
328 f"{beta:12.8f}{q.qqcp:12.5E}{cv:12.5E}") # 20
329 # Save the structure in atmstruct_mod, for the stitched output
330 # (JvS).
331 atm.atmop[nstep] = apr.ap
332 atm.atmot[nstep] = apr.at
333 atm.atmod[nstep] = apr.ad
334 atm.abetas[nstep] = beta
335 atm.agam1[nstep] = gam1
336 atm.aqdt[nstep] = q.qqdt
337 atm.afxions[1, nstep] = apr.afxion[1]
338 atm.afxions[2, nstep] = apr.afxion[2]
339 atm.afxions[3, nstep] = apr.afxion[3]
340 atm.atmoo[nstep] = apr.ao
341 atm.atmocp[nstep] = q.qqcp
342 if hdid == h:
343 nok = nok + 1
344 else:
345 nbad = nbad + 1
346 # Pulsation output: atmosphere values (also for the acoustic depth,
347 # LCLCD, JVS 02/11) (DBG) (Fortran lines 431–476).
348 # Suspected bug (see the page): taup, op, dp are set only with
349 # pulsation output; otherwise they are left from an earlier call.
350 if (lpulpt and lprt) or acd.lclcd or nml.lstch:
351 q.qqed = 0.0
352 qesum = 0.0
353 q.qqet = 0.0
354 q.qfs = 1.0
355 on = q.qo
356 dn = math.exp(c.cln * q.qdl)
357 taun = math.exp(c.cln * apr.taul)
358 # See J.P. Cox, Princ. of Stell. Struc., p. 590.
359 deltr = (taun - taup) / (((dn * on) + (dp * op)) / 2)
360 pelpf = c.cgas * q.qt * q.qd * q.qemu
361 op = on
362 # From the first lines of TPGRAD.
363 delr = q.qo * b * math.exp(c.cln * (q.qpl - hstot
364 - 4.0 * q.qtl + c.clsunl
365 - c.cgl + c.cdelrl)) \
366 * ftl / fpl
367 del_ = delr if delr - q.qdela <= 1.0e-6 else q.qdela
368 if lpulpt and lprt:
369 if nml.ipver == 1:
370 _pulse_lines(io.iopatm,
371 [deltr, q.qfs, b, q.qtl, q.qdl, q.qpl,
372 qesum, q.qo, q.qqdp, q.qqed, q.qqet,
373 q.qqod, q.qqot, del_, q.qdela, q.qqcp,
374 q.qrmu, q.qqdt, pelpf], 0)
375 elif nml.ipver in (2, 3):
376 _pulse_lines(io.iopatm,
377 [deltr, q.qfs, b, q.qtl, q.qdl, q.qpl,
378 taun, q.qo, q.qqdp, q.qqed, q.qqet,
379 q.qqod, q.qqot, del_, q.qdela, q.qqcp,
380 q.qrmu, q.qqdt, pelpf], 5)
381 # Save in atmstruct_mod, for the stitched output (JvS).
382 atm.atmor[nstep] = deltr
383 atm.adels[1, nstep] = delr
384 atm.adels[2, nstep] = del_
385 atm.adels[3, nstep] = q.qdela
386 atm.numatm = nstep
387 taup = taun
388 dp = dn
389 # Is the integration complete? (Fortran lines 479–488.)
390 if abs(x0 - xlim) <= stoler:
391 io.write(io.ishort,
392 f" ATMOSPHERE INTEGRATION COMPLETE NUMBER OF STEPS "
393 f"ACCEPTED{nok:5d} REJECTED{nbad:5d}\n"
394 f" MAXIMUM RELATIVE ERROR IN P "
395 f"{errsum[1]:22.13E}") # format 35
396 break # go to 200
397 if hnext < hmin:
398 hnext = hmin
399 h = hnext
400 else:
401 msg = (" ATMOSPHERE INTEGRATION FAILED AFTER MAXSTP "
402 "INTEGRATIONS.I QUIT.") # format 50
403 io.write(io.iowr, msg)
404 io.write(io.ishort, msg)
405 raise SystemExit(1) # STOP 1, the run-time error code
406
407 # label 200 (tables come here directly). If only the photospheric pressure
408 # is wanted, stop here (G Somers 3/17) (Fortran lines 504–508).
409 ovrtrn_mod.pphot = apr.ap
410 if not nml.lcalcenv:
411 return rl, istore, katm, kenv, ksaha # go to 555
412
413 # The envelope: P is the independent variable, m, T and r the
414 # dependent ones; integrate from tau = 2/3 to the model's last point
415 # (Fortran lines 509–514).
416 if lprt and nml.lstenv:
417 if not nml.lstch:
418 io.write(io.istor, "\n******** ENVELOPE BEGIN ********") # 61
419
420 # Pulsation output: end-of-data marker for the atmosphere file (DBG)
421 # (Fortran lines 515–525).
422 if lpulpt and lprt:
423 if nml.ipver == 1:
424 _pulse_lines(io.iopatm, list(XYZ[1:20]), 0)
425 elif nml.ipver in (2, 3):
426 _pulse_lines(io.iopatm, list(XYZ[1:20]), 5)
427
428 # The envelope mass is negligible: the photosphere is the fitting point
429 # (Fortran lines 527–540).
430 comp = comp_mod
431 if comp.senv > -1.0e-12:
432 if lsbc:
433 ps[ie] = apr.ap
434 rs[ie] = rl
435 ts[ie] = apr.at
436 if lprt:
437 if not nml.lstch:
438 io.write(io.istor,
439 f" {ps[ie]:16.12f}{ts[ie]:16.12f}"
440 f"{rs[ie]:16.12f} {comp.senv:16.12f}")
441 else:
442 # Initialize; set the numerical parameters (Fortran lines 542–567).
443 latmo = False
444 for i in range(1, 4):
445 errsum[i] = 0.0
446 nok = 0
447 nbad = 0
448 x0 = apr.ap
449 sl = 0.0
450 y[1] = sl
451 y[2] = apr.at
452 y[3] = rl
453 nv = 3
454 eps = nml.enverr
455 hmax = nml.envmax
456 hmin = nml.envmin
457 h = nml.envbeg
458 stoler = abs(intpar_mod.stolr0 * comp.senv)
459 rsurf = math.exp(c.cln * rl)
460 if istore == ie:
461 istore = 0
462 llstore = False
463 ltest = not lsbc
464
465 # dy/dx at the start of the step (Fortran lines 568–570).
466 rl, kenv, ksaha = qenv(x0, y, dydx, b, fpl, ftl, gl, latmo,
467 lderiv, locond, lprt, rl, teffl, x, z, kenv,
468 ksaha)
469
470 # Pulsation output: the first point of the envelope (DBG), and the
471 # .store table's header and first line (Fortran lines 571–623).
472 pal = pualpha_mod
473 if lpulpt and lprt:
474 _envelope_pulse_point(rl, b, pal)
475 if lprt and nml.lstenv:
476 if not nml.lstch:
477 io.write(io.istor,
478 " " + "".join(f"{s:>10}" for s in
479 ("GRAV ", "P ", "T "))
480 + "".join(f"{s:>14}" for s in ("DEPTH ", "M "))
481 + "".join(f"{s:>10}" for s in ("D ", "O "))
482 + f"{'BETA':>7}{'DELR ':>9}"
483 + "".join(f"{s:>6}" for s in
484 ("DELA", "DEL ", "HII ", "HEII", "HEIII"))
485 + f"{'V ':>9}{'GAM1 ':>12}{'QQDP ':>12}") # 240
486 srad = math.exp(c.cln * rl)
487 de = (rsurf - srad) / rsurf
488 sgl = c.cgl + hstot - 2.0 * rl
489 eg = math.exp(c.cln * sgl)
490 if not nml.lstch:
491 io.write(io.istor, _fmt260(eg, de))
492
493 # Store the starting values (one stored envelope point) (07/02)
494 # (Fortran lines 624–660). TAUCZENV is summed but never kept
495 # (unused?, as in the Fortran).
496 lsurcz = False
497 tauczenv = 0.0
498 _store_envelope_point(1, x, z, b)
499 # Gradients kept for the convection zone's edge (unused here) (JVS 10/10).
500 chdelj = epr.edel[2] # noqa: F841 (unused in the Fortran too)
501 chdeld = q.qdela # noqa: F841 (unused in the Fortran too)
502 if env.lcenv[1]:
503 lsurcz = True
504 env.numenv = 1
505
506 # The integration loop (Fortran lines 661–826).
507 for nstep in range(1, MAXSTP + 1):
508 xstart = x0
509 for i in range(1, nv + 1):
510 ystart[i] = y[i]
511 yscal[i] = abs(y[i]) + abs(h * dydx[i]) + TINY
512 sest = y[1] + h * dydx[1]
513 if comp.senv - y[1] > 0.0 or comp.senv - sest > 0.0:
514 # The integration has overshot the fitting point, or the
515 # next step would: shorten the step, or integrate back.
516 h = (comp.senv - y[1]) / dydx[1]
517 if h < 0.0:
518 if h < -hmax:
519 h = -hmax
520 else:
521 if h > hmax:
522 h = hmax
523 # plim estimates the end pressure from the model's last point:
524 # the first time the integrator would pass it, limit the step.
525 if ltest:
526 if x0 + h > plim:
527 h = plim - x0
528 ltest = False
529 x0, hdid, hnext, rl, kenv, ksaha = bsstep(
530 y, dydx, nv, x0, h, eps, yscal, 0.0, 0.0, qenv, b, fpl, ftl, gl,
531 latmo, lderiv, locond, lprt, rl, teffl, x, z, kenv, ksaha, err)
532 for k in range(1, 4):
533 errsum[k] = errsum[k] + err[k]
534 # dy/dx at the start of the next step.
535 rl, kenv, ksaha = qenv(x0, y, dydx, b, fpl, ftl, gl, latmo,
536 lderiv, locond, lprt, rl, teffl, x, z,
537 kenv, ksaha)
538 if lpulpt and lprt:
539 _envelope_pulse_point(rl, b, pal)
540 if lprt and nml.lstenv:
541 srad = math.exp(c.cln * rl)
542 de = (rsurf - srad) / rsurf
543 sgl = c.cgl + hstot - 2.0 * rl
544 eg = math.exp(c.cln * sgl)
545 if not nml.lstch:
546 io.write(io.istor, _fmt260(eg, de))
547 if hdid == h:
548 nok = nok + 1
549 else:
550 nbad = nbad + 1
551 # Is the integration complete? Store the point if it has not
552 # overshot the fitting point (07/02).
553 sdif = comp.senv - y[1]
554 if sdif <= stoler:
555 env.numenv = env.numenv + 1
556 n = env.numenv
557 _store_envelope_point(n, x, z, b)
558 if not lsurcz:
559 if env.lcenv[n]:
560 lsurcz = True
561 elif epr.evel > 0.0:
562 dr = 10.0**env.envr[n - 1] - 10.0**env.envr[n]
563 # tauczenv is never kept (unused?).
564 tauczenv = tauczenv + dr / epr.evel
565 if abs(sdif) <= stoler:
566 if lsbc:
567 # Interpolate linearly to the fitting mass.
568 wt0 = sdif / (ystart[1] - y[1])
569 ps[ie] = x0 + wt0 * (xstart - x0)
570 rs[ie] = y[3] + wt0 * (ystart[3] - y[3])
571 ts[ie] = y[2] + wt0 * (ystart[2] - y[2])
572 if lprt:
573 io.write(io.ishort,
574 f" {ps[ie]:16.12f}{ts[ie]:16.12f}"
575 f"{rs[ie]:16.12f} {comp.senv:16.12f}")
576 break # go to 300
577 elif not llstore:
578 # TENV was COMMON/CTLIM/, set by parmin.f; derived here.
579 tenv = 0.5 * (nml.tenv0 + nml.tenv1)
580 if y[2] >= tenv and lsbc:
581 llstore = True
582 istore = ie
583 estore[1] = x0
584 estore[2] = y[2]
585 estore[3] = y[3]
586 estore[4] = y[1]
587 if hnext < hmin:
588 hnext = hmin
589 h = hnext
590 else:
591 msg = (" ENVELOPE INTEGRATION FAILED AFTER MAXSTP TRIES. "
592 "I QUIT") # format 911
593 io.write(io.iowr, msg)
594 io.write(io.ishort, msg)
595 raise SystemExit(1) # STOP 1, the run-time error code
596
597 # label 300: reverse the envelope profile, centre outward (07/02)
598 # (Fortran lines 827–914).
599 if comp.senv <= -1.0e-12:
600 n = env.numenv
601 for i in range(1, n + 1):
602 i1 = i
603 i2 = n - i + 1
604 if i1 >= i2:
605 break # go to 310
606 for arr in (env.envd, env.envp, env.envr, env.envs, env.envt,
607 env.envx, env.envz, env.lcenv, env.envo):
608 arr[i1], arr[i2] = arr[i2], arr[i1]
609 dum1 = env.envl[i1]
610 env.envl[i1] = env.envl[i2]
611 env.envl[i2] = dum1
612 for arr in (env.eqdt, env.evels, env.ebetas, env.egam1,
613 env.eqcp):
614 arr[i1], arr[i2] = arr[i2], arr[i1]
615 for k in range(1, 4):
616 env.edels[k, i1], env.edels[k, i2] = \
617 env.edels[k, i2], env.edels[k, i1]
618 env.efxions[k, i1], env.efxions[k, i2] = \
619 env.efxions[k, i2], env.efxions[k, i1]
620
621 # Pulsation output: end-of-data marker for the envelope file (DBG)
622 # (Fortran lines 915–923).
623 if lpulpt and lprt:
624 if nml.ipver == 1:
625 _pulse_lines(io.iopenv, list(XYZ[1:20]), 0)
626 elif nml.ipver in (2, 3):
627 _pulse_lines(io.iopenv, list(XYZ[1:23]), 6)
628
629 # Suspected bug (see the page): when the envelope is skipped,
630 # nok, nbad, sdif and y are from the atmosphere or an earlier call
631 # (Fortran lines 925–930).
632 io.write(io.ishort,
633 f" ENVELOPE INTEGRATION COMPLETE NUMBER OF STEPS ACCEPTED"
634 f"{nok:5d} REJECTED{nbad:5d}\n"
635 f" SENV-LAST M={sdif:22.13E} LAST M={y[1]:22.13E}\n"
636 f" MAX RELATIVE ERRORS:M {errsum[1]:14.5E} T "
637 f"{errsum[2]:14.5E} R {errsum[3]:14.5E}") # format 215
638
639 # If the convection zone's base lies in the envelope, find it, and the
640 # overturn time (not with LNEWTCZ, which computes it elsewhere) (JVS 08/13)
641 # (G Somers 3/17) (Fortran lines 932–973).
642 if nml.lnewtcz:
643 return rl, istore, katm, kenv, ksaha # go to 555
644 jenv = 0
645 for i in range(1, env.numenv + 1):
646 if jenv == 0 and env.lcenv[i]:
647 jenv = i
648 if jenv > 1:
649 # The base of the surface convection zone in radius, by linear
650 # interpolation of del_rad - del.
651 dd2 = env.edels[1, jenv - 1] - env.edels[2, jenv - 1]
652 dd1 = env.edels[1, jenv] - env.edels[2, jenv]
653 fx = dd2 / (dd2 - dd1)
654 # envczl is no longer used (unused?).
655 envczl = (env.envr[jenv - 1] + fx * (env.envr[jenv] # noqa: F841 (unused in the Fortran too)
656 - env.envr[jenv - 1])
657 - c.crsunl)
658 for i in range(1, env.numenv + 1):
659 envs1[i] = math.exp(c.cln * (env.envs[i] + hstot))
660 if i == env.numenv:
661 envs2[i] = 0.0
662 else:
663 envs2[i] = (math.exp(c.cln * env.envs[i + 1])
664 - math.exp(c.cln * env.envs[i]))
665 edel1[i] = env.edels[1, i]
666 edel2[i] = env.edels[2, i]
667 # Suspected bug (see the page): envg is never set.
668 taucal(envs2, envs1, env.lcenv, env.envr, env.envp, env.envd, envg,
669 env.numenv, env.evels, edel1, edel2)
670 return rl, istore, katm, kenv, ksaha # label 555
671
672
673def _store_envelope_point(n, x, z, b):
674 """Copy the point just integrated (envprt_mod, pulse2_mod) into entry n
675 of envstruct_mod: the Fortran writes this block out twice."""
676
677 # Fortran lines 628–655 (and 755–781).
678 env = envstruct_mod
679 e = envprt_mod
680 q = pulse2_mod
681 env.envd[n] = e.ed
682 env.envp[n] = e.ep
683 env.envr[n] = e.er
684 env.envs[n] = e.es
685 env.envt[n] = e.et
686 env.envx[n] = x
687 env.envz[n] = z
688 env.lcenv[n] = e.evel > 0.0
689 for k in range(1, 4):
690 env.edels[k, n] = e.edel[k]
691 env.evels[n] = e.evel
692 env.ebetas[n] = e.ebeta
693 # Always save these, for the profile output (JVS 08/25).
694 chrh = 1.0 / q.qqdp
695 cht = -chrh * q.qqdt
696 cv = q.qqcp - math.exp(constants_mod.cln * (e.ep - e.ed - e.et)) \
697 * cht**2 / chrh
698 env.egam1[n] = chrh * q.qqcp / cv
699 env.eqcp[n] = q.qqcp
700 for k in range(1, 4):
701 env.efxions[k, n] = e.efxion[k]
702 env.envo[n] = e.eo
703 env.envl[n] = b
704 env.eqdt[n] = q.qqdt
705
706
707def _envelope_pulse_point(rl, b, pal):
708 """DBG pulse: one envelope point to the pulsation file (FORMATs 5001,
709 6001, or 6003 with the mixing-length values appended to the first
710 three lines); the Fortran writes this block out twice."""
711
712 # Fortran lines 572–595 (and 703–725).
713 q = pulse2_mod
714 io = iounits_mod
715 q.qqed = 0.0
716 qesum = 0.0
717 q.qqet = 0.0
718 pelpf = constants_mod.cgas * q.qt * q.qd * q.qemu
719 vals = [rl, q.qfs, b, q.qtl, q.qdl, q.qpl, qesum, q.qo, q.qqdp, q.qqed,
720 q.qqet, q.qqod, q.qqot, q.qdel, q.qdela, q.qqcp, q.qrmu, q.qqdt,
721 pelpf]
722 if nmlpar_mod.ipver == 1:
723 _pulse_lines(io.iopenv, vals, 0)
724 elif nmlpar_mod.ipver == 2:
725 _pulse_lines(io.iopenv, vals, 5)
726 elif nmlpar_mod.ipver == 3:
727 _pulse_lines(io.iopenv,
728 vals[0:5] + [pal.alfmlt] + vals[5:10] + [pal.phmlt]
729 + vals[10:15] + [pal.cmxmlt] + vals[15:19], 6)