1C $$$$$$
2C MHP 10/02 ECOD, ECOD2 NO LONGER USED; OMITTED FROM CALL
3 SUBROUTINE CHECKJ(HD,HJMP,HJMSAV,HR,HS,HS2,HV,IMIN,IMAX,IT,LCZ,
4C * LPRT,M,DT,ETA2,HI,HJM,IREDO,LOK,LREDO,OMEGA, ! KC 2025-05-31
5 * * M,DT,ETA2,HI,HJM,IREDO,LOK,LREDO,OMEGA,
6 * * QIW,R0,WSAV,ID,IDM,LOKAD)
7C 10/15/26 MHP via Claude
8C code changed to take JSON from DIMENSIONS_MOD instead of declaring
9C PARAMETER(JSON=5000) here.
10 USE DIMENSIONS_MOD
11C 10/14/26 MHP via Claude
12C code changed to convert COMMON/TEMP2/ into TEMP2_MOD.
13 USE TEMP2_MOD
14C 10/14/26 MHP via Claude
15C code changed to convert COMMON/QUADD/ into QUADD_MOD.
16 USE QUADD_MOD
17C 10/14/26 MHP via Claude
18C code changed to convert COMMON/OLDROT/ into OLDROT_MOD.
19 USE OLDROT_MOD
20C 10/14/26 MHP via Claude
21C code changed to convert COMMON/ERRMOM/ into ERRMOM_MOD.
22 USE ERRMOM_MOD
23C 10/14/26 MHP via Claude
24C code changed to convert COMMON/EGRID/ into EGRID_MOD.
25 USE EGRID_MOD
26C 10/14/26 MHP via Claude
27C code changed to convert COMMON/DIFAD2/ into DIFAD2_MOD.
28 USE DIFAD2_MOD
29C 10/14/26 MHP via Claude
30C code changed to convert COMMON/DIFAD/ into DIFAD_MOD.
31 USE DIFAD_MOD
32C 10/14/26 MHP via Claude
33C code changed to convert COMMON/NMLPAR/ into NMLPAR_MOD.
34 USE NMLPAR_MOD
35C 10/14/26 MHP via Claude
36C code changed to convert COMMON/IOUNITS/ into IOUNITS_MOD.
37 USE IOUNITS_MOD
38C 10/20/26 MHP via Claude
39C code changed to replace implicit typing with IMPLICIT NONE and
40C explicit declarations.
41 IMPLICIT NONE
42 REAL*8 ACC,DELJ,DJ,DT,ETA2,HD,HI,HJM,HJMP,HJMSAV,HR,HS,HS2,
43 * * HV,OMEGA,QIW,R0,TOLER0,WSAV
44 INTEGER I,IBEG,IBOT,ID,IDM,IEND,II,IJMAX,IMAX,IMIN,IREDO,IT,
45 * * ITOP,J,M,NHIST
46C 10/30/26 MHP via Claude
47C code changed to size the per-iteration history from ITDIF2. DELJI and
48C IMAXI were dimensioned 16 and indexed by the iteration number, which
49C nothing bounded, so ITDIF2 > 16 wrote past them. They are kept
50C (SAVE) across SECULR's iterations, which print the whole history.
51 REAL*8, ALLOCATABLE :: DELJI(:)
52 INTEGER, ALLOCATABLE :: IMAXI(:)
53 LOGICAL*4 LCZ,LOK,LOKAD,LREDO
54C * QIW,R0,WSAV,ID,IDM,ECOD,ECOD2,LOKAD)
55C 10/12/26 MHP via Claude
56C code changed to extend COMMON/NMLPAR/ to 234 members, adding the
57C namelist variables that the physics routines read from 27 COMMON
58C blocks which also held derived or working members. Those blocks keep
59C only their non-namelist members; five of them -- NEWMX, VMULT,
60C PMMWIND, CCOUT1 and ENVGEN -- had nothing left and are gone.
61C Derivation-only namelist variables from the same blocks went to
62C COMMON/NMLBYPASS/ instead, not here.
63C
64C Declared identically in all 121 files that hold any of these members.
65C Generated from one member list, so every copy is byte-identical; the
66C linker's 'size of symbol changed' warning is the check that they are.
67C 9/25/26 MHP via Claude
68C code changed to consolidate all I/O logical unit numbers, previously
69C scattered across COMMON/LUOUT/, COMMON/LUNUM/, COMMON/ALEX06/,
70C COMMON/ACDPTH/, COMMON/NEWOPAC/, COMMON/LOPAL95/, COMMON/ATMOS2/,
71C COMMON/ALATM03/, COMMON/OPALEOS/, and COMMON/SCV2/, into a single
72C COMMON/IOUNITS/
73C MHP 7/93
74C 10/13/26 MHP via Claude
75C code changed to drop COMMON/VARFC/, declared here but never used.
76C MHP 3/93
77C MHP 11/94
78 DIMENSION HD(JSON),HJM(JSON),HJMP(JSON),HR(JSON),HS(JSON),
79 * * HS2(JSON),LCZ(JSON),ETA2(JSON),HI(JSON),OMEGA(JSON),
80 * * QIW(JSON),R0(JSON),HJMSAV(JSON),
81 * * ID(JSON),HV(JSON),WSAV(JSON)
82C * ECOD(JSON),ECOD2(JSON)
83 SAVE
84
85C SR CHECKJ PERFORMS SEVERAL FUNCTIONS.
86C FIRST, IT CHECKS FOR NEGATIVE SPECIFIC ANGULAR MOMENTA.
87C IF THEY ARE ENCOUNTERED, THE TIMESTEP IS CUT.
88C SECOND, IT GUARDS AGAINST REVERSAL OF ANGULAR VELOCITY GRADIENTS.
89C THIRD, IT COMPARES THE RUN OF OMEGA TO THAT FOR THE PREVIOUS ITERATION.
90C IF THE RUN HAS CHANGED BY LITTLE ENOUGH THEN THE DIFFUSION EQUATIONS
91C ARE CONSIDERED CONVERGED. LOK=T IF THE RUN HAS CONVERGED.
92C FOURTH, IT CORRECTS THE MOMENT OF INERTIA FOR CHANGES IN OMEGA.
93C INPUT VARIABLES:
94C FIFTH, IT WRITES OUT DETAILS OF THE DIFFUSION IF REQUESTED.
95C
96C HD : RUN OF LOG DENSITY.
97C HJM : RUN OF SPECIFIC ANGULAR MOMENTUM AFTER THE CURRENT ITERATION.
98C HJMP : RUN OF SPECIFIC ANGULAR MOMENTUM AFTER THE LAST ITERATION
99C FOR THE DIFFUSION COEFFICIENTS.
100C HJMSAV : RUN OF SPECIFIC ANGULAR MOMENTUM AT THE BEGINNING OF THE
101C TIMESTEP.
102C HR : RUN OF LOG RADIUS.
103C HS : RUN OF LOG MASS.
104C HS2 : MASS (UNLOGGED) CONTAINED IN EACH OF THE MODEL POINTS.
105C HV : RUN OF CHARACTERISTIC DIFFUSION VELOCITIES. IF HV(I)>0 THEN ZONE
106C I IS UNSTABLE WITH RESPECT TO ZONE I-1.
107C IMIN,IMAX : THE FIRST AND LAST ZONES AT WHICH HV IS COMPUTED.
108C *NOTE: IMIN = 2 AND IMAX = NUMBER OF MODEL POINTS UNLESS A SURFACE
109C OR CENTRAL CONVECTION ZONE EXISTS.
110C IT : ITERATION NUMBER.
111C ITDIF2 : MAXIMUM NUMBER OF ITERATIONS ALLOWED IN A GIVEN DIFFUSION
112C TIMESTEP.
113C LCZ : ARRAY SET T IF A ZONE IS CONVECTIVE FOR ANGULAR MOMENTUM
114C REDISTRIBUTION PURPOSES (I.E. INCLUDES OVERSHOOT REGION.)
115C LPRT : FLAG SET T IF MODEL I/O DESIRED.
116C M : NUMBER OF MODEL POINTS.
117C
118C OUTPUT VARIABLES:
119C
120C DT : DIFFUSION TIMESTEP, WHICH CAN BE CUT IF ERRORS IN THE DIFFUSION
121C ARE DETECTED.
122C ETA2,R0 : AUXILLARY QUANTITIES USED TO FIND OMEGA GIVEN J/M.
123C HI,QIW : RUN OF MOMENTS OF INERTIA AND THEIR DERIVATIVES WITH RESPECT
124C TO OMEGA.
125C HJMP : ARRAY HJMP IS SET EQUAL TO HJM AFTER CONVERGENCE IS CHECKED.
126C IREDO : NUMBER OF TIMES DIFFUSION TIMESTEP HAS BEEN CUT.
127C LOK : FLAG SET T IF DIFFUSION CEOFFICIENTS HAVE CONVERGED.
128C LREDO : FLAG SET T IF A PROBLEM REQUIRES CUTTING THE DIFFUSION TIMESTEP.
129C OMEGA : RUN OF ANGULAR VELOCITY.
130C
131C ONE HISTORY SLOT PER ITERATION; SECULR RUNS IT = 1,ITDIF2.
132 NHIST = MAX(ITDIF2,IT,1)
133 IF(.NOT.ALLOCATED(DELJI))THEN
134 ALLOCATE(DELJI(NHIST),IMAXI(NHIST))
135 ELSE IF(SIZE(DELJI).LT.NHIST)THEN
136 DEALLOCATE(DELJI,IMAXI)
137 ALLOCATE(DELJI(NHIST),IMAXI(NHIST))
138 ENDIF
139C CHECK FOR NEGATIVE J/M.
140 LOK = .FALSE.
141 LREDO = .FALSE.
142 IF(LOKAD)THEN
143 LOK = .TRUE.
144 LREDO = .FALSE.
145 ENDIF
146 DO 10 I = 1,M
147 IF(HJM(I).LE.0.0D0) THEN
148 IREDO = IREDO + 1
149C STOP IF TIMESTEP CUT MORE THAN 3 TIMES.
150C IF(IREDO.GT.3)THEN
151 IF(IREDO.GT.0)THEN
152 WRITE(6,1000) I
153 WRITE(ISHORT,1000) I
154 1000 1000 FORMAT(1X,39('>'),39('<')/5X,'ERROR IN SR CHECKJ'/
155 * * 5X,'NEGATIVE J/M ENCOUNTERED IN ZONE',I5,
156 * * ' AND 3 ATTEMPTS AT CUTTING TIMESTEP FAILED'/
157 * * 'RUN STOPPED')
158C 10/30/26 MHP via Claude: bare STOP -> STOP 1, the run-time error code (issue 38).
159 STOP 1
160 ELSE
161 LREDO = .TRUE.
162 DT = 0.5D0*DT
163 WRITE(6,1005)IREDO,I
164 WRITE(ISHORT,1005)IREDO,I
165 1005 1005 FORMAT(5X,'ERROR IN SR CHECKJ'/5X,'TIMESTEP CUT,',1X,
166 * * 'NUMBER',I5,' DUE TO NEGATIVE J/M IN ZONE',I5)
167 GOTO 240
168 ENDIF
169 ENDIF
170 10 10 CONTINUE
171C CHECK IF THE FRACTIONAL CHANGE IN OMEGA RELATIVE TO THE PREVIOUS
172C ITERATION IS SMALL ENOUGH TO BE CONSIDERED CONVERGED.
173C ALSO LOCATE THE ZONE WHERE THE MAXIMUM CHANGE OCCURS FOR I/O.
174 DELJI(IT) = (HJM(1)-HJMP(1))/HJMP(1)
175 HJMP(1) = HJM(1)
176 IMAXI(IT) = 1
177 DO 140 I = 2,M
178 DJ = (HJM(I)-HJMP(I))/HJMP(I)
179 IF(ABS(DJ).GT.ABS(DELJI(IT))) THEN
180 DELJI(IT) = DJ
181 IMAXI(IT) = I
182 ENDIF
183 HJMP(I) = HJM(I)
184 140 140 CONTINUE
185 IF(ABS(DELJI(IT)).LE.DJOK) THEN
186C LOK = .FALSE.
187C ELSE
188 LOK = .TRUE.
189 ENDIF
190C FIND THE RUN OF OMEGA THAT CORRESPONDS TO THE NEW RUN OF J/M.
191C THE MOMENT OF INERTIA IS A FUNCTION OF OMEGA WHICH IS SOLVED
192C ITERATIVELY. BECAUSE THE ERROR IN THE DIFFUSION IS OF ORDER DJOK,
193C RELAX TOLERANCE FOR MOMENT OF INERTIA ITERATION EXCEPT FOR THE
194C FINAL STEP.
195 TOLER0 = TOLERI
196 ACC = ACFPFT
197 IF(IT.LT.ITDIF2.AND..NOT.LOK)THEN
198 TOLERI = MAX(DJOK*1.0D-2,TOLER0)
199 ACFPFT = MAX(DJOK*1.0D-2,ACC)
200 ENDIF
201 CALL GETROT(HD,HJM,HR,HS,HS2,LCZ,M,ETA2,HI,OMEGA,QIW,R0)
202 TOLERI = TOLER0
203 ACFPFT = ACC
204C SEARCH FOR REVERSAL OF OMEGA GRADIENTS. IF ONE EXISTS, ENFORCE
205C SOLID-BODY ROTATION IN THE OFFENDING PAIR OF ZONES.
206 I = M
207 IBOT = M
208 20 20 CONTINUE
209C POSITIVE OMEGA GRADIENT ENCOUNTERED.
210C 10/30/26 MHP via Claude
211C code changed to enter at 1.0D-15 rad/s, the threshold the rest of this
212C search uses. At 1 rad/s (the Sun turns at 2.9e-6) it never fired.
213 IF(OMEGA(I)-OMEGA(I-1).GT.1.0D-15)THEN
214C IF PREVIOUS GRADIENT WAS POSITIVE, LEAVE ALONE.
215 IF(WOLD(I)-WOLD(I-1).GT.1.0D-15)THEN
216 I = IBOT-1
217 IBOT = I
218 IF(I.GT.1)THEN
219 GOTO 20
220 ELSE
221 GOTO 130
222 ENDIF
223 ENDIF
224C SIGN OF D OMEGA/DR HAS CHANGED,INDICATING AN ERROR IN DIFFUSION.
225C MIX THE OFFENDING ZONES TO SOLID BODY ROTATION.
226C ITOP IS THE UPPERMOST UNSTABLE SHELL.
227 ITOP = I
228C IF ADJACENT TO A CONVECTION ZONE, MIX THE CONVECTION ZONE AS WELL.
229 IF(LCZ(ITOP) .AND. ITOP.LT.M) THEN
230 DO 30 J = ITOP + 1,M
231 IF(.NOT.LCZ(J)) GOTO 40
232 30 30 CONTINUE
233 40 40 ITOP = J - 1
234 ENDIF
235C IBOT IS THE BOTTOM UNSTABLE ZONE. CHECK FOR ADJACENT CZ AS ABOVE.
236 IBOT = I - 1
237 IF(LCZ(IBOT) .AND. IBOT.GT.1) THEN
238 DO 50 J = IBOT - 1,1,-1
239 IF(.NOT.LCZ(J)) GOTO 60
240 50 50 CONTINUE
241 60 60 IBOT = J + 1
242 ENDIF
243C ENFORCE A SOLID BODY ROTATION CURVE FROM IBOT TO ITOP.
244 CALL SOLID(HD,HJM,HR,HS,HS2,IBOT,ITOP,ETA2,HI,
245 * * OMEGA,QIW,R0,M)
246C NOW CHECK TO SEE IF THE REDISTRIBUTION HAS GENERATED ANY NEW REVERSALS.
247 70 70 CONTINUE
248 LREDO = .FALSE.
249C CHECK FOR GRADIENT REVERSALS BELOW ZONE IBOT.
250 IF(IBOT.GT.1) THEN
251 IF(OMEGA(IBOT)-OMEGA(IBOT-1).GT.1.0D-15)THEN
252 IF(WOLD(IBOT)-WOLD(IBOT-1).LT.1.0D-15)THEN
253 LREDO = .TRUE.
254 IBOT = IBOT - 1
255 IF(LCZ(IBOT) .AND. IBOT.GT.1) THEN
256 DO 80 J = IBOT - 1,1,-1
257 IF(.NOT.LCZ(J)) GOTO 90
258 80 80 CONTINUE
259 90 90 IBOT = J + 1
260 ENDIF
261 ENDIF
262 ENDIF
263 ENDIF
264C CHECK FOR GRADIENT REVERSALS ABOVE ZONE ITOP.
265 IF(ITOP.LT.M) THEN
266 IF(OMEGA(ITOP+1)-OMEGA(ITOP).GT.1.0D-15)THEN
267 IF(WOLD(ITOP+1)-WOLD(ITOP).LT.1.0D-15)THEN
268 LREDO = .TRUE.
269 ITOP = ITOP+1
270 IF(LCZ(ITOP) .AND. ITOP.LT.M) THEN
271 DO 100 J = ITOP+1,M
272 IF(.NOT.LCZ(J)) GOTO 110
273 100 100 CONTINUE
274 110 110 ITOP = J - 1
275 ENDIF
276 ENDIF
277 ENDIF
278 ENDIF
279C IF LREDO=T THEN THE REDISTRIBUTION OF ANGULAR MOMENTUM IN A REVERSED
280C REGION HAS EFFECTED A CHANGE IN OMEGA AT ONE OF BOTH OF THE BOUNDARIES
281C THAT HAS CAUSED A NEW GRADIENT REVERSAL AT THE BOUNDARY.
282 IF(LREDO) THEN
283 CALL SOLID(HD,HJM,HR,HS,HS2,IBOT,ITOP,ETA2,HI,
284 * * OMEGA,QIW,R0,M)
285 GOTO 70
286 ENDIF
287 IF(IT.EQ.ITDIF2.OR.LOK)WRITE(*,120)IBOT,ITOP,IT
288 120 120 FORMAT(5X,'OMEGA GRADIENT REVERSAL BETWEEN ZONES ',
289 * * I5,' AND ',I5,' ITERATION ',I5)
290 ENDIF
291 130 130 CONTINUE
292C RETURN FOR NEXT ZONE.
293 I = IBOT - 1
294 IBOT = I
295 IF(I.GT.1) GOTO 20
296C I/O FOR END OF DIFFUSION STEP.
297 IF(IT.EQ.ITDIF2.OR.LOK)THEN
298C FIND MAXIMUM FRACTIONAL CHANGE IN J/M OVER TIMESTEP.
299 DELJ = 0.0D0
300 IJMAX = 0
301C MHP 10/02 ICRIT REMOVED
302C IF(ICRIT.EQ.0)THEN
303 II = 1
304C ELSE
305C II = ICRIT
306C ENDIF
307 DO 150 I = II,M
308 DJ = (HJM(I)-HJMSAV(I))/HJMSAV(I)
309 IF(ABS(DJ).GT.ABS(DELJ)) THEN
310 DELJ = DJ
311 IJMAX = I
312 ENDIF
313 150 150 CONTINUE
314 IF(.NOT.LOKAD)THEN
315 WRITE(*,160)DELJ,IJMAX,(DELJI(J),IMAXI(J),J=1,IT)
316 160 160 FORMAT(' MAX D(J/M)/(J/M)',1PE12.3,' AT PT.',I5,
317 * * ' BY ITERATION'/5(1X,E11.3,I4))
318 ENDIF
319C
320C G Somers 11/14, I AM TURNING OFF THE OUTPUT TO THE .FULL FILE.
321C THE AM CHANGES WILL NOT BE RECORDED, BUT THIS CAN BE TRIVIALLY
322C EXTRACTED FROM THE EXTENDED .STORE FILE.
323C
324C SKIP OUTPUT IF NOT DESIRED.
325C IF(.NOT.LPRT)GOTO 240
326 IF(.TRUE.)GOTO 240
327C G Somers END
328C
329C IF NPRTPT IS SET TO A LARGE NUMBER, SKIP DETAILED OUTPUT.
330 IF(NPRTPT.GT.M)GOTO 240
331 WRITE(IMODPT,170)
332 170 170 FORMAT(' SHELL',3X,'OMEGA',5X,'DEL OMEGA',6X,'J/M',7X,'DEL J/M')
333C DETERMINE WHICH SHELLS TO PRINT.
334C FIRST POINT ALWAYS PRINTED OUT.
335 ID(1) = 1
336 IDM = 2
337 IBEG = MAX(IMIN,NPRTPT)
338 IEND = MIN(IMAX,INT(IMAX/NPRTPT)*NPRTPT)
339C PRINT OUT EVERY NPRTPT POINTS. WHEN V=0, SKIP POINTS.
340 DO 180 J = IBEG,IEND,NPRTPT
341C IF(HV(J).EQ.0.0D0)GOTO 180
342 ID(IDM) = J
343 IDM = IDM + 1
344 180 180 CONTINUE
345C OUTERMOST MODEL POINT (OR POINT AT BASE OF SURFACE C.Z.)ALWAYS PRINTED.
346 IF(ID(IDM-1).NE.IMAX)THEN
347 ID(IDM) = IMAX
348 ELSE
349 IDM = IDM-1
350 ENDIF
351C I/O CONCERNING ANGULAR MOMENTUM TRANSPORT.
352 DO 200 I=1,IDM
353 WRITE(IMODPT,190)ID(I),OMEGA(ID(I)),OMEGA(ID(I))-
354 * * WSAV(ID(I)),HJM(ID(I)),HJM(ID(I))-HJMSAV(ID(I))
355 190 190 FORMAT(1X,I5,1P4E12.3)
356 200 200 CONTINUE
357C I/O CONCERNING DIFFUSION VELOCITIES AND SCALE LENGTHS.
358 WRITE(IMODPT,210)
359 210 210 FORMAT(1X,'SHELL',4X,'VES0',9X,'VES',7X,'VGSF0',8X,'VGSF',9X,
360 * * 'VSS',9X,'RAT',8X,'VTOT',7X,'LENGTH',8X,'VMU')
361 DO 230 I = 1,IDM
362 WRITE(IMODPT,220)ID(I),VES0(ID(I)),VES(ID(I)),VGSF0(ID(I)),
363 * * VGSF(ID(I)),VSS(ID(I)),RAT(ID(I)),HV(ID(I)),HLE(ID(I)),
364 * * VMU(ID(I))
365 220 220 FORMAT(1X,I5,1P10E12.3)
366 230 230 CONTINUE
367 IF(LDIFAD)THEN
368C DO I = 1,IDM
369C WRITE(IMODPT,221)ID(I),VES(ID(I)),VESA(ID(I)),
370C * VESD(ID(I)),
371C * ECOD(ID(I)),ECOD2(ID(I)),ECOD3(ID(I)),ECOD4(ID(I))
372C 221 FORMAT(1X,I5,1P7E12.3)
373C END DO
374 IF(IDM.EQ.NTOT)THEN
375 DO I = 1,IDM
376 WRITE(IMODPT,221)I,CHI(I),VES(I),VESA(I),
377 * * VESD(I),ECHI(I),ECOD3(I),ECOD4(I)
378 221 221 FORMAT(1X,I5,1P7E12.3)
379 END DO
380 ELSE IF(IDM.LT.NTOT)THEN
381 DO I = 1,IDM
382 WRITE(IMODPT,221)I,CHI(I),VES(I),VESA(I),
383 * * VESD(I),ECHI(I),ECOD3(I),ECOD4(I)
384 END DO
385 DO I = IDM+1,NTOT
386 WRITE(IMODPT,222)I,ECHI(I),ECOD3(I),ECOD4(I)
387 222 222 FORMAT(1X,I5,48X,1P3E12.3)
388 END DO
389 ELSE
390 DO I = 1,NTOT
391 WRITE(IMODPT,221)I,CHI(I),VES(I),VESA(I),
392 * * VESD(I),ECHI(I),ECOD3(I),ECOD4(I)
393 END DO
394 DO I = NTOT+1,IDM
395 WRITE(IMODPT,223)I,CHI(I),VES(I),VESA(I),
396 * * VESD(I)
397 223 223 FORMAT(1X,I5,1P4E12.3)
398 END DO
399 ENDIF
400 ENDIF
401 ENDIF
402 240 240 CONTINUE
403 RETURN
404 END1"""Python version of checkj.f (YREC 7be4133): checks one iteration of
2angular-momentum transport."""
3
4import numpy as np
5
6from pyrec import difad2_mod
7from pyrec import difad_mod
8from pyrec import egrid_mod
9from pyrec import errmom_mod
10from pyrec import iounits_mod
11from pyrec import nmlpar_mod
12from pyrec import oldrot_mod
13from pyrec import quadd_mod
14from pyrec import temp2_mod
15from pyrec.getrot import getrot
16from pyrec.solid import solid
17
18# Locals kept between calls (SAVE): the largest change of j over each
19# iteration, and where it occurred.
20# Allocatable in the Fortran, sized MAX(ITDIF2, IT, 1) (index 0 unused).
21delji = None
22imaxi = None
23
24
25def checkj(hd: np.ndarray, hjmp: np.ndarray, hjmsav: np.ndarray, hr: np.ndarray, hs: np.ndarray, hs2: np.ndarray,
26 hv: np.ndarray, imin: int, imax: int, it: int, lcz: np.ndarray, m: int, dt: float, eta2: np.ndarray,
27 hi: np.ndarray, hjm: np.ndarray, iredo: int, lok: bool, lredo: bool, omega: np.ndarray, qiw: np.ndarray,
28 r0: np.ndarray, wsav: np.ndarray, id_: np.ndarray, idm: int,
29 lokad: bool) -> tuple[float, int, bool, bool, int]:
30 """Check one iteration of angular-momentum diffusion: stop on j <= 0,
31 test convergence against nmlpar_mod.djok, get Omega (and the moment
32 of inertia) from j with getrot, and remove reversals of the Omega
33 gradient by local solid-body rotation (solid).
34
35 Translates: checkj.f#CHECKJ (7be4133)
36
37 Arguments: the model hd, hr, hs, hs2 (logs, and the shell masses), hv
38 (the diffusion velocities), lcz (convective for angular momentum);
39 hjmp, j after the previous iteration (set to hjm here); hjmsav, j at
40 the start of the step; imin, imax, the range of hv; it, the iteration;
41 m, the number of points; dt, the diffusion step; the arrays eta2, hi,
42 omega, qiw, r0 (set by getrot and solid); hjm, j after this iteration
43 (changed by solid); iredo; wsav, Omega at the start of the step (for
44 the print); id_, the printed shells (ID); idm; lokad, convergence
45 assumed on entry.
46
47 Returns (dt, iredo, lok, lredo, idm): the step (halved in a branch
48 that never runs), the count of cuts, converged or not, a cut needed,
49 and the number of printed shells (set only in the switched-off print).
50
51 """
52
53 global delji, imaxi
54 n = nmlpar_mod
55
56 # One history slot per iteration; SECULR runs it = 1..itdif2 (Fortran
57 # lines 131–138).
58 nhist = max(n.itdif2, it, 1)
59 if delji is None or delji.size - 1 < nhist:
60 delji = np.zeros(nhist + 1)
61 imaxi = np.zeros(nhist + 1, dtype=int)
62
63 # Check for negative j (Fortran lines 139–170).
64 lok = False
65 lredo = False
66 if lokad:
67 lok = True
68 lredo = False
69 for i in range(1, m + 1):
70 if hjm[i] <= 0.0:
71 iredo = iredo + 1
72 # Question (see the page): IREDO.GT.0, not .GT.3, so the
73 # time-step cut below never runs (unused?).
74 if iredo > 0:
75 msg = (" " + ">" * 39 + "<" * 39 + "\n"
76 + " ERROR IN SR CHECKJ\n"
77 + f" NEGATIVE J/M ENCOUNTERED IN ZONE{i:5d}"
78 + " AND 3 ATTEMPTS AT CUTTING TIMESTEP FAILED\n"
79 + "RUN STOPPED")
80 print(msg)
81 iounits_mod.write(iounits_mod.ishort, msg)
82 raise SystemExit(1) # STOP 1, the run-time error code
83 else:
84 lredo = True
85 dt = 0.5 * dt
86 msg = (" ERROR IN SR CHECKJ\n"
87 + f" TIMESTEP CUT, NUMBER{iredo:5d}"
88 + f" DUE TO NEGATIVE J/M IN ZONE{i:5d}")
89 print(msg)
90 iounits_mod.write(iounits_mod.ishort, msg)
91 return dt, iredo, lok, lredo, idm # GOTO 240
92
93 # Convergence: the largest fractional change of j over the iteration,
94 # and where it occurs; hjmp becomes hjm (Fortran lines 171–189).
95 delji[it] = (hjm[1] - hjmp[1]) / hjmp[1]
96 hjmp[1] = hjm[1]
97 imaxi[it] = 1
98 for i in range(2, m + 1):
99 dj = (hjm[i] - hjmp[i]) / hjmp[i]
100 if abs(dj) > abs(delji[it]):
101 delji[it] = dj
102 imaxi[it] = i
103 hjmp[i] = hjm[i]
104 if abs(delji[it]) <= n.djok:
105 lok = True
106
107 # Omega from the new j. The error in the diffusion is of order DJOK,
108 # so the tolerances of the moment-of-inertia iteration are relaxed
109 # except at the last iteration, and restored after (Fortran lines
110 # 190–203).
111 toler0 = errmom_mod.toleri
112 acc = n.acfpft
113 if it < n.itdif2 and not lok:
114 errmom_mod.toleri = max(n.djok * 1.0e-2, toler0)
115 n.acfpft = max(n.djok * 1.0e-2, acc)
116 getrot(hd, hjm, hr, hs, hs2, lcz, m, eta2, hi, omega, qiw, r0)
117 errmom_mod.toleri = toler0
118 n.acfpft = acc
119
120 # Reversals of the Omega gradient, from the surface inward: enforce
121 # solid-body rotation in the offending pair of shells (Fortran lines
122 # 204–223).
123 wold = oldrot_mod.wold
124 i = m
125 ibot = m
126 while True: # label 20
127 if omega[i] - omega[i - 1] > 1.0e-15:
128 if wold[i] - wold[i - 1] > 1.0e-15:
129 # The previous gradient was positive too: leave it.
130 i = ibot - 1
131 ibot = i
132 if i > 1:
133 continue # GOTO 20
134 # else GOTO 130
135 else:
136 # ITOP, the uppermost unstable shell, and IBOT, the
137 # lowest, each widened over an adjacent convection zone;
138 # then SOLID (Fortran lines 224–245).
139 itop = i
140 if lcz[itop] and itop < m:
141 j = itop + 1
142 while j <= m and lcz[j]:
143 j += 1
144 itop = j - 1 # label 40
145 ibot = i - 1
146 if lcz[ibot] and ibot > 1:
147 j = ibot - 1
148 while j >= 1 and lcz[j]:
149 j -= 1
150 ibot = j + 1 # label 60
151 solid(hd, hjm, hr, hs, hs2, ibot, itop, eta2, hi, omega,
152 qiw, r0, m)
153
154 # Widen the range while its boundaries show new reversals
155 # (Fortran lines 246–286).
156 while True: # label 70
157 lredo = False
158 if ibot > 1:
159 if omega[ibot] - omega[ibot - 1] > 1.0e-15:
160 if wold[ibot] - wold[ibot - 1] < 1.0e-15:
161 lredo = True
162 ibot = ibot - 1
163 if lcz[ibot] and ibot > 1:
164 j = ibot - 1
165 while j >= 1 and lcz[j]:
166 j -= 1
167 ibot = j + 1 # label 90
168 if itop < m:
169 if omega[itop + 1] - omega[itop] > 1.0e-15:
170 if wold[itop + 1] - wold[itop] < 1.0e-15:
171 lredo = True
172 itop = itop + 1
173 if lcz[itop] and itop < m:
174 j = itop + 1
175 while j <= m and lcz[j]:
176 j += 1
177 itop = j - 1 # label 110
178 if lredo:
179 solid(hd, hjm, hr, hs, hs2, ibot, itop, eta2, hi,
180 omega, qiw, r0, m)
181 continue # GOTO 70
182 break
183
184 # Fortran lines 287–289.
185 if it == n.itdif2 or lok:
186 print(f" OMEGA GRADIENT REVERSAL BETWEEN ZONES "
187 f"{ibot:5d} AND {itop:5d} ITERATION {it:5d}")
188
189 # label 130: the next shell (Fortran lines 291–295).
190 i = ibot - 1
191 ibot = i
192 if i <= 1:
193 break
194
195 # Output at the end of the diffusion step (Fortran lines 296–318).
196 if it == n.itdif2 or lok:
197 # The largest fractional change of j over the time step.
198 delj = 0.0
199 ijmax = 0
200 ii = 1
201 for i in range(ii, m + 1):
202 dj = (hjm[i] - hjmsav[i]) / hjmsav[i]
203 if abs(dj) > abs(delj):
204 delj = dj
205 ijmax = i
206 if not lokad:
207 line = f" MAX D(J/M)/(J/M){delj:12.3E} AT PT.{ijmax:5d} BY ITERATION"
208 for j in range(1, it + 1):
209 if (j - 1) % 5 == 0:
210 line += "\n"
211 line += f" {delji[j]:11.3E}{imaxi[j]:4d}"
212 print(line)
213
214 # The detailed output to IMODPT is switched off (IF(.TRUE.)GOTO
215 # 240); kept here as in the Fortran (G. Somers 11/14); unused?
216 # (Fortran lines 319–330).
217 if False:
218 idm = _detailed_print(hjm, hjmsav, hv, imin, imax, m, omega,
219 wsav, id_, idm)
220
221 # label 240 (Fortran line 402).
222 return dt, iredo, lok, lredo, idm
223
224
225def _detailed_print(hjm: np.ndarray, hjmsav: np.ndarray, hv: np.ndarray, imin: int, imax: int, m: int,
226 omega: np.ndarray, wsav: np.ndarray, id_: np.ndarray, idm: int) -> int:
227 """The switched-off detailed print of CHECKJ (after IF(.TRUE.)GOTO
228 240): Omega, j and the diffusion velocities at every NPRTPT-th shell.
229 Returns idm.
230
231 """
232
233 n = nmlpar_mod
234 t2 = temp2_mod
235 imodpt = iounits_mod.imodpt
236 nprtpt = n.nprtpt
237 if nprtpt > m:
238 return idm
239
240 # Omega and j at the chosen shells (Fortran lines 331–356).
241 iounits_mod.write(imodpt, " SHELL OMEGA DEL OMEGA J/M"
242 " DEL J/M")
243 # The first point is always printed.
244 id_[1] = 1
245 idm = 2
246 ibeg = max(imin, nprtpt)
247 iend = min(imax, int(imax / nprtpt) * nprtpt)
248 for j in range(ibeg, iend + 1, nprtpt):
249 id_[idm] = j
250 idm = idm + 1
251 # The outermost point (or the base of a surface CZ) is always printed.
252 if id_[idm - 1] != imax:
253 id_[idm] = imax
254 else:
255 idm = idm - 1
256 for i in range(1, idm + 1):
257 k = id_[i]
258 iounits_mod.write(imodpt, f" {k:5d}{omega[k]:12.3E}"
259 f"{omega[k] - wsav[k]:12.3E}{hjm[k]:12.3E}"
260 f"{hjm[k] - hjmsav[k]:12.3E}")
261
262 # The diffusion velocities (Fortran lines 357–366).
263 iounits_mod.write(imodpt, " SHELL VES0 VES VGSF0"
264 " VGSF VSS RAT"
265 " VTOT LENGTH VMU")
266 for i in range(1, idm + 1):
267 k = id_[i]
268 vals = (t2.ves0[k], t2.ves[k], t2.vgsf0[k], t2.vgsf[k], t2.vss[k],
269 quadd_mod.rat[k], hv[k], t2.hle[k], t2.vmu[k])
270 iounits_mod.write(imodpt, f" {k:5d}"
271 + "".join(f"{v:12.3E}" for v in vals))
272
273 # With LDIFAD, the even grid's coefficients (Fortran lines 367–400).
274 if n.ldifad:
275 eg = egrid_mod
276 ntot = eg.ntot
277
278 def row7(i: int) -> str:
279 vals = (eg.chi[i], t2.ves[i], difad2_mod.vesa[i],
280 difad2_mod.vesd[i], eg.echi[i], difad_mod.ecod3[i],
281 difad_mod.ecod4[i])
282 return f" {i:5d}" + "".join(f"{v:12.3E}" for v in vals)
283
284 if idm == ntot:
285 for i in range(1, idm + 1):
286 iounits_mod.write(imodpt, row7(i))
287 elif idm < ntot:
288 for i in range(1, idm + 1):
289 iounits_mod.write(imodpt, row7(i))
290 for i in range(idm + 1, ntot + 1):
291 iounits_mod.write(imodpt, f" {i:5d}" + " " * 48
292 + f"{eg.echi[i]:12.3E}"
293 f"{difad_mod.ecod3[i]:12.3E}"
294 f"{difad_mod.ecod4[i]:12.3E}")
295 else:
296 for i in range(1, ntot + 1):
297 iounits_mod.write(imodpt, row7(i))
298 for i in range(ntot + 1, idm + 1):
299 vals = (eg.chi[i], t2.ves[i], difad2_mod.vesa[i],
300 difad2_mod.vesd[i])
301 iounits_mod.write(imodpt, f" {i:5d}"
302 + "".join(f"{v:12.3E}" for v in vals))
303 return idm