1C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
2C
3C ESAC06
4C
5C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
6
7C subroutine esac06 (xh,ztab,t6,r,iorder,irad,*) ! KC 2025-05-31
8 subroutine esac06 (xh,t6,r,iorder,irad,*)
9C 10/14/26 MHP via Claude
10C code changed to convert COMMON/IOUNITS/ into IOUNITS_MOD.
11 USE IOUNITS_MOD
12C 10/20/26 MHP via Claude
13C code changed to convert COMMON/EEEOS06/, COMMON/AEOS06/, COMMON/EOS06MISC/ into
14C EEEOS06_MOD, AEOS06_MOD, EOS06MISC_MOD.
15 USE AEOS06_MOD
16 USE EEEOS06_MOD
17 USE EOS06MISC_MOD
18C 10/20/26 MHP via Claude
19C code changed to replace implicit typing with IMPLICIT NONE and
20C explicit declarations.
21 IMPLICIT NONE
22 REAL*8 APROP,DIXR,EGROUND,FRAC,FRACZ,GMASS06,P0,QUADEOS06,R,RI,
23 * * SLR,SLT,SUM1,SUM2,SUM23,SUM33,T6,TMASS,XH,XXI,Z
24 INTEGER I,IHI,ILO,IMD,IORDER,IPU,IQU,IR,IRAD,IS,IT,IV,IW,MF2,MG,
25 * * MH,MI,MS,MV,MX,NR,NT
26
27c..... The purpose of this subroutine is to interpolate
28c the equation of state and its derivatives in X, T6, density
29c izi=0 recalulate table indices to use; =1 keep previous
30
31c xh=hydrogen mass fraction
32c ztab is metal fraction of the EOS5_data tables you are using.
33c included only for purpose of preventing mismatch
34c t6=T6=temperature in millions of degrees kelvin
35c r=rho=Rho=density(g/cm**3)
36c..... to use esac06 insert common/eeos06/ esact,eos(10) in the calling routine.
37c This common contains the interpolated EOS values for the EOS
38c
39c..... eos(i) are obtained from a quadradic interpolation at
40c fixed T6 at three values of Rho; followed by quadratic
41c interpolation along T6. Results smoothed by mixing
42c overlapping quadratics.
43c definitions:
44c
45c T6 is the temperature in units of 10**6 K
46c
47c rho is the density in grams/cc
48c R=Rho/T6**3
49
50c eos(1) is the pressure in megabars (10**12dyne/cm**2)
51c eos(2) is energy in 10**12 ergs/gm. Zero is zero T6
52c eos(3) is the entropy in units of energy/T6
53c eos(4) is dE/dRHO at constant T6
54c eos(5) is the specific heat, dE/dT6 at constant V.
55c eos(6) is dlogP/dlogRho at constant T6.
56c Cox and Guil1 eq 9.82
57c eos(7) is dlogP/dlogT6 at conxtant Rho.
58c Cox and Guil1 eq 9.81
59c eos(8) is gamma1. Eqs. 9.88 Cox and Guili.
60c eos(9) is gamma2/(gaamma2-1). Eqs. 9.88 Cox and Guili
61
62c iorder sets maximum index for eos(i);i.e., iorder=1
63c gives just the pressure
64c
65c irad if =0 no radiation correction; if =1 adds radiation
66
67c index(i),i=1,10 sets order in which the equation of state
68c variables are stored in eos(i). Above order corresponds
69c to block data statement:
70c data (index(i),i=1,10)/1,2,3,4,5,6,7,8,9,10/.
71c If you, for example, only want to return gamma1: set iorder=1
72c and set: data (index(i),i=1,10)/8,2,3,4,5,6,7,1,9,10/
73c
74c
75 real*8 moles
76c integer w
77 parameter (mx=5,mv=10,nr=169,nt=197)
78
79c***************************************************************************
80c parameters:
81c mx is the number of x's in the 2001 Opal EOS table
82c mv is the maximum number of thermmodynamic variables in the 2001
83c Opal EOS table. Note that the input tables only contain 9.
84c nr is the number of densities in the table.
85c nt is the number of t6's associated with an x/density. See
86c below for more details about the table.
87c***************************************************************************
88
89 character blank*1
90 Character*15 ID
91C 10/13/26 MHP via Claude
92C code changed to remove COMMON/LREADCO/ and its itime first-time switch.
93C The table read and the one-time index setup now happen in SETEOS06,
94C called from SETUPS under IF(lopale06) with the other table reads.
95
96
97
98c***************************************************************************
99c common eeeos06 definitions
100c xx is an auxilliary array containing the same values of x as array xa.
101c***************************************************************************
102
103C 10/9/26 MHP via Claude
104C code changed to rename AEOS06's M member to MIDX throughout: M is
105C such a universally-used name elsewhere in this codebase (e.g. shell
106C count in calcad.f) that keeping it here was just asking for a
107C future collision. Used in this file as the composition/mixture-axis
108C loop index below (MF/MG/MH/MI are local, unshared bracket indices,
109C unaffected).
110
111c***************************************************************************
112c
113c common aeos06 definitions:
114c
115c xz is the array of thermodynamic variables, as a funtion of four arguments:
116c arg 1: identifies the value of x in the corresponding array xa, ie,
117c xz(1,*,*,*) refers to values at x=xa(1)=0.00, xz(2,*,*,*)
118c refers to values at x=xa(2)=0.20, etc.
119c arg 2: identifies which thermodynamic variable. xz(*,1,*,*) refers to
120c the thermodynamic variable specified for eos(1) - by default the
121c pressure, xz(*,2,*,*) refers to the thermodynamic variable
122c specified for eos(2), etc.
123c arg 3: identifies the value of t6 in the corresponding array t6a
124c (and t6list). e.g., xz(*,*,1,*) refers to values at
125c t6=t6a(1)=100, xz(*,*,2,*) refers to values at t6=t6a(2), etc.
126c arg 4: identifies the vaue of d, the density, in the corresponding
127c array rho. eg, xz(*,*,*,1) refers to values at d=rho(1)=10**-14,
128c xz(*,*,*,2) refers to values at d-rho(2), etc.
129c Therefore, in the default eos case, xz(1,1,1,1) would be the pressure
130c at x=0.00, t6=100, density=10**-14.
131c
132c t6list is the array of t6 (temperature in units of 10**6 degrees Kelvin)
133c The arguments match the first and fourth arguments in array xz above,
134c with the first argument being associated with a temperature and the
135c second with a density.
136c
137c In the first column, associated with the density of 10**-14,
138c temperatures range from t6=100 in t6list(1,1) to t6=.002 in
139c t6list(1,191). In the last column, associated with a density
140c of 10**7, temperatures range from t6=100 in t6list(169,1) to
141c t6=22.5 in t6list(169,16). About 10% of the table in vicinity
142c of the lower right hand corner is empty. The table is homogeneous
143c in t6, in that for each row in the table, all non-empty elements
144c have the same value of t6. In row 1, all t6's are 100. In row
145c 191, all t6's in the non-empty part of the table are .002, etc.
146c
147c rho is to array of density in units of grams per cc. This array has
148c one argument, matching the fourth argument in array xz. The
149c first density in the table is for density 10**-14, and the last
150c (169th) is for density 10**7.
151c
152c t6a is an auxiliary array of temperatures. It has the full range of
153c temmperatures, from t6a(1)=100 to t6a(191)=.002. (It exactly
154c matches the first column of t6list)
155c
156c xa is the array of the tabulated values of x, eg, xa(1)=0.00, xa(2)=0.20,
157c through xa(5)=0.80.
158c
159c dsfx is the first of three auxilliary arrays used in interpolation.
160c dfsx(2)=1/(xa(2)-xa(1)), dfsx(3)=1/(xa(3)-x1(2), ...,
161c dsfx(5)=1/(xa(5)-xa(4)).
162c
163c dfs is the second auxilliary array used in interpolation.
164c dfs(2)=1/(t6a(2)-t6a(1), ..., dfs(191)=1/(t6a(191)-t6a(190)).
165c
166c dfsr is the third auxilliary array used in interpolaltion.
167c dfsr(2)=1/(rho(2)-rho(1)), ..., dfsr(169)=1/(rho(169)-rho(168)).
168c
169c***************************************************************************
170
171C KC 2025-05-30 reordered common block elements
172C common/beos06/ iri(10),index(10),nta(nr),zz(mx),nra(nt)
173
174c***************************************************************************
175c
176c common beos96 definitions
177c
178c array index defines the order in which thermodynamic variables are
179c returned in array eos and stored in array xz. By default,
180c index(1)=1, index(2)=2 , ..., index(10)=10. For our purposes,
181c I see no reason to change index.
182c
183c array iri provides the inverse mapping to array index.
184c index(iri(i))=iri(index(1)=i, for i = 1, 2 ..., 10.
185c in the default case, iri(1)=1, iri(2)=2, ..., iri(10)=10.
186c
187c array nta provides the index in array t6a of the lower limits of
188c temperature in the xz array as a function of density. For
189c example, nta(1)=191, and t6a(nta(1))=t6a(191)=.002, the
190c lowest temperature associated with the first density,
191c rho(1)=10**-14. At the highest density, in column 169,
192c rho(169)=10**7. The associated nta(169)=16, identifies
193c the lowest temperature for this density, and
194c t6a(nta(169))=t6a(16)=22.5.
195c
196c array zz contains the values of z read in from the 2001 Opal EOS table.
197c It can be used for checking purposes.
198c
199c***************************************************************************
200
201C 10/9/26 MHP via Claude
202C code changed to merge COMMON/EEOS06/, COMMON/AAEOS06/, COMMON/BBEOS06/,
203C and COMMON/BEOS06/ into COMMON/EOS06MISC/: all four are small
204C (<2KB combined) and no member name collides with any dummy argument
205C in any file that declares this block (checked codebase-wide), unlike
206C COMMON/AEOS06/ and COMMON/EEEOS06/ (both left untouched -- AEOS06 is
207C ~14MB and collides with calcad.f's own M argument; EEEOS06 is ~1.3MB).
208
209c***************************************************************************
210c common eeos06 definitions:
211c
212c array eos is an output array containing the values of the thermodynamic
213c variables obtained by interpolation. These interplated values
214c are for the specified x, t6 and density.
215c***************************************************************************
216
217C 9/25/26 MHP via Claude
218C code changed to consolidate all I/O logical unit numbers, previously
219C scattered across COMMON/LUOUT/, COMMON/LUNUM/, COMMON/ALEX06/,
220C COMMON/ACDPTH/, COMMON/NEWOPAC/, COMMON/LOPAL95/, COMMON/ATMOS2/,
221C COMMON/ALATM03/, COMMON/OPALEOS/, and COMMON/SCV2/, into a single
222C COMMON/IOUNITS/
223 dimension frac(7)
224 data aprop/83.14511D0/
225 data ID/"OPALEOS/ESAC06:"/
226
227 save
228
229 blank=' '
230 if (iorder .gt. 9 ) then
231 write (ISHORT,'(A, " iorder cannot exceed 9")') ID
232 endif
233 if ((irad .ne. 0) .and. (irad .ne. 1)) then
234 write (ISHORT,'(A, " Irad must be 0 or 1")') ID
235 stop
236 endif
237 xxi=xh
238 ri=r
239c
240 slt=t6
241 slr=r
242c
243C 10/13/26 MHP via Claude
244C code changed to move the one-time work out of here. The index setup,
245C the xx=xa copy and the CALL READCOEOS06 are now in SETEOS06, run once
246C from SETUPS, so this routine no longer reads tables on its first call
247C and rhoofp06.f no longer has to fake a call to force that read.
248C
249C z is simply taken from the table each call: zz is filled by READCOEOS06
250C and z feeds GMASS06 below, which uses it for the metal fraction and for
251C the helium number fraction, so it is needed on every call.
252C
253C The old "z+xh > 1" test that lived here is dropped. It gated on the
254C composition of whichever call happened to be first, which was
255C rhoofp06.f's fabricated XDBG=0.5, so it never tested a real model; the
256C composition cannot be checked before the model is read in, and it is
257C checked elsewhere. Its label 61 handler is removed with it.
258 z=zz(1)
259c
260c
261c..... Determine T6,rho grid points to use in the
262c interpolation.
263 if((slt .gt. t6a(1)).or.(slt .lt. t6a(nt))) go to 62
264 if((slr .lt. rho(1)).or.(slr .gt. rho(nr))) go to 62
265c
266c
267c
268c if (izi .eq. 0) then ! freeze table indices if not 0
269 ilo=2
270 ihi=mx
271 8 8 if(ihi-ilo .gt. 1) then
272 imd=(ihi+ilo)/2
273 if(xh .le. xa(imd)+1.D-7) then
274 ihi=imd
275 else
276 ilo=imd
277 endif
278 go to 8
279 endif
280 i=ihi
281 mf=i-2
282 mg=i-1
283 mh=i
284 mi=i+1
285 mf2=mi
286 if (xh .lt. 1.D-6) then
287 mf=1
288 mg=1
289 mh=1
290 mi=2
291 mf2=1
292 endif
293 if((xh .le. xa(2)+1.D-7) .or. (xh .ge. xa(mx-2)-1.D-7)) mf2=mh
294c
295 ilo=2
296 ihi=nr
297 12 12 if(ihi-ilo .gt. 1) then
298 imd=(ihi+ilo)/2
299 if (slr .eq. rho(imd)) then
300 ihi=imd
301 go to 13
302 endif
303 if(slr .le. rho(imd)) then
304 ihi=imd
305 else
306 ilo=imd
307 endif
308 go to 12
309 endif
310 13 13 i=ihi
311 l1=i-2
312 l2=i-1
313 l3=i
314 l4=l3+1
315 iqu=3
316 if(l4 .gt. nr) iqu=2
317c
318 ilo=nt
319 ihi=2
320 11 11 if(ilo-ihi .gt. 1) then
321 imd=(ihi+ilo)/2
322 if (t6 .eq. t6list(1,imd)) then
323 ilo=imd
324 go to 14
325 endif
326 if(t6 .le. t6list(1,imd)) then
327 ihi=imd
328 else
329 ilo=imd
330 endif
331 go to 11
332 endif
333 14 14 i=ilo
334 k1=i-2
335 k2=i-1
336 k3=i
337 k4=k3+1
338 ipu=3
339 if (k4 .gt. nt) ipu=2
340 if (k3 .eq. 0) then
341 write (ISHORT,'(A, " ihi,ilo,imd",3i5)') ID,ihi,ilo,imd
342 endif
343c endif
344
345c check to determine if interpolation indices fall within
346c table boundaries. choose largest allowed size.
347 sum1=0.0D0
348 sum2=0.0D0
349 sum23=0.0D0
350 sum33=0.0D0
351 do midx=mf,mf2
352 do ir=l1,l1+1
353 do it=k1,k1+1
354 sum1=sum1+xz(midx,1,it,ir)
355 enddo
356 enddo
357 do ir=l1,l1+2
358 do it=k1,k1+2
359 sum2=sum2+xz(midx,1,it,ir)
360 enddo
361 enddo
362 if (ipu .eq. 3) then
363 do ir=l1,l1+2
364 do it=k1,k1+ipu
365 sum23=sum23+xz(midx,1,it,ir)
366 enddo
367 enddo
368 else
369 sum23=2.D+30
370 endif
371 if (iqu .eq. 3) then
372 do ir=l1,l1+3
373 do it=k1,k1+ipu
374 sum33=sum33+xz(midx,1,it,ir)
375 enddo
376 enddo
377 else
378 sum33=2.D+30
379 endif
380 enddo
381 iq=2
382 ip=2
383 if (sum2 .gt. 1.D+30) then
384 if (sum1 .lt. 1.D+25 ) then
385 k1=k3-3
386 k2=k1+1
387 k3=k2+1
388 l1=l3-3
389 l2=l1+1
390 l3=l2+1
391 go to 15
392 else
393 go to 65
394 endif
395 endif
396 if (sum23 .lt. 1.D+30) ip=3
397 if (sum33 .lt. 1.D+30) iq=3
398
399 if(t6 .ge. t6list(1,2)+1.D-7) ip=2
400 if(slr .le. rho(2)+1.D-15) iq=2
401
402 if((l3 .eq. nr) .or. (k3 .eq. nt)) then
403 iq=2
404 ip=2
405 endif
406
407 15 15 continue
408 do 124 iv=1,iorder
409 do 123 midx=mf,mf2
410
411 is=0
412c__________
413 do ir=l1,l1+iq
414 do it=k1,k1+ip
415 epl(midx,it,ir)=xz(midx,iv,it,ir)
416 is=1
417 enddo
418 enddo
419 123 123 continue
420 if((zz(mg) .ne. zz(mf)) .or. (zz(mh) .ne. zz(mf))) then
421 write(ISHORT,'(A,"Z does not match Z in OEOS06 files you are"
422 x x ," using")') ID
423 stop
424 endif
425 if(z .ne. zz(mf)) go to 66
426 is=0
427 iw=1
428 do 45 ir=l1,l1+iq
429 do it=k1,k1+ip
430 if (mf2 .eq. 1) then
431 esk(it,ir)=epl(mf,it,ir)
432 go to 46
433 endif
434 esk(it,ir)=quadeos06(is,iw,xh,epl(mf,it,ir),epl(mg,it,ir)
435 x x ,epl(mh,it,ir),xx(mf),xx(mg),xx(mh))
436 if(esk(it,ir) .gt. 1.D+20) then
437 write(ISHORT,'(A," problem it,ir,l3,k3,iq,ip=", 6i5)') ID,
438 x x it,ir,l3,k3,iq,ip
439 write(ISHORT,'(3e12.4)') (epl(ms,it,ir),ms=mf,mf+2)
440 endif
441 is=1
442 46 46 continue
443 enddo
444 45 45 continue
445
446 if (mi .eq. mf2) then ! interpolate between quadratics
447 is=0
448 iw=1
449 dixr=(xx(mh)-xh)*dfsx(mh)
450 do 47 ir=l1,l1+iq
451 do it=k1,k1+ip
452 esk2(it,ir)=quadeos06(is,iw,xh,epl(mg,it,ir),epl(mh,it,ir)
453 x x ,epl(mi,it,ir),xx(mg),xx(mh),xx(mi))
454 if(esk(it,ir) .gt. 1.D+20) then
455 write(ISHORT,'(A," problem it,ir,l3,k3,iq,ip=", 6i5)') ID,
456 x x it,ir,l3,k3,iq,ip
457 write(ISHORT,'(3e12.4)') (epl(ms,it,ir),ms=mg,mg+2)
458 endif
459 esk(it,ir)=esk(it,ir)*dixr+esk2(it,ir)*(1D0-dixr)
460 is=1
461 enddo
462 47 47 continue
463
464
465 endif
466
467 is=0
468c
469c..... completed X interpolation. Now interpolate T6 and rho on a
470c 4x4 grid. (t6a(i),i=i1,i1+3),rho(j),j=j1,j1+3)).Procedure
471c mixes overlapping quadratics to obtain smoothed derivatives.
472c
473c
474 call t6rinteos06(slr,slt)
475 eos(iv)=esact
476 124 124 continue
477 p0=t6*r
478 eos(iri(1))=eos(iri(1))*p0 ! interpolated in p/po
479 eos(iri(2))=eos(iri(2))*t6 ! interpolated in E/T6
480 tmass=gmass06(xh,z,moles,eground,fracz,frac)
481 if (irad .eq. 1) then
482 call radsub06(irad,t6,r,moles,tmass)
483 else
484 eos(iri(5))=eos(iri(5))*moles*aprop/tmass
485 endif
486 return
487
488 62 62 write(ISHORT,*) ID," T6/LogR outside of table range (62)"
489 write(ISHORT,*) "slt, t6a(1),t6a(nt):", slt, t6a(1),t6a(nt)
490 write(ISHORT,*) "slr, rho(1), rho(nr):", slr, rho(1), rho(nr)
491 RETURN 1
492 65 65 write(ISHORT,*) ID,"T6/log rho in empty region of table (65)"
493 write(ISHORT,'("xh,t6,r=", 3e12.4)') xh,t6,r
494 RETURN 1
495 66 66 write(ISHORT,*)ID," Z does not match Z in EOSdata* files you ",
496 . . "are using (66)"
497 write(ISHORT,'("mf,zz(mf)=",i5,e12.4)') mf,zz(mf)
498 write(ISHORT,'(" iq,ip,k3,l3,xh,t6,r,z= ",4i5,4e12.4)')
499 x x ip,iq,k3,l3,xh,t6,r,z
500 stop
501
502 end
503
504C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
505C 10/13/26 MHP via Claude
506C SETEOS06 - one-time setup for the OPAL 2006 equation of state.
507C
508C This is the work that ESAC06 used to do behind an itime first-time
509C switch in COMMON/LREADCO/: map index into iri, copy the tabulated x
510C values into xx, and read the table. SETUPS calls it under
511C IF(lopale06), matching the gate on the OPEN of IOPALE in parmin.f, and
512C alongside the SCV table read -- both may be active in the same run.
513C
514C index and xa are COMMON members initialized by DATA in ESAC06, so they
515C already hold their values when this runs. The file on IOPALE is opened
516C by parmin.f; READCOEOS06 only reads it.
517C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
518 subroutine seteos06
519C 10/20/26 MHP via Claude
520C code changed to convert COMMON/AEOS06/, COMMON/EEEOS06/, COMMON/EOS06MISC/ into
521C AEOS06_MOD, EEEOS06_MOD, EOS06MISC_MOD.
522 USE AEOS06_MOD
523 USE EEEOS06_MOD
524 USE EOS06MISC_MOD
525C 10/20/26 MHP via Claude
526C code changed to replace implicit typing with IMPLICIT NONE and
527C explicit declarations.
528 IMPLICIT NONE
529 INTEGER I,J,MV,MX,NR,NT
530 parameter (mx=5,mv=10,nr=169,nt=197)
531 save
532 do i=1,10
533 do j=1,10
534 if (index(i) .eq. j) iri(i)=j
535 enddo
536 enddo
537 do i=1,mx
538 xx(i)=xa(i)
539 enddo
540 call readcoeos06
541 return
542 end1"""Python version of esac06.f (YREC development, 7be4133): interpolation
2of the OPAL 2006 equation-of-state tables in X, T6 and rho, and their
3one-time setup."""
4
5import numpy as np
6
7from pyrec import aeos06_mod
8from pyrec import eeeos06_mod
9from pyrec import eos06misc_mod
10from pyrec import iounits_mod
11from pyrec.gmass06 import gmass06
12from pyrec.quadeos06 import quadeos06
13from pyrec.radsub06 import radsub06
14from pyrec.readcoeos06 import readcoeos06
15from pyrec.t6rinteos06 import t6rinteos06
16
17MX = 5 # X tables
18MV = 10 # quantities
19NR = 169 # densities
20NT = 197 # temperatures
21APROP = 83.14511 # gas constant, 1e12 erg / mole / 1e6 K (DATA)
22ID = "OPALEOS/ESAC06:"
23
24# Locals kept between calls (SAVE).
25frac = np.zeros(8)
26moles = 0.0
27
28
29def esac06(xh: float, t6: float, r: float, iorder: int, irad: int) -> bool:
30 """Interpolate the OPAL 2006 EOS tables in X (overlapping quadratics),
31 then T6 and rho (t6rinteos06), and scale the results; the ten
32 quantities go to eos06misc_mod.eos.
33
34 Translates: esac06.f#ESAC06 (7be4133)
35
36 Arguments: xh (X), t6 (T in 1e6 K), r (density), iorder (how many
37 quantities), irad (1: add radiation through radsub06). None is set.
38
39 Returns alt1, True when the Fortran takes its alternate return (*):
40 out of the table (label 62) or in its empty region (label 65).
41
42 """
43
44 global moles
45 a = aeos06_mod
46 e = eeeos06_mod
47 m = eos06misc_mod
48 io = iounits_mod
49
50 # Check the options (Fortran lines 229–236).
51 if iorder > 9:
52 # Suspected bug (see the page): warns, but goes on.
53 io.write(io.ishort, f"{ID} iorder cannot exceed 9")
54 if irad != 0 and irad != 1:
55 io.write(io.ishort, f"{ID} Irad must be 0 or 1")
56 raise SystemExit(f"{ID} Irad must be 0 or 1")
57
58 # Copies of the arguments, and Z from the table, every call (the tables
59 # are read by seteos06) (Fortran lines 237–258).
60 xxi = xh # noqa: F841 (unused in the Fortran too)
61 ri = r # noqa: F841 (unused in the Fortran too)
62 slt = t6
63 slr = r
64 z = m.zz[1]
65
66 # Is the point within the grid? (t6a decreases with its index.)
67 # (Fortran lines 261–264.)
68 if slt > a.t6a[1] or slt < a.t6a[NT] or slr < a.rho[1] or slr > a.rho[NR]:
69 return _out_of_table(slt, slr) # go to 62
70
71 # Bisection for the X tables mf, mg, mh, mi (Fortran lines 268–293).
72 ilo = 2
73 ihi = MX
74 while ihi - ilo > 1: # label 8
75 imd = (ihi + ilo) // 2
76 if xh <= a.xa[imd] + 1.0e-7:
77 ihi = imd
78 else:
79 ilo = imd
80 i = ihi
81 a.mf = i - 2
82 mg = i - 1
83 mh = i
84 mi = i + 1
85 mf2 = mi
86 if xh < 1.0e-6:
87 a.mf = 1
88 mg = 1
89 mh = 1
90 mi = 2
91 mf2 = 1
92 if xh <= a.xa[2] + 1.0e-7 or xh >= a.xa[MX - 2] - 1.0e-7:
93 mf2 = mh
94 mf = a.mf
95
96 # Bisection in rho: l1 ... l4 (Fortran lines 295–316).
97 ilo = 2
98 ihi = NR
99 while ihi - ilo > 1: # label 12
100 imd = (ihi + ilo) // 2
101 if slr == a.rho[imd]:
102 ihi = imd
103 break # go to 13
104 if slr <= a.rho[imd]:
105 ihi = imd
106 else:
107 ilo = imd
108 i = ihi # label 13
109 m.l1 = i - 2
110 m.l2 = i - 1
111 m.l3 = i
112 m.l4 = m.l3 + 1
113 iqu = 3
114 if m.l4 > NR:
115 iqu = 2
116
117 # Bisection in T6 (along t6list(1, .)): k1 ... k4 (Fortran lines
118 # 318–342).
119 ilo = NT
120 ihi = 2
121 while ilo - ihi > 1: # label 11
122 imd = (ihi + ilo) // 2
123 if t6 == a.t6list[1, imd]:
124 ilo = imd
125 break # go to 14
126 if t6 <= a.t6list[1, imd]:
127 ihi = imd
128 else:
129 ilo = imd
130 i = ilo # label 14
131 m.k1 = i - 2
132 m.k2 = i - 1
133 m.k3 = i
134 m.k4 = m.k3 + 1
135 ipu = 3
136 if m.k4 > NT:
137 ipu = 2
138 if m.k3 == 0:
139 io.write(io.ishort, f"{ID} ihi,ilo,imd{ihi:5d}{ilo:5d}{imd:5d}")
140
141 # Does the stencil avoid the empty region (values above 1e30)?
142 # (Fortran lines 345–380.)
143 sum1 = 0.0
144 sum2 = 0.0
145 sum23 = 0.0
146 sum33 = 0.0
147 l1, k1 = m.l1, m.k1
148 for midx in range(mf, mf2 + 1):
149 a.midx = midx
150 for ir in range(l1, l1 + 2):
151 for it in range(k1, k1 + 2):
152 sum1 = sum1 + a.xz[midx, 1, it, ir]
153 for ir in range(l1, l1 + 3):
154 for it in range(k1, k1 + 3):
155 sum2 = sum2 + a.xz[midx, 1, it, ir]
156 if ipu == 3:
157 for ir in range(l1, l1 + 3):
158 for it in range(k1, k1 + ipu + 1):
159 sum23 = sum23 + a.xz[midx, 1, it, ir]
160 else:
161 sum23 = 2.0e30
162 if iqu == 3:
163 for ir in range(l1, l1 + 4):
164 for it in range(k1, k1 + ipu + 1):
165 sum33 = sum33 + a.xz[midx, 1, it, ir]
166 else:
167 sum33 = 2.0e30
168
169 # Choose the largest stencil allowed (Fortran lines 381–405).
170 m.iq = 2
171 m.ip = 2
172 shifted = False
173 if sum2 > 1.0e30:
174 if sum1 < 1.0e25:
175 # Move the stencil back by one in T6 and rho, used as 3x3
176 # without a new check.
177 m.k1 = m.k3 - 3
178 m.k2 = m.k1 + 1
179 m.k3 = m.k2 + 1
180 m.l1 = m.l3 - 3
181 m.l2 = m.l1 + 1
182 m.l3 = m.l2 + 1
183 shifted = True # go to 15
184 else:
185 return _empty_region(xh, t6, r) # go to 65
186 if not shifted:
187 if sum23 < 1.0e30:
188 m.ip = 3
189 if sum33 < 1.0e30:
190 m.iq = 3
191 if t6 >= a.t6list[1, 2] + 1.0e-7:
192 m.ip = 2
193 if slr <= a.rho[2] + 1.0e-15:
194 m.iq = 2
195 if m.l3 == NR or m.k3 == NT:
196 m.iq = 2
197 m.ip = 2
198
199 # label 15: for each quantity, interpolate in X, then in T6 and rho
200 # (Fortran lines 407–476).
201 l1, k1, ip, iq = m.l1, m.k1, m.ip, m.iq
202 for iv in range(1, iorder + 1):
203
204 # The table values of quantity iv (Fortran lines 408–419).
205 for midx in range(mf, mf2 + 1):
206 a.midx = midx
207 is_ = 0
208 for ir in range(l1, l1 + iq + 1):
209 for it in range(k1, k1 + ip + 1):
210 e.epl[midx, it, ir] = a.xz[midx, iv, it, ir]
211 is_ = 1
212
213 # The tables' Z must agree (Fortran lines 420–425).
214 if m.zz[mg] != m.zz[mf] or m.zz[mh] != m.zz[mf]:
215 io.write(io.ishort,
216 f"{ID}Z does not match Z in OEOS06 files you are using")
217 raise SystemExit(
218 f"{ID}Z does not match Z in OEOS06 files you are using")
219 if z != m.zz[mf]:
220 _z_mismatch(mf, xh, t6, r, z) # go to 66 (stops)
221
222 # A quadratic through the tables mf, mg, mh (Fortran lines 426–444).
223 is_ = 0
224 iw = 1
225 for ir in range(l1, l1 + iq + 1):
226 for it in range(k1, k1 + ip + 1):
227 if mf2 == 1:
228 a.esk[it, ir] = e.epl[mf, it, ir]
229 continue # go to 46
230 a.esk[it, ir] = quadeos06(
231 is_, iw, xh, e.epl[mf, it, ir], e.epl[mg, it, ir],
232 e.epl[mh, it, ir], e.xx[mf], e.xx[mg], e.xx[mh])
233 if a.esk[it, ir] > 1.0e20:
234 io.write(io.ishort, f"{ID} problem it,ir,l3,k3,iq,ip="
235 f"{it:5d}{ir:5d}{m.l3:5d}{m.k3:5d}"
236 f"{iq:5d}{ip:5d}")
237 io.write(io.ishort, "".join(
238 f"{e.epl[ms, it, ir]:12.4e}"
239 for ms in range(mf, mf + 3)))
240 is_ = 1
241
242 # A second quadratic through mg, mh, mi, blended linearly (Fortran
243 # lines 446–465).
244 if mi == mf2:
245 is_ = 0
246 iw = 1
247 dixr = (e.xx[mh] - xh) * a.dfsx[mh]
248 for ir in range(l1, l1 + iq + 1): # loop 47
249 for it in range(k1, k1 + ip + 1):
250 a.esk2[it, ir] = quadeos06(
251 is_, iw, xh, e.epl[mg, it, ir], e.epl[mh, it, ir],
252 e.epl[mi, it, ir], e.xx[mg], e.xx[mh], e.xx[mi])
253 # Suspected bug (see the page): tests esk, not esk2.
254 if a.esk[it, ir] > 1.0e20:
255 io.write(io.ishort, f"{ID} problem it,ir,l3,k3,iq,ip="
256 f"{it:5d}{ir:5d}{m.l3:5d}{m.k3:5d}"
257 f"{iq:5d}{ip:5d}")
258 io.write(io.ishort, "".join(
259 f"{e.epl[ms, it, ir]:12.4e}"
260 for ms in range(mg, mg + 3)))
261 a.esk[it, ir] = (a.esk[it, ir] * dixr
262 + a.esk2[it, ir] * (1.0 - dixr))
263 is_ = 1
264
265 # Interpolate in T6 and rho (overlapping quadratics on 4x4)
266 # (Fortran lines 467–475).
267 is_ = 0
268 t6rinteos06(slr, slt)
269 m.eos[iv] = m.esact
270
271 # Unscale: P was tabulated as P/(T6 rho), E as E/T6; then c_V, or the
272 # radiation (Fortran lines 477–486).
273 # Suspected bug (see the page): with iorder < 5, eos(2) and eos(5) are
274 # left from an earlier call, and are rescaled again here.
275 p0 = t6 * r
276 m.eos[m.iri[1]] = m.eos[m.iri[1]] * p0
277 m.eos[m.iri[2]] = m.eos[m.iri[2]] * t6
278 tmass, moles, eground, fracz = gmass06(xh, z, moles, 0.0, 0.0, frac)
279 if irad == 1:
280 radsub06(irad, t6, r, moles, tmass)
281 else:
282 m.eos[m.iri[5]] = m.eos[m.iri[5]] * moles * APROP / tmass
283 return False
284
285
286def _out_of_table(slt: float, slr: float) -> bool:
287 """Label 62: write the message and take the alternate return (Fortran
288 lines 488–491).
289
290 """
291
292 a = aeos06_mod
293 io = iounits_mod
294 io.write(io.ishort, f" {ID} T6/LogR outside of table range (62)")
295 io.write(io.ishort, f" slt, t6a(1),t6a(nt): {slt} {a.t6a[1]} {a.t6a[NT]}")
296 io.write(io.ishort, f" slr, rho(1), rho(nr): {slr} {a.rho[1]} {a.rho[NR]}")
297 return True
298
299
300def _empty_region(xh: float, t6: float, r: float) -> bool:
301 """Label 65: write the message and take the alternate return (Fortran
302 lines 492–494).
303
304 """
305
306 io = iounits_mod
307 io.write(io.ishort, f" {ID}T6/log rho in empty region of table (65)")
308 io.write(io.ishort, f"xh,t6,r={xh:12.4e}{t6:12.4e}{r:12.4e}")
309 return True
310
311
312def _z_mismatch(mf: int, xh: float, t6: float, r: float, z: float) -> None:
313 """Label 66: the tables' Z differs from Z; write and stop (Fortran lines
314 495–500).
315
316 """
317
318 m = eos06misc_mod
319 io = iounits_mod
320 io.write(io.ishort, f" {ID} Z does not match Z in EOSdata* files you "
321 "are using (66)")
322 io.write(io.ishort, f"mf,zz(mf)={mf:5d}{m.zz[mf]:12.4e}")
323 # Suspected bug (see the page): ip, iq printed under the heading iq, ip.
324 io.write(io.ishort, f" iq,ip,k3,l3,xh,t6,r,z= {m.ip:5d}{m.iq:5d}"
325 f"{m.k3:5d}{m.l3:5d}{xh:12.4e}{t6:12.4e}{r:12.4e}{z:12.4e}")
326 raise SystemExit("ESAC06: Z does not match Z in EOSdata* files (66)")
327
328
329def seteos06() -> None:
330 """One-time setup of the OPAL 2006 EOS, called by setups when lopale06
331 is set: map index into iri, copy the X values into xx, and read the
332 table (the file is opened by parmin).
333
334 Translates: esac06.f#SETEOS06 (7be4133)
335
336 """
337
338 # Map index into iri, copy xa into xx, read the tables (Fortran lines
339 # 532–540).
340 m = eos06misc_mod
341 for i in range(1, 11):
342 for j in range(1, 11):
343 if m.index[i] == j:
344 m.iri[i] = j
345 for i in range(1, MX + 1):
346 eeeos06_mod.xx[i] = aeos06_mod.xa[i]
347 readcoeos06()