1C $$$$$$
2 SUBROUTINE CHECKC(HCOMP,IT,LPRT,M,DT,IREDO,LOK,LREDO)
3C 10/15/26 MHP via Claude
4C code changed to take JSON from DIMENSIONS_MOD instead of declaring
5C PARAMETER(JSON=5000) here.
6 USE DIMENSIONS_MOD
7C 10/14/26 MHP via Claude
8C code changed to convert COMMON/OLDMOD/ into OLDMOD_MOD.
9 USE OLDMOD_MOD
10C 10/14/26 MHP via Claude
11C code changed to convert COMMON/MDPHY/ into MDPHY_MOD.
12 USE MDPHY_MOD
13C 10/14/26 MHP via Claude
14C code changed to convert COMMON/COMP2/ into COMP2_MOD.
15 USE COMP2_MOD
16C 10/14/26 MHP via Claude
17C code changed to convert COMMON/COMP/ into COMP_MOD.
18 USE COMP_MOD
19C 10/14/26 MHP via Claude
20C code changed to convert COMMON/NMLPAR/ into NMLPAR_MOD.
21 USE NMLPAR_MOD
22C 10/14/26 MHP via Claude
23C code changed to convert COMMON/IOUNITS/ into IOUNITS_MOD.
24 USE IOUNITS_MOD
25C 10/20/26 MHP via Claude
26C code changed to replace implicit typing with IMPLICIT NONE and
27C explicit declarations.
28 IMPLICIT NONE
29 REAL*8 AMU2,ATOMWT,CMINCHK,DCOMP,DELC,DFX1,DFX2,DFX3,DFX4,DT,EMU2,
30 * * HCOMP,TEMP
31 INTEGER I,ICMAX,IREDO,IT,J,JCMAX,JEND,M
32 LOGICAL*4 LOK,LPRT,LREDO
33
34C 10/12/26 MHP via Claude
35C code changed to extend COMMON/NMLPAR/ to 234 members, adding the
36C namelist variables that the physics routines read from 27 COMMON
37C blocks which also held derived or working members. Those blocks keep
38C only their non-namelist members; five of them -- NEWMX, VMULT,
39C PMMWIND, CCOUT1 and ENVGEN -- had nothing left and are gone.
40C Derivation-only namelist variables from the same blocks went to
41C COMMON/NMLBYPASS/ instead, not here.
42C
43C Declared identically in all 121 files that hold any of these members.
44C Generated from one member list, so every copy is byte-identical; the
45C linker's 'size of symbol changed' warning is the check that they are.
46C 9/25/26 MHP via Claude
47C code changed to consolidate all I/O logical unit numbers, previously
48C scattered across COMMON/LUOUT/, COMMON/LUNUM/, COMMON/ALEX06/,
49C COMMON/ACDPTH/, COMMON/NEWOPAC/, COMMON/LOPAL95/, COMMON/ATMOS2/,
50C COMMON/ALATM03/, COMMON/OPALEOS/, and COMMON/SCV2/, into a single
51C COMMON/IOUNITS/
52C COMMON/MDPHY/HAMU(JSON),CP(JSON),DELM(JSON),DELAM(JSON),
53C * DELRM(JSON),SESUM(JSON),OM(JSON),SQDT(JSON),
54C * THDIF(JSON),SVEL(JSON),VISC(JSON),EPSM(JSON)
55C JvS 09/25 UPDATED NAMES IN COMMON BLOCK FOR CONSISTENCY ACROSS ALL SUBROUTINES
56 DIMENSION HCOMP(15,JSON),ATOMWT(4)
57 DATA ATOMWT/1.007825D0,4.002603D0,12.0D0,3.01603D0/
58 SAVE
59
60C SR CHECKC PERFORMS SEVERAL FUNCTIONS.
61C FIRST, IT CHECKS FOR ANOMALOUS COMPOSITIONS.
62C IF THEY ARE ENCOUNTERED, THE TIMESTEP IS CUT.
63C IT ALSO COMPUTES THE CHANGE IN MEAN MOLECULAR WEIGHT DUE TO
64C COMPOSITION DIFFUSION.
65C
66C INPUT VARIABLES:
67C HCOMP : MASS FRACTION OF ALL THE SPECIES BEING TRACKED AS A FUNCTION
68C OF MASS.
69C IT : THE PROGRAM ITERATES FOR THE DIFFUSION COEFFICIENTS; IT IS THE
70C ITERATION NUMBER.
71C ITDIF2 : USER PARAMETER - MAXIMUM NUMBER OF ITERATIONS.
72C IREDO : NUMBER OF TIMES DIFFUSION TIMESTEP HAS BEEN CUT.
73C LOK : FLAG SET T IF DIFFUSION COEFFICEINTS HAVE CONVERGED.
74C LPRT : FLAG SET T IF OUTPUT ABOUT CHECMICAL DIFFUSION DESIRED.
75C M : NUMBER OF MODEL POINTS.
76C
77C OUTPUT VARIABLES:
78C DT : DIFFUSION TIMESTEP, WHICH CAN BE CUT IF ERRORS IN THE COMPOSITION
79C DIFFUSION ARE DISCOVERED.
80C AMUM : NEW RUN OF MEAN MOLECULAR WEIGHT.
81C IREDO : NUMBER OF TIMES DIFFUSION TIMESTEP HAS BEEN CUT.
82C LOK : SET F IF ERRORS IN COMPOSITION DIFFUSION DISCOVERED.
83C LREDO : SET T IF ERRORS IN COMPOSITION DIFFUSION DISCOVERED.
84C
85C CHECK FOR ANOMALOUS COMPOSITIONS.
86C PRIOR TO THE LAST ITERATION, ONLY DIFFUSION OF H,HE,HE3 PERFORMED.
87C FIND NUMBER OF SPECIES BEING DIFFUSED.
88C 10/30/26 MHP via Claude
89C code changed to check species 1-4 on every iteration. SECULR mixes
90C species 5-11 (5-15 with LEXCOM) once, after its iteration loop, so the
91C last-iteration branch below tested them before they were mixed. MIX
92C and ROTMIX clip every species to [0,1] at the start of the next step.
93C IF(IT.EQ.ITDIF2)THEN
94C JEND = 11
95C ELSE
96C JEND = 4
97C ENDIF
98 JEND = 4
99 LREDO = .FALSE.
100 DO 20 J = 1,JEND
101C HCOMP(3,...) IS Z, WHICH IS NOT DIFFUSED AS A UNIT.
102 IF(J.EQ.3)GOTO 20
103 DO 10 I = 2,M-1
104 IF(HCOMP(J,I).LT.0.0D0.OR.HCOMP(J,I).GT.1.0D0)THEN
105C SOME SPECIES CAN BE MIXED INTO REGIONS WHERE THEY ARE DESTROYED VERY
106C QUICKLY. THE INTERPLAY BETWEEN DIFFUSION AND NUCLEAR BURNING CAN LEAD
107C TO SMALL NEGATIVE ABUNDANCES. THEREFORE, ZERO OUT THESE SMALL NUMBERS
108C RATHER THAN STOPPING THE CODE.
109 IF(ABS(HCOMP(J,I)).LT.1.0D-5*HCOMP(J,M))THEN
110 IF(I.EQ.1.OR.I.EQ.M)THEN
111 HCOMP(J,I) = 0.0D0
112 ELSE
113 HCOMP(J,I) = MAX(0.0D0,0.5D0*(HCOMP(J,I+1)+
114 * * HCOMP(J,I-1)))
115 ENDIF
116 GOTO 10
117 ENDIF
118 IREDO = IREDO + 1
119 IF(IREDO.GT.3)THEN
120 WRITE(6,1010) J,I,HCOMP(J,I)
121 WRITE(ISHORT,1010) J,I,HCOMP(J,I)
122 1010 1010 FORMAT(1X,39('>'),39('>')/' ERROR IN SR CHECKC'/
123 * * ' ANOMALOUS COMP NUMBER',I2,' IN ZONE',I5,' ABUNDANCE ',
124 * * 1PE12.3/' 3 ATTEMPTS AT TIMESTEP CUTTING FAILED'/
125 * * 'RUN STOPPED')
126C 10/30/26 MHP via Claude: bare STOP -> STOP 1, the run-time error code (issue 38).
127 STOP 1
128 ELSE
129 LREDO = .TRUE.
130 LOK = .FALSE.
131 DT = 0.5D0*DT
132 WRITE(6,1015)IREDO,J,I,HCOMP(J,I)
133 WRITE(ISHORT,1015)IREDO,J,I,HCOMP(J,I)
134 1015 1015 FORMAT(' ERROR IN SR CHECKC'/' TIMESTEP CUT NUMBER ',I2,
135 * * ' DUE TO ANOMALOUS COMP NUMBER',I2,' IN ZONE',I5,
136 * * ' ABUNDANCE ',1PE12.3)
137 GOTO 100
138 ENDIF
139 ENDIF
140 10 10 CONTINUE
141 20 20 CONTINUE
142 IF(IT.EQ.ITDIF2.AND.LPRT) THEN
143C FIND MAXIMUM FRACTIONAL CHANGE IN COMPOSITION AND PRINT IT OUT.
144 DELC = 0.0D0
145 ICMAX = 0
146 JCMAX = 0
147 DO 40 J = 1,JEND
148 IF(J.EQ.3)GOTO 40
149C CMINCHK IS USED TO GUARD AGAINST DIVISION BY ZERO.
150 CMINCHK = MAX(1.0D-6*HCOMP(J,M),1.0D-20)
151 DO 30 I = 1,M
152 IF(HCOMPP(J,I).LT.CMINCHK)GOTO 30
153 DCOMP = (HCOMP(J,I)-HCOMPP(J,I))/HCOMPP(J,I)
154 IF(ABS(DCOMP).GT.ABS(DELC)) THEN
155 DELC = DCOMP
156 ICMAX = I
157 JCMAX = J
158 ENDIF
159 30 30 CONTINUE
160 40 40 CONTINUE
161 WRITE(*,50)DELC,JCMAX,ICMAX
162 50 50 FORMAT(' MAX FRAC.COMP.CHANGE',1PE12.3,' SPECIES',I2,
163 * * ' AT PT.',I5)
164 IF(LEXCOM)WRITE(*,60)HCOMP(14,M),HCOMPP(14,M)
165 60 60 FORMAT(5X,'NEW SURFACE LI',1PE14.4,'OLD VALUE',E14.4)
166 ENDIF
167C FIND NEW RUN OF MEAN MOLECULAR WEIGHT ASSUMING FULLY IONIZED GAS.
168C AMUENV IS(1/MEAN MOLECULAR WEIGHT PER ION OF THE SURFACE MIXTURE.)
169C CORRECTION FOR PARTIAL IONIZATION NEEDED IN MASSIVE STARS.
170 IF(IT.GT.1)THEN
171 DO 90 I = 1,M
172 DFX1 = HCOMP(1,I) - XENV
173 DFX2 = HCOMP(2,I) - YENV
174 DFX3 = HCOMP(3,I) - ZENV
175 DFX4 = HCOMP(4,I) - Y3ENV
176 TEMP = AMUENV + DFX1/ATOMWT(1) + DFX2/ATOMWT(2) +
177 * * DFX3/ATOMWT(3) + DFX4/ATOMWT(4)
178 AMU2 = 1.0D0/TEMP
179 TEMP = HCOMP(1,I)/ATOMWT(1)+2.0D0*(HCOMP(4,I)/ATOMWT(4)
180 * * + HCOMP(2,I)/ATOMWT(2)) + 0.5D0*HCOMP(3,I)
181 EMU2 = 1.0D0/TEMP
182 AMUM(I) = AMU2*EMU2/(AMU2+EMU2)
183 90 90 CONTINUE
184 ENDIF
185 100 100 CONTINUE
186
187 RETURN
188 END1"""Python version of checkc.f (YREC 7be4133): the composition checked after
2rotational mixing, and the mean molecular weight recomputed."""
3
4from pyrec import comp2_mod
5import numpy as np
6from pyrec import comp_mod
7from pyrec import iounits_mod
8from pyrec import mdphy_mod
9from pyrec import nmlpar_mod
10from pyrec import oldmod_mod
11
12# DATA ATOMWT: H, 4He, Z (as carbon), 3He; index 0 unused.
13ATOMWT = (None, 1.007825, 4.002603, 12.0, 3.01603)
14
15
16def checkc(hcomp: np.ndarray, it: int, lprt: bool, m: int, dt: float, iredo: int, lok: bool,
17 lredo: bool) -> tuple[float, int, bool, bool]:
18 """Check abundances after a step of rotational mixing: repair tiny
19 anomalies, halve the time step otherwise (stop after 3 cuts), and
20 recompute the mean molecular weight AMUM of a fully ionized gas.
21
22 Translates: checkc.f#CHECKC (7be4133)
23
24 Arguments: hcomp[species, shell] (changed in place where repaired); it,
25 the iteration on the diffusion coefficients; lprt, print the largest
26 change at the last iteration; m, the number of shells; dt, the time
27 step; iredo, the number of cuts so far; lok, lredo.
28
29 Returns (dt, iredo, lok, lredo): dt halved, iredo increased and
30 lok = False, lredo = True after an anomaly that is not repaired.
31
32 """
33
34 hcompp = oldmod_mod.hcompp
35
36 # X, Y and 3He on every iteration (species 5-11 are mixed after
37 # SECULR's iteration loop) (Fortran lines 98–141).
38 jend = 4
39 lredo = False
40 for j in range(1, jend + 1): # label 20
41 if j == 3: # Z is not diffused as a unit.
42 continue
43 for i in range(2, m): # label 10
44 if hcomp[j, i] < 0.0 or hcomp[j, i] > 1.0:
45 # Question (see the page): the threshold is relative to the
46 # surface value, which may be zero.
47 if abs(hcomp[j, i]) < 1.0e-5 * hcomp[j, m]:
48 # Never true in this loop (unused?, as in the Fortran).
49 if i == 1 or i == m:
50 hcomp[j, i] = 0.0
51 else:
52 hcomp[j, i] = max(0.0, 0.5 * (hcomp[j, i + 1]
53 + hcomp[j, i - 1]))
54 continue
55 iredo = iredo + 1
56 if iredo > 3:
57 msg = (" " + ">" * 78 + "\n ERROR IN SR CHECKC\n"
58 f" ANOMALOUS COMP NUMBER{j:2d} IN ZONE{i:5d}"
59 f" ABUNDANCE {hcomp[j, i]:12.3E}\n"
60 " 3 ATTEMPTS AT TIMESTEP CUTTING FAILED\n"
61 "RUN STOPPED") # format 1010
62 print(msg)
63 iounits_mod.write(iounits_mod.ishort, msg)
64 raise SystemExit(1) # STOP 1, the run-time error code
65 lredo = True
66 lok = False
67 dt = 0.5 * dt
68 msg = (" ERROR IN SR CHECKC\n"
69 f" TIMESTEP CUT NUMBER {iredo:2d}"
70 f" DUE TO ANOMALOUS COMP NUMBER{j:2d} IN ZONE{i:5d}"
71 f" ABUNDANCE {hcomp[j, i]:12.3E}") # format 1015
72 print(msg)
73 iounits_mod.write(iounits_mod.ishort, msg)
74 # GOTO 100: the end, without recomputing mu.
75 return dt, iredo, lok, lredo
76
77 # The largest fractional change over the step (Fortran lines 142–166).
78 if it == nmlpar_mod.itdif2 and lprt:
79 delc = 0.0
80 icmax = 0
81 jcmax = 0
82 for j in range(1, jend + 1): # label 40
83 if j == 3:
84 continue
85 cminchk = max(1.0e-6 * hcomp[j, m], 1.0e-20)
86 for i in range(1, m + 1): # label 30
87 if hcompp[j, i] < cminchk:
88 continue
89 dcomp = (hcomp[j, i] - hcompp[j, i]) / hcompp[j, i]
90 if abs(dcomp) > abs(delc):
91 delc = dcomp
92 icmax = i
93 jcmax = j
94 print(f" MAX FRAC.COMP.CHANGE{delc:12.3E} SPECIES{jcmax:2d}"
95 f" AT PT.{icmax:5d}") # format 50
96 if nmlpar_mod.lexcom:
97 print(f" NEW SURFACE LI{hcomp[14, m]:14.4E}"
98 f"OLD VALUE{hcompp[14, m]:14.4E}") # format 60
99
100 # Mean molecular weight of a fully ionized gas (Fortran lines 170–184).
101 if it > 1:
102 for i in range(1, m + 1): # label 90
103 dfx1 = hcomp[1, i] - comp_mod.xenv
104 dfx2 = hcomp[2, i] - comp2_mod.yenv
105 dfx3 = hcomp[3, i] - comp_mod.zenv
106 dfx4 = hcomp[4, i] - comp2_mod.y3env
107 temp = (comp_mod.amuenv + dfx1 / ATOMWT[1] + dfx2 / ATOMWT[2]
108 + dfx3 / ATOMWT[3] + dfx4 / ATOMWT[4])
109 amu2 = 1.0 / temp
110 temp = (hcomp[1, i] / ATOMWT[1]
111 + 2.0 * (hcomp[4, i] / ATOMWT[4]
112 + hcomp[2, i] / ATOMWT[2])
113 + 0.5 * hcomp[3, i])
114 emu2 = 1.0 / temp
115 mdphy_mod.amum[i] = amu2 * emu2 / (amu2 + emu2)
116
117 return dt, iredo, lok, lredo