1C
2C
3C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
4C LIBURN
5C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
6C 11/91 HR ADDED TO CALL.
7C SUBROUTINE LIBURN(DELTS,HCOMP,HD,HR,HS1,HS2,HT,JNENV,JNENV0,M) ! KC 2025-05-31
8 SUBROUTINE LIBURN(DELTS,HCOMP,HR,HS1,HS2,HT,JNENV,JNENV0,M)
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/SCRTCH/ into SCRTCH_MOD.
15 USE SCRTCH_MOD
16C 10/14/26 MHP via Claude
17C code changed to convert COMMON/PREVCZ/ into PREVCZ_MOD.
18 USE PREVCZ_MOD
19C 10/14/26 MHP via Claude
20C code changed to convert COMMON/OLDRAT/ into OLDRAT_MOD.
21 USE OLDRAT_MOD
22C 10/14/26 MHP via Claude
23C code changed to convert COMMON/OLDMOD/ into OLDMOD_MOD.
24 USE OLDMOD_MOD
25C 10/14/26 MHP via Claude
26C code changed to convert COMMON/NEWRAT/ into NEWRAT_MOD.
27 USE NEWRAT_MOD
28C 10/14/26 MHP via Claude
29C code changed to convert COMMON/MDPHY/ into MDPHY_MOD.
30 USE MDPHY_MOD
31C 10/14/26 MHP via Claude
32C code changed to convert COMMON/LIOV/ into LIOV_MOD.
33 USE LIOV_MOD
34C 10/14/26 MHP via Claude
35C code changed to convert COMMON/DEUTER/ into DEUTER_MOD.
36 USE DEUTER_MOD
37C 10/14/26 MHP via Claude
38C code changed to convert COMMON/NMLPAR/ into NMLPAR_MOD.
39 USE NMLPAR_MOD
40C 10/14/26 MHP via Claude
41C code changed to convert COMMON/CONSTANTS/ into CONSTANTS_MOD.
42 USE CONSTANTS_MOD
43C 10/14/26 MHP via Claude
44C code changed to convert COMMON/IOUNITS/ into IOUNITS_MOD.
45 USE IOUNITS_MOD
46C 10/20/26 MHP via Claude
47C code changed to replace implicit typing with IMPLICIT NONE and
48C explicit declarations.
49 IMPLICIT NONE
50 REAL*8 BBE9,BBE90,BLI6,BLI60,BLI7,BLI70,CZBE9,CZLI6,CZLI7,DBE9,
51 * * DDJ,DDJM1,DELTS,DLI6,DLI7,DMASS,DR,DT,FBE9,FBE90,FBE9A,
52 * * FBE9ADD,FLI6,FLI60,FLI6A,FLI6ADD,FLI7,FLI70,FLI7A,FLI7ADD,
53 * * FM,FM0,FRAC,FRAD,FX,HCOMP,HR,HS1,HS2,HS2SAV,HSAV,HSBEG,
54 * * HSEND,HT,R,RATBE9,RATLI6,RATLI7,RBASE,ROS,TBE9,TLI6,TLI7,
55 * * XEST,YERR,YEST,YEXT,YTOL
56 INTEGER I,J,JENV,JENV0,JNENV,JNENV0,K,K2,M,MAXJ,MINJ,NSTEPE
57 LOGICAL*4 LDOACC,LDONE
58
59C 9/25/26 MHP via Claude
60C code changed to consolidate all I/O logical unit numbers, previously
61C scattered across COMMON/LUOUT/, COMMON/LUNUM/, COMMON/ALEX06/,
62C COMMON/ACDPTH/, COMMON/NEWOPAC/, COMMON/LOPAL95/, COMMON/ATMOS2/,
63C COMMON/ALATM03/, COMMON/OPALEOS/, and COMMON/SCV2/, into a single
64C COMMON/IOUNITS/
65C 9/25/26 MHP via Claude
66C code changed to merge COMMON/CONST/, COMMON/CONST1/, COMMON/CONST2/,
67C and COMMON/CONST3/ into a single COMMON/CONSTANTS/
68C MHP 10/91 COMMON BLOCK ADDED FOR OVERSHOOT.
69C 10/12/26 MHP via Claude
70C code changed to extend COMMON/NMLPAR/ to 234 members, adding the
71C namelist variables that the physics routines read from 27 COMMON
72C blocks which also held derived or working members. Those blocks keep
73C only their non-namelist members; five of them -- NEWMX, VMULT,
74C PMMWIND, CCOUT1 and ENVGEN -- had nothing left and are gone.
75C Derivation-only namelist variables from the same blocks went to
76C COMMON/NMLBYPASS/ instead, not here.
77C
78C Declared identically in all 121 files that hold any of these members.
79C Generated from one member list, so every copy is byte-identical; the
80C linker's 'size of symbol changed' warning is the check that they are.
81C 10/13/26 MHP via Claude: removed the unused COMMON/DPMIX/ declaration
82C (no member referenced here; residue of the obsolete-option removals).
83C MHP 11/91 COMMON BLOCK ADDED FOR OVERSHOOT.
84C 11/91 COMMON BLOCK ADDED FOR THE EXACT LOCATION OF THE BASE OF THE
85C SURFACE CZ AND THE INTEGRATED BURNING RATES THROUGHOUT IT. USED TO
86C REMEMBER THE PREVIOUS "END OF TIMESTEP VALUES" FOR THE NEW
87C "BEGINNING OF TIMESTEP VALUES".
88C MHP 05/02 INCLUDE MASS ACCRETION AND ITS EFFECT ON ABUNDANCES
89 DIMENSION HCOMP(15,JSON),HS2(JSON),HT(JSON),HS1(JSON) ! HD(JSON)
90 DIMENSION RATLI6(JSON),RATLI7(JSON),RATBE9(JSON)
91 DIMENSION HSAV(3,JSON),HR(JSON)
92 DIMENSION NSTEPE(11),YTOL(3),YEST(3),YERR(3),YEXT(3)
93 DATA NSTEPE/2,4,6,8,12,16,24,32,48,64,96/
94 DATA YTOL/1.0D-6,1.0D-6,1.0D-6/
95C DATA TLIM/6.0D0/
96 SAVE
97C DETERMINE LITHIUM AND BERYLLIUM BURNING IN A NON-ROTATING MODEL.
98C THE BURNING RATES DEPEND ON THE LOCAL T AND RHO, THE ABUNDANCE, AND
99C IN THE CASE OF CONVECTION ZONES THE MIXING.
100C
101C IN RADIATIVE REGIONS BURNING IS DONE IMPLICITLY, CORRECTING THE RATES FOR THE
102C CHANGE IN ABUNDANCE AS A FUNCTION OF TIME.
103C
104C IN CONVECTIVE REGIONS THE PROGRAM ITERATES BETWEEN BURNING AND MIXING
105C TO CORRECT THE RATES FOR THE CHANGE IN ABUNDANCE AS A FUNCTION OF TIME.
106C
107C SAVE ORIGINAL ABUNDANCES.
108 DO 10 I = 1,M
109 HSAV(1,I) = HCOMP(13,I)
110 HSAV(2,I) = HCOMP(14,I)
111 HSAV(3,I) = HCOMP(15,I)
112 10 10 CONTINUE
113C THE DEGREE OF LITHIUM BURNING IN A SURFACE CZ DEPENDS SENSITIVELY
114C ON THE TEMPERATURE AT ITS BASE - SO ACCURATELY LOCATING IS IMPORTANT.
115C DETERMINE THE TRUE LOCATION (FX) OF THE BASE OF THE CZ AT THE END
116C OF THE TIMESTEP, AND THE LOCATION OF THE EDGE OF OVERSHOOT REGIONS
117C IF APPLICABLE.
118 IF(JNENV.GT.1.AND.JNENV.LT.M)THEN
119 IF(LROT.AND.LINSTB)THEN
120 DDJ = DELAM(JNENV) - DELRM(JNENV)
121 DDJM1 = DELAM(JNENV-1) - DELRM(JNENV-1)
122 ELSE
123C EVALUATE DEL(AD) - DEL(RAD) AT THE LAST CONVECTIVE POINT AND THE ONE
124C BELOW IT.
125 DDJ = SDEL(3,JNENV)-SDEL(1,JNENV)
126 DDJM1 = SDEL(3,JNENV-1)-SDEL(1,JNENV-1)
127 ENDIF
128C USE LINEAR INTERPOLATION TO FIND THE DISTANCE OF THE TRUE LOCATION
129C OF THE BASE FROM THE ZONE MIDPOINT. IF FX IS NEGATIVE,THEN THE TRUE
130C BASE IS HIGHER; IF IT IS POSITIVE, THE TRUE BASE IS LOWER.
131 FX = MAX(-0.5D0,0.5D0-DDJM1/(DDJM1-DDJ))
132 FX = MIN(0.5D0,FX)
133 IF(.NOT.LOVSTE)THEN
134 JENV = JNENV
135 JENV0 = JNENV0
136 ELSE
137C STARTING CZ DEPTH
138 IF(RBASE0.EQ.0.0D0)THEN
139C 10/30/26 MHP via Claude
140C code changed to take the radius below shell 1 as zero (the centre).
141C With a fully convective start-of-step model JNENV0 is 1 and this read
142C HRO(0).
143 IF(JNENV0.GT.1)THEN
144 RBASE0 = 0.5D0*(EXP(CLN*HRO(JNENV0))
145 * * +EXP(CLN*HRO(JNENV0-1)))
146 ELSE
147 RBASE0 = 0.5D0*EXP(CLN*HRO(1))
148 ENDIF
149 ROS = RBASE0 - PSCAL0
150 DO J = JNENV0-1,1,-1
151 R = EXP(CLN*HRO(J))
152 IF(R.LT.ROS)THEN
153 JENV0 = J + 1
154 GOTO 11
155 ENDIF
156 END DO
157 JENV0 = 1
158 11 11 CONTINUE
159 ELSE
160 JENV0 = JENVOS
161 ENDIF
162C ENDING CZ DEPTH : DETERMINE OVERSHOOT FROM TRUE CZ BASE.
163 DR = EXP(CLN*HR(JNENV))-EXP(CLN*HR(JNENV-1))
164 RBASE = 0.5D0*(EXP(CLN*HR(JNENV))+EXP(CLN*HR(JNENV-1)))
165 * * -FX*DR
166 RBASE0 = RBASE
167 ROS = RBASE - PSCAL1
168 DO J = JNENV-1,1,-1
169 R = EXP(CLN*HR(J))
170 IF(R.LT.ROS)THEN
171 JENV = J + 1
172C 10/30/26 MHP via Claude
173C code changed to take the zone width from the end-of-step model alone.
174C The outer edge was the start-of-step HRO, the inner one the current HR.
175 DR = EXP(CLN*HR(J+1))-R
176 FX = 0.5D0-((ROS-R)/DR)
177 FX = MAX(-0.5D0,FX)
178 FX = MIN(0.5D0,FX)
179 GOTO 12
180 ENDIF
181 END DO
182 JENV = 1
183 12 12 CONTINUE
184 ENDIF
185 ELSE
186 JENV = JNENV
187 JENV0 = JNENV0
188 ENDIF
189 JENVOS = JENV
190C RADIATIVE INTERIOR.
191 MINJ = MIN(JENV,JENV0)
192 MAXJ = MAX(JENV,JENV0)
193 DO 50 I = 1,MINJ-1
194 IF(RBE9(I).LE.1.0D-32 .OR. RBE90(I).LE.1.0D-32)GOTO 60
195 IF(HCOMP(13,I).LT.1.0D-24.AND.HCOMP(14,I).LT.1.0D-24
196 * * .AND.HCOMP(15,I).LT.1.0D-24)GOTO 50
197 IF(HT(I).GT.7.0D0)THEN
198 HCOMP(13,I) = 0.0D0
199 HCOMP(14,I) = 0.0D0
200 HCOMP(15,I) = 0.0D0
201 GOTO 50
202 ENDIF
203 DO 40 J = 1,11
204C PERFORM BURNING IN NSTEPE SMALLER TIMESTEPS.
205C DEFINE FRAC - THE FRACTION OF THE TOTAL TIMESTEP IN EACH SMALL ONE -
206C AND DELT - THE FRACTIONAL TIMESTEP IN YEARS.
207 FRAC = 1.0D0/DFLOAT(NSTEPE(J))
208 DT = FRAC*DELTS
209 LDONE = .FALSE.
210C RESTORE INITIAL ABUNDANCES.
211 HCOMP(13,I) = HSAV(1,I)
212 HCOMP(14,I) = HSAV(2,I)
213 HCOMP(15,I) = HSAV(3,I)
214C STORE STARTING REACTION RATES.
215 TLI6 = LOG(RLI60(I))-0.5D0*FRAC*
216 * * (LOG(RLI6(I))-LOG(RLI60(I)))
217 TLI7 = LOG(RLI70(I))-0.5D0*FRAC*
218 * * (LOG(RLI7(I))-LOG(RLI70(I)))
219 TBE9 = LOG(RBE90(I))-0.5D0*FRAC*
220 * * (LOG(RBE9(I))-LOG(RBE90(I)))
221 DO 20 K = 1,NSTEPE(J)
222C INCREMENT THE REACTION RATES.
223 TLI6 = TLI6+FRAC*(LOG(RLI6(I))-LOG(RLI60(I)))
224 TLI7 = TLI7+FRAC*(LOG(RLI7(I))-LOG(RLI70(I)))
225 TBE9 = TBE9+FRAC*(LOG(RBE9(I))-LOG(RBE90(I)))
226 RATLI6(I) = DT*EXP(TLI6)
227 RATLI7(I) = DT*EXP(TLI7)
228 RATBE9(I) = DT*EXP(TBE9)
229C THE REACTION RATES ARE OF THE FORM
230C DLI/DT = F(RHO,T,X)*LI =>DLN LI = DT*F(RHO,T,X)
231C SOLVE FOR D LN (SPECIES)/DT AND ZERO OUT IF THE DEPLETION IS TOO HIGH.
232 IF(RATLI6(I).LT.3.0D1)THEN
233 HCOMP(13,I) = HCOMP(13,I)/EXP(RATLI6(I))
234 ELSE
235 HCOMP(13,I) = 0.0D0
236 ENDIF
237 IF(RATLI7(I).LT.3.0D1)THEN
238 HCOMP(14,I) = HCOMP(14,I)/EXP(RATLI7(I))
239 ELSE
240 HCOMP(14,I) = 0.0D0
241 ENDIF
242 IF(RATBE9(I).LT.3.0D1)THEN
243 HCOMP(15,I) = HCOMP(15,I)/EXP(RATBE9(I))
244 ELSE
245 HCOMP(15,I) = 0.0D0
246 ENDIF
247 20 20 CONTINUE
248C STORE ABUNDANCES AS A FUNCTION OF TIMESTEP IN VECTOR YEST.
249 YEST(1) = HCOMP(13,I)
250 YEST(2) = HCOMP(14,I)
251 YEST(3) = HCOMP(15,I)
252 XEST = FRAC**2
253C USE A RATIONAL POLYNOMIAL EXTRAPOLATOR TO EXTRAPOLATE TO THE SOLUTION FOR
254C ZERO TIMESTEP.
255 CALL RATEXT(J,XEST,YEST,YEXT,YERR,3,7)
256C CHECK IF THE SOLUTION HAS CONVERGED.
257 IF(J.GT.1)THEN
258 LDONE = .TRUE.
259 DO 30 K2=1,3
260 IF(YEST(K2).LT.1.0D-24)GOTO 30
261 YERR(K2) = ABS(YERR(K2)/YEST(K2))
262 IF(YERR(K2).GT.YTOL(K2))LDONE=.FALSE.
263 30 30 CONTINUE
264 IF(LDONE)THEN
265C WRITE(ISHORT,912)I,J,(YEXT(K2)K2=1,3)
266C 912 FORMAT(1X,'CONVERGED',I5,' LEVEL ',I2,' LI6 ',1P,
267C * E12.5,' LI7 ',E12.5,' BE9 ',E12.5)
268 GOTO 45
269 ENDIF
270 ENDIF
271 40 40 CONTINUE
272C IF THE PROGRAM GETS HERE THEN IT FAILED TO CONVERGE TO WITHIN
273C THE SPECIFIED TOLERANCE IN THE MAXIMUM NUMBER OF ITERATIONS.
274 WRITE(ISHORT,911)I,(YERR(K2),K2=1,3)
275 911 911 FORMAT(1X,'***LIBURN CONVERGENCE FAILURE IN SHELL ',I4,
276 * * 'ERRORS '/1P3E10.3)
277 45 45 CONTINUE
278C WRITE NEW ABUNDANCES AND EXIT.
279 HCOMP(13,I)=YEXT(1)
280 IF(HCOMP(13,I).LT.1.0D-24)HCOMP(13,I)=0.0D0
281 HCOMP(14,I)=YEXT(2)
282 IF(HCOMP(14,I).LT.1.0D-24)HCOMP(14,I)=0.0D0
283 HCOMP(15,I)=YEXT(3)
284 IF(HCOMP(15,I).LT.1.0D-24)HCOMP(15,I)=0.0D0
285 50 50 CONTINUE
286 60 60 CONTINUE
287C CONVECTION ZONE.
288C
289C SKIP IF WHOLE CZ IS BELOW THE BURNING THRESHOLD.
290C 11/02/26 MHP via Claude
291C code changed to clear LBLIOLD when the CZ is skipped, so a later step
292C does not reuse a stale end-of-step rate.
293 IF(RBE90(JENV0).LE.1.0D-32.OR.RBE9(JENV).LE.1.0D-32)THEN
294 LBLIOLD = .FALSE.
295 GOTO 200
296 ENDIF
297C FIND RATES AT THE BEGINNING OF THE TIMESTEP (USING THE DEPTH AT THE START).
298 FLI60 = 0.0D0
299 FLI70 = 0.0D0
300 FBE90 = 0.0D0
301 FM0 = 0.0D0
302 DO 65 I = JENV0,M
303 FLI60 = FLI60+HCOMP(13,I)*HS2(I)
304 FLI70 = FLI70+HCOMP(14,I)*HS2(I)
305 FBE90 = FBE90+HCOMP(15,I)*HS2(I)
306 FM0 = FM0 + HS2(I)
307 65 65 CONTINUE
308C 67 CONTINUE
309 FLI60 = FLI60/FM0
310 FLI70 = FLI70/FM0
311 FBE90 = FBE90/FM0
312C 11/02/26 MHP via Claude
313C code changed to test LBLIOLD. BLI6OLD is the log of a small rate, so
314C BLI6OLD.LE.0 was always true and the ELSE branch never ran.
315 IF(.NOT.LBLIOLD)THEN
316C COMPUTE MASS-WEIGHTED AVERAGE RATES AT THE START OF THE STEP.
317 BLI60 = 0.0D0
318 BLI70 = 0.0D0
319 BBE90 = 0.0D0
320 DO 68 I = JENV0,M
321 BLI60 = BLI60 + RLI60(I)*HS2(I)
322 BLI70 = BLI70 + RLI70(I)*HS2(I)
323 BBE90 = BBE90 + RBE90(I)*HS2(I)
324 68 68 CONTINUE
325 BLI60 = LOG(BLI60/FM0)
326 BLI70 = LOG(BLI70/FM0)
327 BBE90 = LOG(BBE90/FM0)
328 ELSE
329C USE THE RATE FROM THE END OF THE PREVIOUS TIMESTEP.
330 BLI60 = BLI6OLD
331 BLI70 = BLI7OLD
332 BBE90 = BBE9OLD
333 ENDIF
334C USE THE LOCATION OF THE TRUE EDGE OF THE CONVECTION ZONE (FX, FOUND AT
335C BEGINNING OF SR) TO ADJUST THE BURNING RATE AND MASS OF THE BOTTOM POINT
336C SUCH THAT IT INCLUDES THE ENTIRE C.Z.
337 HS2SAV = HS2(JENV)
338 IF(JENV.GT.1.AND.JENV.LT.M)THEN
339 HS2(JENV) = HS2(JENV)+FX*(HS1(JENV)-HS1(JENV-1))
340 RLI6(JENV) = RLI6(JENV)+0.5D0*FX*(RLI6(JENV-1)-RLI6(JENV))
341 RLI7(JENV) = RLI7(JENV)+0.5D0*FX*(RLI7(JENV-1)-RLI7(JENV))
342 RBE9(JENV) = RBE9(JENV)+0.5D0*FX*(RBE9(JENV-1)-RBE9(JENV))
343 ENDIF
344C FIND RATES AT THE END OF THE TIMESTEP (USING THE DEPTH AT THE END).
345C ALSO STORE INITIAL ABUNDANCES(FLI60,FLI70,FBE90).
346C FM IS THE TOTAL MASS IN THE CZ.
347 FLI6 = 0.0D0
348 FLI7 = 0.0D0
349 FBE9 = 0.0D0
350 FM = 0.0D0
351 BLI6 = 0.0D0
352 BLI7 = 0.0D0
353 BBE9 = 0.0D0
354 DO 70 I = JENV,M
355 BLI6 = BLI6 + RLI6(I)*HS2(I)
356 BLI7 = BLI7 + RLI7(I)*HS2(I)
357 BBE9 = BBE9 + RBE9(I)*HS2(I)
358 FLI6 = FLI6+HCOMP(13,I)*HS2(I)
359 FLI7 = FLI7+HCOMP(14,I)*HS2(I)
360 FBE9 = FBE9+HCOMP(15,I)*HS2(I)
361 FM = FM + HS2(I)
362 70 70 CONTINUE
363C 75 CONTINUE
364 FLI6 = FLI6/FM
365 FLI7 = FLI7/FM
366 FBE9 = FBE9/FM
367C MASS-WEIGHTED AVERAGE RATES AT THE END OF THE STEP.
368 BLI6 = LOG(BLI6/FM)
369 BLI7 = LOG(BLI7/FM)
370 BBE9 = LOG(BBE9/FM)
371C RESTORE ORIGINAL MASS TO BASE OF C.Z.
372 HS2(JENV) = HS2SAV
373C FOR THE STARTING ABUNDANCE, USE THE MIXED ABUNDANCE FOR THE
374C DEEPER CZ.
375 IF(JENV.LT.JENV0)THEN
376 FLI60 = FLI6
377 FLI70 = FLI7
378 FBE90 = FBE9
379 ENDIF
380 DO 120 J = 1,11
381C PERFORM BURNING IN NSTEPE SMALLER TIMESTEPS.
382C DEFINE FRAC - THE FRACTION OF THE TOTAL TIMESTEP IN EACH SMALL ONE -
383C AND DELT - THE FRACTIONAL TIMESTEP IN YEARS.
384 FRAC = 1.0D0/DFLOAT(NSTEPE(J))
385 DT = FRAC*DELTS
386 LDONE = .FALSE.
387C INITIALIZE ABUNDANCES.
388 FLI6 = FLI60
389 FLI7 = FLI70
390 FBE9 = FBE90
391C INITIALIZE RATES.
392 CZLI6 = BLI60-0.5D0*FRAC*(BLI6-BLI60)
393 CZLI7 = BLI70-0.5D0*FRAC*(BLI7-BLI70)
394 CZBE9 = BBE90-0.5D0*FRAC*(BBE9-BBE90)
395C BURN IN NSTEPE SMALL TIMESTEPS.
396C MHP 05/02 ADDED THE IMPACT OF MASS ACCRETION ON LIGHT
397C ELEMENT ABUNDANCES. INITIALIZE ARRAYS; SPECIES ARE
398C ASSUMED TO EXPERIENCE BURNING FOR ON AVERAGE 1/2 OF
399C THE STEP WHERE THEY ARE INITIALLY ACCRETED AND ARE
400C THEN FULLY BURNED.
401 IF(LMDOT.AND.DMDT0.GT.0.0D0)THEN
402 LDOACC = .TRUE.
403 FLI6A = 0.0D0
404 FLI7A = 0.0D0
405 FBE9A = 0.0D0
406 ELSE
407 LDOACC = .FALSE.
408 ENDIF
409 DO 100 K = 1,NSTEPE(J)
410C INCREMENT RATES BY LINEAR INTERPOLATION IN THE LOG.
411 CZLI6 = CZLI6 + FRAC*(BLI6-BLI60)
412 CZLI7 = CZLI7 + FRAC*(BLI7-BLI70)
413 CZBE9 = CZBE9 + FRAC*(BBE9-BBE90)
414 DLI6 = DT*EXP(CZLI6)
415 DLI7 = DT*EXP(CZLI7)
416 DBE9 = DT*EXP(CZBE9)
417 IF(DLI6.LT.3.0D1 .AND. FLI6.GT.1.0D-24)THEN
418 FLI6 = FLI6/EXP(DLI6)
419 ELSE
420 FLI6 = 0.0D0
421 ENDIF
422 IF(FLI6.LT.1.0D-24)FLI6 = 0.0D0
423 IF(DLI7.LT.3.0D1 .AND. FLI7.GT.1.0D-24)THEN
424 FLI7 = FLI7/EXP(DLI7)
425 ELSE
426 FLI7 = 0.0D0
427 ENDIF
428 IF(FLI7.LT.1.0D-24)FLI7 = 0.0D0
429 IF(DBE9.LT.3.0D1 .AND. FBE9.GT.1.0D-24)THEN
430 FBE9 = FBE9/EXP(DBE9)
431 ELSE
432 FBE9 = 0.0D0
433 ENDIF
434 IF(FBE9.LT.1.0D-24)FBE9 = 0.0D0
435C MHP 05/02 MASS ACCRETION. THE NET LITHIUM ADDED IN
436C A GIVEN STEP IS XXXADD WHILE THE LITHIUM ADDED IN PRIOR
437C STEPS IS XXXA; THE TWO ARE ADDED AFTER BURNING.
438C N.B. SINCE THIS IS A HALF-LIFE PROBLEM IT IS OK TO BURN
439C THE COMPONENTS SEPARATELY.
440 IF(LDOACC)THEN
441 FLI6ADD = COMPACC(13)*FRAC/EXP(0.5D0*DLI6)
442 FLI6A = FLI6A/EXP(DLI6) + FLI6ADD
443 FLI7ADD = COMPACC(14)*FRAC/EXP(0.5D0*DLI7)
444 FLI7A = FLI7A/EXP(DLI7) + FLI7ADD
445 FBE9ADD = COMPACC(15)*FRAC/EXP(0.5D0*DBE9)
446 FBE9A = FBE9A/EXP(DBE9) + FBE9ADD
447 ENDIF
448 100 100 CONTINUE
449C DO A MASS-WEIGHTED AVERAGE OF THE ORIGINAL LIGHT
450C ELEMENT CONTENT AND THE NET AMOUNT ADDED.
451C FMASSACC = DMDT*DT/ORIGINAL CZ MASS
452C NOTE: THIS FORMULATION ASSUMES THAT ALL ACCRETED MATTER
453C LANDS IN A CZ WITH THE END-OF-TIMESTEP DEPTH.
454C NOT STRICTLY TRUE, BUT NOT A BAD APPROXIMATION EITHER.
455 IF(LDOACC)THEN
456 WRITE(*,913)FLI60,FLI6,FLI6A,FLI70,FLI7,FLI7A,FMASSACC
457 913 913 FORMAT(1P7E12.3)
458 FLI6 = (FLI6*FM+FLI6A*FMASSACC)/(FM+FMASSACC)
459 FLI7 = (FLI7*FM+FLI7A*FMASSACC)/(FM+FMASSACC)
460 FBE9 = (FBE9*FM+FBE9A*FMASSACC)/(FM+FMASSACC)
461 ENDIF
462C STORE THE ABUNDANCE AS A FUNCTION OF THE TIME STEP.
463C STORE ABUNDANCES AS A FUNCTION OF TIMESTEP IN VECTOR YEST.
464 YEST(1) = FLI6
465 YEST(2) = FLI7
466 YEST(3) = FBE9
467 XEST = FRAC**2
468C USE A RATIONAL POLYNOMIAL EXTRAPOLATOR TO EXTRAPOLATE TO THE SOLUTION FOR
469C ZERO TIMESTEP.
470 CALL RATEXT(J,XEST,YEST,YEXT,YERR,3,7)
471C CHECK IF THE SOLUTION HAS CONVERGED.
472 IF(J.GT.1)THEN
473 LDONE = .TRUE.
474 DO 110 K2=1,3
475 IF(YEST(K2).LT.1.0D-24)GOTO 110
476 YERR(K2) = ABS(YERR(K2)/YEST(K2))
477 IF(YERR(K2).GT.YTOL(K2))LDONE=.FALSE.
478 110 110 CONTINUE
479 IF(LDONE)THEN
480C WRITE(ISHORT,912)JENV,J,(YEXT(K2),YERR(K2),K2=1,3)
481 GOTO 125
482 ENDIF
483 ENDIF
484 120 120 CONTINUE
485C IF THE PROGRAM GETS HERE THEN IT FAILED TO CONVERGE TO WITHIN
486C THE SPECIFIED TOLERANCE IN THE MAXIMUM NUMBER OF ITERATIONS.
487 WRITE(ISHORT,911)JENV,(YERR(K2),K2=1,3)
488 125 125 CONTINUE
489C WRITE NEW ABUNDANCES AND EXIT.
490 FLI6 = YEXT(1)
491 IF(FLI6.LT.1.0D-24)FLI6=0.0D0
492 FLI7 = YEXT(2)
493 IF(FLI7.LT.1.0D-24)FLI7=0.0D0
494 FBE9 = YEXT(3)
495 IF(FBE9.LT.1.0D-24)FBE9=0.0D0
496 DO 130 I = MAXJ,M
497 HCOMP(13,I) = FLI6
498 HCOMP(14,I) = FLI7
499 HCOMP(15,I) = FBE9
500 130 130 CONTINUE
501C STORE ENDING RATE FOR USE AT THE BEGINNING OF THE NEXT STEP.
502 BLI6OLD = BLI6
503 BLI7OLD = BLI7
504 BBE9OLD = BBE9
505 LBLIOLD = .TRUE.
506C NOW SOLVE FOR ABUNDANCES IN THE REGION WHICH BEGAN CONVECTIVE AND
507C ENDED RADIATIVE.
508 IF(JENV.LE.JENV0)GOTO 200
509C FIND STARTING AND ENDING LOCATION IN MASS OF THE CZ BASE, AND
510C ASSUME THAT THE FRACTION OF TIME SPENT IN THE CZ IS PROPORTIONAL TO
511C THE LOCATION IN MASS (I.E. A POINT 1/3 FROM THE OLD TO THE NEW CZ
512C BASE IN MASS SPENT 1/3 OF THE TIME IN THE CZ AND 2/3 OUT OF IT).
513 IF(JENV0.GT.1)THEN
514 HSBEG = 0.5D0*(HS1(JENV0)+HS1(JENV0-1))
515 ELSE
516 HSBEG = 0.0D0
517 ENDIF
518 HSEND = 0.5D0*(HS1(JENV)+HS1(JENV-1))
519 DMASS = HSBEG - HSEND
520 DO 140 I = JENV0,JENV-1
521C MHP 9/91 CHANGE TO AVOID DIVISION BY ZERO.
522C SKIP IF SHELL TEMPERATURE DROPS BELOW BURNING THRESHOLD.
523 IF(RBE9(I).LE.1.0D-32)GOTO 145
524 FRAD = (HS1(I)-HSBEG)/DMASS
525C USE FRAD*RADIATIVE RATE AND (1-FRAD)*CONVECTIVE RATE.
526 DLI6 = DELTS*EXP(FRAD*LOG(RLI6(I))+(1.0D0-FRAD)*BLI60)
527 DLI7 = DELTS*EXP(FRAD*LOG(RLI7(I))+(1.0D0-FRAD)*BLI70)
528 DBE9 = DELTS*EXP(FRAD*LOG(RBE9(I))+(1.0D0-FRAD)*BBE90)
529C***REMEMBER TO ADD FAILSAFES FOR LARGE DEPLETION***
530C KC 2025-05-31 PREVENT FLOATING POINT EXCEPTION
531C HCOMP(13,I) = HCOMP(13,I)/EXP(DLI6)
532 CALL SAFEDIVEXP(HCOMP(13,I),DLI6)
533 IF(HCOMP(13,I).LT.1.0D-24)HCOMP(13,I)=0.0D0
534C HCOMP(14,I) = HCOMP(14,I)/EXP(DLI7)
535 CALL SAFEDIVEXP(HCOMP(14,I),DLI7)
536 IF(HCOMP(14,I).LT.1.0D-24)HCOMP(14,I)=0.0D0
537 HCOMP(15,I) = HCOMP(15,I)/EXP(DBE9)
538 IF(HCOMP(15,I).LT.1.0D-24)HCOMP(15,I)=0.0D0
539 140 140 CONTINUE
540 145 145 CONTINUE
541 200 200 CONTINUE
542 RETURN
543 END
544
545C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
546C KC 2025-05-31 SAFEDIVEXP
547C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
548 SUBROUTINE SAFEDIVEXP(NUMERATOR, EXPONENT)
549C 10/20/26 MHP via Claude
550C code changed to replace implicit typing with IMPLICIT NONE.
551 IMPLICIT NONE
552 REAL*8 NUMERATOR, EXPONENT
553
554 IF (EXPONENT .LT. 709.7827D0) THEN
555 NUMERATOR = NUMERATOR/EXP(EXPONENT)
556 ELSE
557 NUMERATOR = 0.0D0
558 ENDIF
559
560 RETURN
561 END SUBROUTINE SAFEDIVEXP
1"""Python version of liburn.f (YREC development, 7be4133)."""
2
3import math
4
5import numpy as np
6
7from pyrec import constants_mod
8from pyrec import deuter_mod
9from pyrec import dimensions_mod
10from pyrec import iounits_mod
11from pyrec import liov_mod
12from pyrec import mdphy_mod
13from pyrec import newrat_mod
14from pyrec import nmlpar_mod
15from pyrec import oldmod_mod
16from pyrec import oldrat_mod
17from pyrec import prevcz_mod
18from pyrec import scrtch_mod
19from pyrec.ratext import ratext
20
21# DATA: the numbers of substeps tried, and the relative tolerances
22# (Fortran lines 93–94).
23NSTEPE = [0, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96]
24YTOL = [0.0, 1.0e-6, 1.0e-6, 1.0e-6]
25
26
27def _converged(yest: np.ndarray, yerr: np.ndarray) -> bool:
28 """Loops 30 and 110: relative errors of the extrapolation within YTOL
29 (Fortran lines 257–263 and 472–478).
30
31 Abundances below 1e-24 are not tested. ``yerr`` is made relative in
32 place, as in the Fortran.
33
34 """
35
36 ldone = True
37 for k2 in range(1, 4):
38 if yest[k2] < 1.0e-24:
39 continue
40 yerr[k2] = abs(yerr[k2] / yest[k2])
41 if yerr[k2] > YTOL[k2]:
42 ldone = False
43 return ldone
44
45
46def _write_911(i: int, yerr: np.ndarray) -> None:
47 """Format 911: convergence failure of the extrapolation (Fortran lines
48 274–276 and 487).
49
50 """
51
52 iounits_mod.write(iounits_mod.ishort,
53 f" ***LIBURN CONVERGENCE FAILURE IN SHELL {i:4d}ERRORS ")
54 iounits_mod.write(iounits_mod.ishort,
55 "".join(f"{yerr[k2]:10.3E}" for k2 in range(1, 4)))
56
57
58def liburn(delts: float, hcomp: np.ndarray, hr: np.ndarray, hs1: np.ndarray, hs2: np.ndarray, ht: np.ndarray,
59 jnenv: int, jnenv0: int, m: int) -> None:
60 """Burn Li6, Li7 and Be9 over one time step (non-rotating mixing).
61
62 The radiative interior is burned shell by shell and the surface
63 convection zone as one mixed reservoir, the rates interpolated in their
64 logs between the start (``oldrat_mod``) and end (``newrat_mod``) of the
65 step, with substeps and rational extrapolation (RATEXT) to zero step
66 size. Points left behind by a retreating zone are burned once with a
67 blended rate.
68
69 Translates: liburn.f#LIBURN (7be4133)
70
71 Parameters
72 ----------
73 delts : float
74 The time step (main.f passes seconds; see the page's question on
75 the comments' units).
76 hcomp : ndarray, shape (16, JSON + 1)
77 Composition; rows 13, 14, 15 (Li6, Li7, Be9) updated in place.
78 hr : ndarray
79 log r at each point.
80 hs1, hs2 : ndarray
81 Mass inside each point, and each point's mass (g). ``hs2[jenv]``
82 is changed and restored.
83 ht : ndarray
84 log T at each point.
85 jnenv, jnenv0 : int
86 The base of the surface convection zone at the end and the start
87 of the step.
88 m : int
89 Number of points.
90
91 Also changes ``newrat_mod`` (the bottom point's rates, a suspected
92 bug on the page) and
93 ``prevcz_mod`` (``rbase0``, ``bli6old``, ``bli7old``, ``bbe9old``,
94 ``lbliold``, ``jenvos``). Nothing is returned.
95
96 """
97
98 # Module data (Fortran lines 12–45).
99 json = dimensions_mod.json
100 cln = constants_mod.cln
101 rli6 = newrat_mod.rli6
102 rli7 = newrat_mod.rli7
103 rbe9 = newrat_mod.rbe9
104 rli60 = oldrat_mod.rli60
105 rli70 = oldrat_mod.rli70
106 rbe90 = oldrat_mod.rbe90
107 hro = oldmod_mod.hro
108 sdel = scrtch_mod.sdel
109
110 # Local arrays (Fortran lines 89–96). SAVE in the Fortran: no local is
111 # read before it is set in a call.
112 hsav = np.zeros((4, json + 1))
113 ratli6 = np.zeros(json + 1)
114 ratli7 = np.zeros(json + 1)
115 ratbe9 = np.zeros(json + 1)
116 yest = np.zeros(4)
117 yerr = np.zeros(4)
118 yext = np.zeros(4)
119 fx = 0.0
120
121 # Save the original abundances (loop 10, Fortran lines 107–112).
122 for i in range(1, m + 1): # loop 10
123 hsav[1, i] = hcomp[13, i]
124 hsav[2, i] = hcomp[14, i]
125 hsav[3, i] = hcomp[15, i]
126
127 # The base of the convection zone: the offset fx (in zones) where
128 # del_ad - del_rad crosses zero, between jnenv - 1 and jnenv (Fortran
129 # lines 113–189).
130 if 1 < jnenv < m:
131 if nmlpar_mod.lrot and nmlpar_mod.linstb:
132 ddj = mdphy_mod.delam[jnenv] - mdphy_mod.delrm[jnenv]
133 ddjm1 = mdphy_mod.delam[jnenv - 1] - mdphy_mod.delrm[jnenv - 1]
134 else:
135 ddj = sdel[3, jnenv] - sdel[1, jnenv]
136 ddjm1 = sdel[3, jnenv - 1] - sdel[1, jnenv - 1]
137 fx = max(-0.5, 0.5 - ddjm1 / (ddjm1 - ddj))
138 fx = min(0.5, fx)
139 if not nmlpar_mod.lovste:
140 jenv = jnenv
141 jenv0 = jnenv0
142 else:
143 # Envelope overshoot: move both bases down by PSCAL0, PSCAL1;
144 # the start of the step (Fortran lines 137–161).
145 if prevcz_mod.rbase0 == 0.0:
146 # The radius below shell 1 is zero (the centre).
147 if jnenv0 > 1:
148 prevcz_mod.rbase0 = 0.5 * (math.exp(cln * hro[jnenv0])
149 + math.exp(cln * hro[jnenv0 - 1]))
150 else:
151 prevcz_mod.rbase0 = 0.5 * math.exp(cln * hro[1])
152 ros = prevcz_mod.rbase0 - liov_mod.pscal0
153 jenv0 = 1
154 for j in range(jnenv0 - 1, 0, -1):
155 r = math.exp(cln * hro[j])
156 if r < ros:
157 jenv0 = j + 1
158 break # to label 11
159 else:
160 jenv0 = prevcz_mod.jenvos
161
162 # The end of the step, from the true base (Fortran lines
163 # 162–183).
164 dr = math.exp(cln * hr[jnenv]) - math.exp(cln * hr[jnenv - 1])
165 rbase = (0.5 * (math.exp(cln * hr[jnenv])
166 + math.exp(cln * hr[jnenv - 1])) - fx * dr)
167 prevcz_mod.rbase0 = rbase
168 ros = rbase - liov_mod.pscal1
169 jenv = 1
170 for j in range(jnenv - 1, 0, -1):
171 r = math.exp(cln * hr[j])
172 if r < ros:
173 jenv = j + 1
174 dr = math.exp(cln * hr[j + 1]) - r
175 fx = 0.5 - ((ros - r) / dr)
176 fx = max(-0.5, fx)
177 fx = min(0.5, fx)
178 break # to label 12
179 else:
180 jenv = jnenv
181 jenv0 = jnenv0
182 prevcz_mod.jenvos = jenv
183
184 # The radiative interior, below both bases (loop 50, Fortran lines
185 # 190–286).
186 minj = min(jenv, jenv0)
187 maxj = max(jenv, jenv0)
188 for i in range(1, minj):
189 # Question (see the page): leaves the loop, not the point.
190 if rbe9[i] <= 1.0e-32 or rbe90[i] <= 1.0e-32:
191 break # to label 60
192 if (hcomp[13, i] < 1.0e-24 and hcomp[14, i] < 1.0e-24
193 and hcomp[15, i] < 1.0e-24):
194 continue
195 if ht[i] > 7.0:
196 hcomp[13, i] = 0.0
197 hcomp[14, i] = 0.0
198 hcomp[15, i] = 0.0
199 continue
200
201 # Substeps of 1/2, 1/4, ... 1/96 of the step, extrapolated to zero
202 # (loop 40, Fortran lines 203–271).
203 ldone = False
204 for j in range(1, 12): # loop 40
205 frac = 1.0 / float(NSTEPE[j])
206 dt = frac * delts
207 hcomp[13, i] = hsav[1, i]
208 hcomp[14, i] = hsav[2, i]
209 hcomp[15, i] = hsav[3, i]
210
211 # log rates, started half a substep before the step (Fortran
212 # lines 214–220).
213 tli6 = (math.log(rli60[i])
214 - 0.5 * frac * (math.log(rli6[i]) - math.log(rli60[i])))
215 tli7 = (math.log(rli70[i])
216 - 0.5 * frac * (math.log(rli7[i]) - math.log(rli70[i])))
217 tbe9 = (math.log(rbe90[i])
218 - 0.5 * frac * (math.log(rbe9[i]) - math.log(rbe90[i])))
219
220 # Burn, the rates rising linearly in their logs (loop 20,
221 # Fortran lines 221–247).
222 for k in range(1, NSTEPE[j] + 1): # loop 20
223 tli6 = tli6 + frac * (math.log(rli6[i]) - math.log(rli60[i]))
224 tli7 = tli7 + frac * (math.log(rli7[i]) - math.log(rli70[i]))
225 tbe9 = tbe9 + frac * (math.log(rbe9[i]) - math.log(rbe90[i]))
226 ratli6[i] = dt * math.exp(tli6)
227 ratli7[i] = dt * math.exp(tli7)
228 ratbe9[i] = dt * math.exp(tbe9)
229 if ratli6[i] < 3.0e1:
230 hcomp[13, i] = hcomp[13, i] / math.exp(ratli6[i])
231 else:
232 hcomp[13, i] = 0.0
233 if ratli7[i] < 3.0e1:
234 hcomp[14, i] = hcomp[14, i] / math.exp(ratli7[i])
235 else:
236 hcomp[14, i] = 0.0
237 if ratbe9[i] < 3.0e1:
238 hcomp[15, i] = hcomp[15, i] / math.exp(ratbe9[i])
239 else:
240 hcomp[15, i] = 0.0
241
242 # Extrapolate to zero step and test (Fortran lines 248–270).
243 yest[1] = hcomp[13, i]
244 yest[2] = hcomp[14, i]
245 yest[3] = hcomp[15, i]
246 xest = frac ** 2
247 ratext(j, xest, yest, yext, yerr, 3, 7)
248 if j > 1:
249 ldone = _converged(yest, yerr)
250 if ldone:
251 break # to label 45
252
253 # Convergence failure (Fortran lines 272–276); label 45, the new
254 # abundances (Fortran lines 277–284).
255 if not ldone:
256 _write_911(i, yerr)
257 hcomp[13, i] = yext[1]
258 if hcomp[13, i] < 1.0e-24:
259 hcomp[13, i] = 0.0
260 hcomp[14, i] = yext[2]
261 if hcomp[14, i] < 1.0e-24:
262 hcomp[14, i] = 0.0
263 hcomp[15, i] = yext[3]
264 if hcomp[15, i] < 1.0e-24:
265 hcomp[15, i] = 0.0
266
267 # label 60: the convection zone; skipped if it does not burn (Fortran
268 # lines 286–296).
269 if rbe90[jenv0] <= 1.0e-32 or rbe9[jenv] <= 1.0e-32:
270 prevcz_mod.lbliold = False
271 return # label 200
272
273 # Mean abundances over the zone at the start of the step (loop 65,
274 # Fortran lines 297–311).
275 fli60 = 0.0
276 fli70 = 0.0
277 fbe90 = 0.0
278 fm0 = 0.0
279 for i in range(jenv0, m + 1):
280 fli60 = fli60 + hcomp[13, i] * hs2[i]
281 fli70 = fli70 + hcomp[14, i] * hs2[i]
282 fbe90 = fbe90 + hcomp[15, i] * hs2[i]
283 fm0 = fm0 + hs2[i]
284 fli60 = fli60 / fm0
285 fli70 = fli70 / fm0
286 fbe90 = fbe90 / fm0
287
288 # log of the zone's mean start rates, or the previous step's end ones
289 # (Fortran lines 312–333).
290 if not prevcz_mod.lbliold:
291 bli60 = 0.0
292 bli70 = 0.0
293 bbe90 = 0.0
294 for i in range(jenv0, m + 1): # loop 68
295 bli60 = bli60 + rli60[i] * hs2[i]
296 bli70 = bli70 + rli70[i] * hs2[i]
297 bbe90 = bbe90 + rbe90[i] * hs2[i]
298 bli60 = math.log(bli60 / fm0)
299 bli70 = math.log(bli70 / fm0)
300 bbe90 = math.log(bbe90 / fm0)
301 else:
302 bli60 = prevcz_mod.bli6old
303 bli70 = prevcz_mod.bli7old
304 bbe90 = prevcz_mod.bbe9old
305
306 # The bottom point's mass and rates adjusted for the offset fx
307 # (Fortran lines 334–343).
308 # Suspected bug (see the page): the rates are not restored afterwards.
309 hs2sav = hs2[jenv]
310 if 1 < jenv < m:
311 hs2[jenv] = hs2[jenv] + fx * (hs1[jenv] - hs1[jenv - 1])
312 rli6[jenv] = rli6[jenv] + 0.5 * fx * (rli6[jenv - 1] - rli6[jenv])
313 rli7[jenv] = rli7[jenv] + 0.5 * fx * (rli7[jenv - 1] - rli7[jenv])
314 rbe9[jenv] = rbe9[jenv] + 0.5 * fx * (rbe9[jenv - 1] - rbe9[jenv])
315
316 # Mean abundances and rates over the zone at the end, then the mass
317 # restored (loop 70, Fortran lines 344–372).
318 fli6 = 0.0
319 fli7 = 0.0
320 fbe9 = 0.0
321 fm = 0.0
322 bli6 = 0.0
323 bli7 = 0.0
324 bbe9 = 0.0
325 for i in range(jenv, m + 1):
326 bli6 = bli6 + rli6[i] * hs2[i]
327 bli7 = bli7 + rli7[i] * hs2[i]
328 bbe9 = bbe9 + rbe9[i] * hs2[i]
329 fli6 = fli6 + hcomp[13, i] * hs2[i]
330 fli7 = fli7 + hcomp[14, i] * hs2[i]
331 fbe9 = fbe9 + hcomp[15, i] * hs2[i]
332 fm = fm + hs2[i]
333 fli6 = fli6 / fm
334 fli7 = fli7 / fm
335 fbe9 = fbe9 / fm
336 bli6 = math.log(bli6 / fm)
337 bli7 = math.log(bli7 / fm)
338 bbe9 = math.log(bbe9 / fm)
339 hs2[jenv] = hs2sav
340
341 # A deepened zone starts from the end-of-step mean (Fortran lines
342 # 373–379).
343 if jenv < jenv0:
344 fli60 = fli6
345 fli70 = fli7
346 fbe90 = fbe9
347
348 # Substeps and extrapolation for the zone (loop 120, Fortran lines
349 # 380–484).
350 ldone = False
351 for j in range(1, 12): # loop 120
352 frac = 1.0 / float(NSTEPE[j])
353 dt = frac * delts
354 fli6 = fli60
355 fli7 = fli70
356 fbe9 = fbe90
357 czli6 = bli60 - 0.5 * frac * (bli6 - bli60)
358 czli7 = bli70 - 0.5 * frac * (bli7 - bli70)
359 czbe9 = bbe90 - 0.5 * frac * (bbe9 - bbe90)
360
361 # Accretion: material of composition COMPACC added each substep
362 # (Fortran lines 396–408).
363 if nmlpar_mod.lmdot and nmlpar_mod.dmdt0 > 0.0:
364 ldoacc = True
365 fli6a = 0.0
366 fli7a = 0.0
367 fbe9a = 0.0
368 else:
369 ldoacc = False
370
371 # Burn in substeps (loop 100, Fortran lines 409–448).
372 for k in range(1, NSTEPE[j] + 1): # loop 100
373 czli6 = czli6 + frac * (bli6 - bli60)
374 czli7 = czli7 + frac * (bli7 - bli70)
375 czbe9 = czbe9 + frac * (bbe9 - bbe90)
376 dli6 = dt * math.exp(czli6)
377 dli7 = dt * math.exp(czli7)
378 dbe9 = dt * math.exp(czbe9)
379 if dli6 < 3.0e1 and fli6 > 1.0e-24:
380 fli6 = fli6 / math.exp(dli6)
381 else:
382 fli6 = 0.0
383 if fli6 < 1.0e-24:
384 fli6 = 0.0
385 if dli7 < 3.0e1 and fli7 > 1.0e-24:
386 fli7 = fli7 / math.exp(dli7)
387 else:
388 fli7 = 0.0
389 if fli7 < 1.0e-24:
390 fli7 = 0.0
391 if dbe9 < 3.0e1 and fbe9 > 1.0e-24:
392 fbe9 = fbe9 / math.exp(dbe9)
393 else:
394 fbe9 = 0.0
395 if fbe9 < 1.0e-24:
396 fbe9 = 0.0
397 if ldoacc:
398 # Accreted material burns half a substep on arrival (Fortran
399 # lines 435–447).
400 fli6add = nmlpar_mod.compacc[13] * frac / math.exp(0.5 * dli6)
401 fli6a = fli6a / math.exp(dli6) + fli6add
402 fli7add = nmlpar_mod.compacc[14] * frac / math.exp(0.5 * dli7)
403 fli7a = fli7a / math.exp(dli7) + fli7add
404 fbe9add = nmlpar_mod.compacc[15] * frac / math.exp(0.5 * dbe9)
405 fbe9a = fbe9a / math.exp(dbe9) + fbe9add
406
407 # Mass-weighted mean with the accreted matter (Fortran lines
408 # 449–461).
409 if ldoacc:
410 fmassacc = deuter_mod.fmassacc
411 print("".join(f"{v:12.3E}" for v in (fli60, fli6, fli6a, fli70,
412 fli7, fli7a, fmassacc)))
413 fli6 = (fli6 * fm + fli6a * fmassacc) / (fm + fmassacc)
414 fli7 = (fli7 * fm + fli7a * fmassacc) / (fm + fmassacc)
415 fbe9 = (fbe9 * fm + fbe9a * fmassacc) / (fm + fmassacc)
416
417 # Extrapolate to zero step and test (Fortran lines 462–483).
418 yest[1] = fli6
419 yest[2] = fli7
420 yest[3] = fbe9
421 xest = frac ** 2
422 ratext(j, xest, yest, yext, yerr, 3, 7)
423 if j > 1:
424 ldone = _converged(yest, yerr)
425 if ldone:
426 break # to label 125
427
428 # Convergence failure (Fortran lines 485–487); label 125, the new
429 # abundances (Fortran lines 488–495).
430 if not ldone:
431 _write_911(jenv, yerr)
432 fli6 = yext[1]
433 if fli6 < 1.0e-24:
434 fli6 = 0.0
435 fli7 = yext[2]
436 if fli7 < 1.0e-24:
437 fli7 = 0.0
438 fbe9 = yext[3]
439 if fbe9 < 1.0e-24:
440 fbe9 = 0.0
441
442 # The zone's new abundances (loop 130, Fortran lines 496–500).
443 # Suspected bug (see the page): from maxj, so points newly in a
444 # deepened zone keep their old abundances.
445 for i in range(maxj, m + 1): # loop 130
446 hcomp[13, i] = fli6
447 hcomp[14, i] = fli7
448 hcomp[15, i] = fbe9
449
450 # The end rates, for the start of the next step (Fortran lines
451 # 501–505).
452 prevcz_mod.bli6old = bli6
453 prevcz_mod.bli7old = bli7
454 prevcz_mod.bbe9old = bbe9
455 prevcz_mod.lbliold = True
456
457 # The region left by a retreating zone: burned once, blended rate
458 # (Fortran lines 506–540).
459 if jenv <= jenv0:
460 return # label 200
461 if jenv0 > 1:
462 hsbeg = 0.5 * (hs1[jenv0] + hs1[jenv0 - 1])
463 else:
464 hsbeg = 0.0
465 hsend = 0.5 * (hs1[jenv] + hs1[jenv - 1])
466 # Suspected bug (see the page): dmass < 0 here, so frad lies in (-1, 0),
467 # and the weights are the other way round from the comment.
468 dmass = hsbeg - hsend
469 for i in range(jenv0, jenv): # loop 140
470 if rbe9[i] <= 1.0e-32:
471 break # to label 145
472 frad = (hs1[i] - hsbeg) / dmass
473 dli6 = delts * math.exp(frad * math.log(rli6[i]) + (1.0 - frad) * bli60)
474 dli7 = delts * math.exp(frad * math.log(rli7[i]) + (1.0 - frad) * bli70)
475 dbe9 = delts * math.exp(frad * math.log(rbe9[i]) + (1.0 - frad) * bbe90)
476 hcomp[13, i] = safedivexp(hcomp[13, i], dli6)
477 if hcomp[13, i] < 1.0e-24:
478 hcomp[13, i] = 0.0
479 hcomp[14, i] = safedivexp(hcomp[14, i], dli7)
480 if hcomp[14, i] < 1.0e-24:
481 hcomp[14, i] = 0.0
482 # Suspected bug (see the page): Be9 is not guarded by SAFEDIVEXP
483 # (math.exp raises OverflowError where the Fortran's EXP overflows).
484 hcomp[15, i] = hcomp[15, i] / math.exp(dbe9)
485 if hcomp[15, i] < 1.0e-24:
486 hcomp[15, i] = 0.0
487
488
489def safedivexp(numerator: float, exponent: float) -> float:
490 """KC's guard (2025-05-31): numerator / exp(exponent), or 0 on overflow.
491
492 Translates: liburn.f#SAFEDIVEXP (7be4133)
493
494 Returns ``numerator``, the Fortran's in-out argument.
495
496 """
497
498 # Fortran lines 554–558.
499 if exponent < 709.7827:
500 numerator = numerator / math.exp(exponent)
501 else:
502 numerator = 0.0
503 return numerator