1C Subroutine ALFILEIN
2
3C PURPOSE
4C To read Allard atmosphere information files into the appropriate input arrays
5C and calculate associated auxilliary information. These files contain the
6C desired properties of stellar and substellar atmospheres from atmospheric
7C tables developed by France Allard, et al. These properties include Log(P)
8C at Teff and GL, as well as Log(P) and Log(T) at Tau = 100 for this Teff and
9C GL. Currently supported input formats include the old Allard, et al, circa
10C 1999 NextGen format, to which we are adding the newer 2007 NextGen format.
11C As other formats become available, weanticipate being able to handle those also.
12
13C Input files may be in Old NextGen file format or in the new Allard Atmosphere
14C file format. This format, is of a conceptually different style from the old.
15C The data part of the new file format has one record for each Teff, GL, FE/h,
16C Alpha. The rest of that record contains the associated PL @ T=Teff,
17C PL @ Tau=100 and TL @ Tau100. In other words the new format has one data record
18C for each item in the table. The old format had one record for each GL in the
19C table. This new format is more flexible in supporting future needs.
20
21C The contents of these files are put into the arrays and elements used by the
22C Allard Atmosphere table lookup routine, ALSURFP. In addition, we fill any vacant
23C entries in the table with -999.0. A value of -999. in the table identifies the
24C entry as "invalid." In addition, key auxililiary arrays, as described below, are
25C created.
26
27
28
29
30
31
32 SUBROUTINE ALFILEIN(FALLARD)
33C 10/14/26 MHP via Claude
34C code changed to convert COMMON/ALATM01/ into ALATM01_MOD.
35 USE ALATM01_MOD
36C 10/14/26 MHP via Claude
37C code changed to convert COMMON/NMLPAR/ into NMLPAR_MOD.
38 USE NMLPAR_MOD
39C 10/14/26 MHP via Claude
40C code changed to convert COMMON/IOUNITS/ into IOUNITS_MOD.
41 USE IOUNITS_MOD
42C 10/20/26 MHP via Claude
43C code changed to replace implicit typing with IMPLICIT NONE and
44C explicit declarations.
45 IMPLICIT NONE
46 REAL*8 ALPHA,FEH,GL,P100L,PL,T100,TEFF,TEFFS
47 INTEGER I,I1,IRECNO,J,J1,NGA,NHDR,NTA
48C
49C Parameters NTA and NGA are respectively the maximum expected numbers
50C of Teff's and GL's we expect to encounter, even in future tables
51C The maximum nuber of Teff's in the current table is nTeff, and the
52C associated max number of GL's is nGL.
53
54 PARAMETER(NTA=250,NGA=25)
55 CHARACTER*256 FALLARD
56 CHARACTER*35 inrec
57 CHARACTER*256 inrec2
58 DIMENSION TEFFs(NTA)
59C 10/14/26 MHP via Claude
60C code changed to drop the redundant LOGICAL*4 LALTPTau100 declaration.
61C LALTPTau100 is a COMMON/NMLPAR/ member, and explicitly typing a COMMON
62C member is legal while doing the same to a USE-associated name is
63C rejected, so it has to go before NMLPAR becomes a module. Removing it
64C changes nothing: IMPLICIT LOGICAL*4(L) already gives that type.
65C 9/25/26 MHP via Claude
66C code changed to consolidate all I/O logical unit numbers, previously
67C scattered across COMMON/LUOUT/, COMMON/LUNUM/, COMMON/ALEX06/,
68C COMMON/ACDPTH/, COMMON/NEWOPAC/, COMMON/LOPAL95/, COMMON/ATMOS2/,
69C COMMON/ALATM03/, COMMON/OPALEOS/, and COMMON/SCV2/, into a single
70C COMMON/IOUNITS/
71
72C 10/12/26 MHP via Claude
73C code changed to extend COMMON/NMLPAR/ to 234 members, adding the
74C namelist variables that the physics routines read from 27 COMMON
75C blocks which also held derived or working members. Those blocks keep
76C only their non-namelist members; five of them -- NEWMX, VMULT,
77C PMMWIND, CCOUT1 and ENVGEN -- had nothing left and are gone.
78C Derivation-only namelist variables from the same blocks went to
79C COMMON/NMLBYPASS/ instead, not here.
80C
81C Declared identically in all 121 files that hold any of these members.
82C Generated from one member list, so every copy is byte-identical; the
83C linker's 'size of symbol changed' warning is the check that they are.
84C MHP 8/25 Removed character file names from common block
85C 10/13/26 MHP via Claude
86C code changed to remove COMMON/ALATM04/: its four members DUMMY1..DUMMY4
87C are placeholders that no file references in executable code. Removing
88C it also retires the hazard behind the earlier alfilein.f fix, where a
89C mis-indented EXTERNAL was absorbed into this declaration and renamed
90C DUMMY4.
91
92C 10/11/26 MHP via Claude
93C code changed to re-indent EXTERNAL to column 7. It had only 5 leading
94C blanks, putting the "E" in column 6, which in fixed form marks a
95C CONTINUATION line. The compiler therefore read the previous statement
96C as
97C COMMON /ALATM04/ DUMMY1,DUMMY2,DUMMY3,DUMMY4EXTERNALSORT_SHELL
98C a legal 24-character name, so it compiled silently: the EXTERNAL
99C declaration was discarded and the 4th block member was renamed.
100C Behavior-neutral today -- sort_shell is only reached via CALL (and is
101C defined in this file), and DUMMY4EXTERNALSORT_SHELL was one implicit
102C REAL*8 at the same position as DUMMY4 in the other five files that
103C declare ALATM04 -- but it would break the moment a 5th member were
104C added, sort_shell were passed as an actual argument, or this file were
105C converted to free form.
106 EXTERNAL sort_shell
107
108 SAVE
109
110C set output arrays to invalid values
111 do i = 1,NTA
112 TEFFLs(i) = -999.D0
113 do j = 1,NGA
114 GLs(j) = -999.D0
115 PLs(i,j) = -999.D0
116 P100Ls(i,j) = -999.D0
117 T100Ls(i,j) = -999.D0
118 enddo
119 enddo
120C the input file name is in FALLARD and its unit number is IOATMA
121 OPEN(IOATMA,FILE=FALLARD,STATUS='OLD',ERR= 899)
122 goto 900
123
124 899 899 continue
125 write(*,*)
126 write(*,*) 'ALFILEIN: Allard File OPEN Failure'
127 write(*,*) ' Unit-Number, FIle-Name are: ',
128 x x ioatma,fallard
129 goto 9999 ! Error exit
130
131 900 900 continue
132C Decide what kind of input file
133 read(ioatma,901) inrec
134 901 901 format(a)
135 rewind(ioatma)
136 if(inrec(4:7) .eq. '3.50') goto 100 ! Old NextGen file type
137 if (inrec(4:9) .eq. 'ALLARD') goto 200 ! Allard file type
138C If we get here, the input file is invalid
139 write(ISHORT,*)'*** Invalid Allard Atmosphere input file ***'
140 Write(ISHORT,*) "First record was '",inrec,"'"
141 write(*,*)'*** Invalid Allard Atmosphere input file ***'
142 Write(*,*) "First record was '",inrec,"'"
143 goto 9999 ! The error exit
144
145C Process old-stype nextgen input file.
146 100 100 CONTINUE
147 LOldNextGen = .TRUE.
148 write(ishort,*) 'ALFileIn: File Description: 1999 NEXTGEN',
149 x x ' (Old NEXTGEN)'
150
151C Ensure that we are not requesting PL,TL at Tau-100. These are not
152C present in the old Nextgen 1 files. If requested, fail
153C 10/29/26 MHP via Claude
154C code changed to test the namelist flag LALTPTau100. The misspelled
155C LATMTPTau100 was a never-set local, so an old NextGen 1 file was
156C accepted with its tau=100 tables still holding the -999 marker.
157 If(LALTPTau100) then
158 write(*,*)
159 write(*,*) 'ALFileIN: Invalid old Allard input file ',
160 x x 'for requested PT,TL at Tau=100'
161 write(ISHORT,*)
162 write(ISHORT,*) 'ALFileIN: Invalid old Allard input file ',
163 x x 'for requested PT,TL at Tau=100'
164 goto 9999 ! The ERROR EXIT
165 endif
166C READ RANGE OF GRAVITIES
167 nTeff = 54
168 nGL = 5
169 READ(IOATMA,905)(GLs(I),I=1,nGL)
170 905 905 FORMAT(5F7.2/)
171c read(ioatma,*)
172
173C READ RANGE OF TEMPERATURES
174 READ(IOATMA,907)(TEFFLs(I),I=1,nTeff)
175 907 907 FORMAT(10(1P5E16.8,/),1P4E16.8,/)
176c read(ioatma,*)
177 READ(IOATMA,909)((PLs(J,I),I=1,nGL),J=1,nTeff)
178 909 909 FORMAT(1P5E16.8)
179 CLOSE(IOATMA)
180 Goto 500 ! NORMAL EXIT
181C Old Nextgen input file has been read an put into the proper arrays
182
183C Process new-style Allard Atmosphere file
184 200 200 CONTINUE
185 LOldNextGen = .FALSE.
186 read(ioatma,911) nhdr, inrec2 ! nhdr is number of header recoreds to skip over
187 911 911 format(i2,X,a)
188 write(ishort,912) 'ALFilein: New Allard Atm: File Description: ',inrec2(1:47)
189 912 912 format(2a)
190C Skip over rest of header, if any
191 if(nhdr .gt. 1) then
192 do i = 2, nhdr
193 read(ioatma,*)
194 enddo
195 endif
196 nGL = 0
197 nTeff = 0
198 nFeH = 0
199 nALPHA = 0
200 irecno = 0
201
202C First we read the file to count the # of Teff,GL,Fe/H and Alpha's
203
204 299 299 read(IOATMA,915,end=400,err=399) Teff,GL,FeH,Alpha ! Process first record(s)
205 915 915 format(F6.0,3F6.2)
206 irecno=irecno+1
207
208C If the record does not have the correct FeH and Alpha, we skip it
209 if((DABS(FeH-ALATM_FeH).gt.1D-5) .or.
210 x x (DABS(Alpha-ALATM_Alpha).gt.0D0)) then
211 goto 299 ! On to next record. Stay in this part until we get an acceptable record
212 endif
213 nTeff = 1 ! Initialize counters and variables after reading first record
214 nGL = 1
215 nFeH = 1
216 nALPHA = 1
217 Teffs(nTeff) = Teff
218 GLs(nGL) = GL
219 FeHs(nFeH) = FeH
220 ALPHAs(nAlpha) = Alpha
221
222 300 300 read(IOATMA,915,end=400,err=399) Teff,GL,FeH,Alpha
223 irecno=irecno+1
224
225C If the record does not have the correct FeH and Alpha, we skip it
226 if((DABS(FeH-ALATM_FeH).gt.1D-5) .or.
227 x x (DABS(Alpha-ALATM_Alpha).gt.0D0)) then
228 goto 316 ! On to next record
229 endif
230
231C Now check Teffs, increase counter if needed
232 do i = 1,nTeff ! Skip out if any old Teff is a match
233 if (DABS(Teff-Teffs(i)) .lt.1D-6) goto 310
234 enddo
235 nTeff = nTeff+1 ! count the nextTeff's
236 Teffs(nTeff) = Teff ! save the new Teff in array Teffs
237 310 310 CONTINUE
238
239c Now check GLs, increase counter if needed
240 do i = 1,nGL ! Skip out if any old GL is a match
241 if (DABS(GL-GLs(i)) .lt.1D-6) goto 312
242 enddo
243 nGL = nGL+1 ! count the next GL
244 GLs(nGL) = GL ! save the new GL in array GLs
245 312 312 CONTINUE
246
247
248c Now check FeHs, increase counter if needed
249 do i = 1,nGL ! Skip out if any old FeH is a match
250 if (DABS(FeH-FeHs(i)) .lt.1D-6) goto 314
251 enddo
252 nFeH = nFeH+1 ! count the next FeH
253 FeHs(nFeH) = FeH ! save the new FeH in array FeHs
254 314 314 CONTINUE
255
256c Now check ALPHAs, increase counter if needed
257 do i = 1,nGL ! Skip out if any old ALPHA is a match
258 if (DABS(ALPHA-ALPHAs(i)) .lt.1D-6) goto 316
259 enddo
260 nALPHA = nALPHA+1 ! count the next ALPHA
261 ALPHAs(nALPHA) = ALPHA ! save the new ALPHA in array ALPHAs
262 316 316 CONTINUE
263
264 goto 300 ! go on to next record
265
266C File Read error exit
267 399 399 continue
268 write(*,*)
269 write(*,*)'AtTabInit Pass 1 File Error '
270 write(*,*)
271 goto 9999
272
273 400 400 continue
274c write(*,*) 'nTeff,nGL,nFeH,nALPHA: ',nTeff,nGL,nFeH,nALPHA
275
276C Now we have unsorted Teff,GL,Fe/H and Alpha's.
277C The next step is to sort them
278 call sort_shell(nTeff,Teffs)
279 call sort_shell(nGL,GLs)
280 call sort_shell(nFeH,FeHs)
281 call sort_shell(nALPHA,ALPHAs)
282
283C Now we can rewind the input file and start over for real
284 rewind(ioatma) ! go back to the beginning of the file
285
286C Skip over header
287 do i = 1,nhdr
288 read(ioatma,*)
289 enddo
290
291 410 410 read(IOATMA,920,END=500,ERR=499) Teff,GL,FeH,Alpha,PL,
292 x x P100L,T100
293 920 920 format(F6.0,3F6.2,1P4D16.8)
294
295C If the record does not have the correct FeH and Alpha, we skip it
296 if((DABS(FeH-ALATM_FeH).gt.1D-5) .or.
297 x x (DABS(Alpha-ALATM_Alpha).gt.0D0)) then
298 goto 440 ! On to next record
299 endif
300
301 do i = 1,nTeff ! Skip out when we find the matching Teff
302 if (DABS(Teff-Teffs(i)) .lt.1D-6) then
303 I1 = i
304 goto 420
305 endif
306 enddo
307 write(*,*) 'ALFilein: Impossible failure #1'
308 Write(*,*) 'Teff of ', Teff, ' not in Teff table.'
309 goto 9999 ! The error exit
310
311 420 420 CONTINUE
312 do j = 1,nGL ! Skip out when we find the a matching GL
313 if (DABS(GL-GLs(j)) .lt.1D-6) then
314 j1 = j
315 goto 430
316 endif
317 enddo
318 write(*,*) 'ALFilein: Impossible failure #2'
319 Write(*,*) 'GL of ', GL, ' not in GL table.'
320 goto 9999 ! The error exit
321
322 430 430 CONTINUE
323
324C we now verify that we have the correct FeH and Alpha
325
326 if (DABS(FeH -ALATM_FeH) .ge. 1D-6) goto 440 ! Wrong FeH, bypass item
327 if (DABS(Alpha -ALATM_Alpha) .ge. 1D-6) goto 440 ! Wrong Alpha, bypass item
328
329
330C We now have the correct indices for our tables, i1 for the Teff-direction
331C and j1 for the GL direction
332
333 TEFFLs(I1) = LOG10(Teff) ! We need log(teff), but up to now we've been using Teff
334 GLs(j1) = GL
335 PLs(i1,j1) = PL
336 P100Ls(i1,j1) = P100L
337 T100Ls(i1,j1) = T100
338
339 440 440 continue
340 goto 410 ! Back to process the next record
341
342 499 499 continue
343 write(*,*)
344 write(*,*)'AtTabInit File Read error'
345 write(*,*)
346 goto 9999
347
348 500 500 continue ! We heve finished with the input file and entered all inputs
349
350 CALL ALTABINIT ! Initialize Allard tables
351
352 return
353
354 9999 9999 continue ! THE ERROR EXIT
355 write(*,*)
356 write(*,*)
357 write(*,*) '**************** PROGRAM ALFilein TERMINATED ',
358 x x '@ 9999 **************'
359 write(*,*)
360 write(*,*)
361 write(*,*)
362 write(ISHORT,*)
363 write(ISHORT,*) '**************** PROGRAM ALFilein TERMINATED ',
364 x x '@ 9999 *************'
365 write(ISHORT,*)
366 write(ISHORT,*)
367C 10/30/26 MHP via Claude: STOP 3, the input-error code (issue 38).
368 STOP 3
369
370 end ! END OF ALINITTAB
371
372C ************* End of ALFilein *************************
373
374
375C Numerical Recipes Shell sort
376
377 SUBROUTINE sort_shell(nn,A)
378C 10/20/26 MHP via Claude
379C code changed to replace implicit typing with IMPLICIT NONE and
380C explicit declarations.
381 IMPLICIT NONE
382 REAL*8 A,V
383 DIMENSION A(nn)
384 INTEGER*4 nn,n,inc,i,j
385C Sorts an array A(1:nn) into ascending numerical order by Shell's method (diminishing
386C incremental sort) nn is input. A is replaced on output by its sorted arrangement
387
388 if (nn .eq. 1) return ! Exit if only one element
389 n=nn
390 inc=1
391 1 1 inc=3*inc+1 ! Determin starting increment
392 if (inc .le. n) goto 1
393 2 2 continue
394 inc=inc/3 ! Loop over the partial sorts
395 do i=inc+1,n
396 v=A(i) ! Outer loop of straight insertion
397 j=i
398 3 3 if (A(j-inc) .gt. v) then ! Inner loop of straight insertion
399 A(j)=A(j-inc)
400 j=j-inc
401 if (j .le. inc) goto 4
402 goto 3
403 endif
404 4 4 A(j)=v
405 enddo
406 if (inc .gt. 1) goto 2
407 return
408 END
409
410
411C ************* End of sort_shell *************************
412
1"""Python version of alfilein.f (YREC 7be4133): reads a table of Allard
2model atmospheres (the 1999 NextGen format or the newer "Allard" format)
3into ALATM01_MOD, then has ALTABINIT check it."""
4
5import math
6
7import numpy as np
8
9from pyrec import alatm01_mod
10from pyrec import iounits_mod
11from pyrec import nmlpar_mod
12from pyrec.altabinit import altabinit
13
14NTA = 250
15NGA = 25
16
17# SAVE: locals kept between calls.
18teffs = np.zeros(NTA + 1)
19
20
21class _ReadError(Exception):
22 """A Fortran READ that would take its ERR= branch."""
23
24
25def _fields(line, specs):
26 """Read a fixed-format record: specs is a list of (width, decimals).
27 A blank field reads as 0; a field without a decimal point or exponent
28 gets the format's implied decimals, as in Fortran; D exponents read as
29 E. A field that cannot be read raises _ReadError."""
30
31 out, pos = [], 0
32 for width, decimals in specs:
33 text = line[pos:pos + width].strip().upper().replace('D', 'E')
34 pos += width
35 if not text:
36 out.append(0.0)
37 continue
38 try:
39 value = float(text)
40 except ValueError:
41 raise _ReadError(text) from None
42 if '.' not in text and 'E' not in text:
43 value /= 10.0 ** decimals
44 out.append(value)
45 return out
46
47
48def _read(unit):
49 """One record; None at end of file (END=)."""
50
51 return iounits_mod.read(unit)
52
53
54def _error_exit():
55 """label 9999: the error exit."""
56
57 # Fortran lines 354–368.
58 msg = " **************** PROGRAM ALFilein TERMINATED @ 9999 "
59 print("")
60 print("")
61 print(msg + "**************")
62 print("")
63 print("")
64 print("")
65 w = iounits_mod.write
66 w(iounits_mod.ishort, "")
67 w(iounits_mod.ishort, msg + "*************")
68 w(iounits_mod.ishort, "")
69 w(iounits_mod.ishort, "")
70 raise SystemExit(3) # STOP 3, the input-error code
71
72
73def _differs(feh, alpha):
74 """The passes' test for a record to skip: [Fe/H] off by more than 1e-5,
75 or alpha off at all."""
76
77 n = nmlpar_mod
78 # Fortran lines 209–210 (and 226–227, 296–297). Suspected bug (see the
79 # page): the second pass tests [Fe/H] again with 1e-6.
80 return abs(feh - n.alatm_feh) > 1e-5 or abs(alpha - n.alatm_alpha) > 0.0
81
82
83def _read_old(ioatma):
84 """The old 1999 NextGen format (label 100): fixed sizes, log P only."""
85
86 # Fortran lines 146–180.
87 a = alatm01_mod
88 ishort = iounits_mod.ishort
89 a.loldnextgen = True
90 iounits_mod.write(ishort, " ALFileIn: File Description: 1999 NEXTGEN"
91 " (Old NEXTGEN)")
92 # Ensure that PL, TL at tau = 100 are not requested: they are not in
93 # the old NextGen files.
94 if nmlpar_mod.laltptau100:
95 text = (" ALFileIN: Invalid old Allard input file for requested"
96 " PT,TL at Tau=100")
97 print("")
98 print(text)
99 iounits_mod.write(ishort, "")
100 iounits_mod.write(ishort, text)
101 _error_exit()
102 a.nteff = 54
103 a.ngl = 5
104 # FORMAT 905, (5F7.2/): the gravities, then one record skipped.
105 a.gls[1:a.ngl + 1] = _fields(_read(ioatma), [(7, 2)] * 5)
106 _read(ioatma)
107 # FORMAT 907, (10(1P5E16.8,/),1P4E16.8,/): the 54 temperatures, as
108 # given (already log Teff in the files).
109 values = []
110 for _ in range(10):
111 values += _fields(_read(ioatma), [(16, 8)] * 5)
112 values += _fields(_read(ioatma), [(16, 8)] * 4)
113 a.teffls[1:a.nteff + 1] = values
114 _read(ioatma)
115 # FORMAT 909, (1P5E16.8): one record of nGL values per Teff.
116 for j in range(1, a.nteff + 1):
117 a.pls[j, 1:a.ngl + 1] = _fields(_read(ioatma), [(16, 8)] * a.ngl)
118 iounits_mod.close(ioatma)
119
120
121def _first_pass(ioatma):
122 """Pass 1 of the new format (labels 299 to 316): collect the distinct
123 Teff, log g, [Fe/H] and alpha of the records with the requested [Fe/H]
124 and alpha, up to the end of the file. Raises _ReadError on a bad
125 record."""
126
127 a = alatm01_mod
128 spec915 = [(6, 0), (6, 2), (6, 2), (6, 2)] # FORMAT 915, (F6.0,3F6.2)
129 # label 299: up to the first acceptable record (Fortran lines 204–220).
130 while True:
131 rec = _read(ioatma)
132 if rec is None:
133 return
134 teff, gl, feh, alpha = _fields(rec, spec915)
135 if not _differs(feh, alpha):
136 break
137 a.nteff = 1
138 a.ngl = 1
139 a.nfeh = 1
140 a.nalpha = 1
141 teffs[a.nteff] = teff
142 a.gls[a.ngl] = gl
143 a.fehs[a.nfeh] = feh
144 a.alphas[a.nalpha] = alpha
145
146 # label 300: the rest of the file (Fortran lines 222–264).
147 while True:
148 rec = _read(ioatma)
149 if rec is None:
150 return
151 teff, gl, feh, alpha = _fields(rec, spec915)
152 if _differs(feh, alpha):
153 continue # label 316
154 if not any(abs(teff - teffs[i]) < 1e-6
155 for i in range(1, a.nteff + 1)):
156 a.nteff += 1
157 teffs[a.nteff] = teff
158 # label 310
159 if not any(abs(gl - a.gls[i]) < 1e-6 for i in range(1, a.ngl + 1)):
160 a.ngl += 1
161 a.gls[a.ngl] = gl
162 # label 312. Suspected bug (see the page): the loop runs to nGL, not
163 # nFeH.
164 if not any(abs(feh - a.fehs[i]) < 1e-6 for i in range(1, a.ngl + 1)):
165 a.nfeh += 1
166 a.fehs[a.nfeh] = feh
167 # label 314. Suspected bug (see the page): to nGL, not nALPHA.
168 if not any(abs(alpha - a.alphas[i]) < 1e-6
169 for i in range(1, a.ngl + 1)):
170 a.nalpha += 1
171 a.alphas[a.nalpha] = alpha
172 # label 316
173
174
175def _second_pass(ioatma, nhdr):
176 """Pass 2 of the new format (labels 410 to 440): place each accepted
177 record's log P at Teff, log P and log T at tau = 100 at its indices.
178 Raises _ReadError on a bad record."""
179
180 a = alatm01_mod
181 n = nmlpar_mod
182
183 # Rewind and skip the header (Fortran lines 283–289).
184 iounits_mod.rewind(ioatma)
185 for _ in range(nhdr):
186 _read(ioatma)
187
188 # FORMAT 920, (F6.0,3F6.2,1P4D16.8) (Fortran lines 291–340).
189 spec920 = [(6, 0), (6, 2), (6, 2), (6, 2), (16, 8), (16, 8), (16, 8)]
190 while True: # label 410
191 rec = _read(ioatma)
192 if rec is None:
193 return
194 teff, gl, feh, alpha, pl, p100l, t100 = _fields(rec, spec920)
195 if _differs(feh, alpha):
196 continue # label 440
197 for i in range(1, a.nteff + 1):
198 if abs(teff - teffs[i]) < 1e-6:
199 i1 = i
200 break
201 else:
202 print(" ALFilein: Impossible failure #1")
203 print(f" Teff of {teff} not in Teff table.")
204 _error_exit()
205 # label 420
206 for j in range(1, a.ngl + 1):
207 if abs(gl - a.gls[j]) < 1e-6:
208 j1 = j
209 break
210 else:
211 print(" ALFilein: Impossible failure #2")
212 print(f" GL of {gl} not in GL table.")
213 _error_exit()
214 # label 430: wrong [Fe/H] or alpha, bypass the record. Suspected bug
215 # (see the page): 1e-6 here, 1e-5 in the test above.
216 if abs(feh - n.alatm_feh) >= 1e-6:
217 continue
218 if abs(alpha - n.alatm_alpha) >= 1e-6:
219 continue
220 a.teffls[i1] = math.log10(teff) # log Teff from here on
221 a.gls[j1] = gl
222 a.pls[i1, j1] = pl
223 a.p100ls[i1, j1] = p100l
224 a.t100ls[i1, j1] = t100
225 # label 440
226
227
228def alfilein(fallard: str) -> None:
229 """Read the Allard atmosphere file FALLARD (old or new format) on unit
230 IOATMA, keep the records with the requested [Fe/H] and alpha
231 (NMLPAR_MOD), fill ALATM01_MOD and call ALTABINIT. Entries absent from
232 the file keep -999. Stops the run on any error.
233
234 Translates: alfilein.f#ALFILEIN (7be4133)
235
236 Argument: the file name fallard. Nothing is returned.
237
238 """
239
240 a = alatm01_mod
241 ioatma = iounits_mod.ioatma
242 ishort = iounits_mod.ishort
243
244 # Every entry starts invalid (-999) (Fortran lines 110–119).
245 for i in range(1, NTA + 1):
246 a.teffls[i] = -999.0
247 for j in range(1, NGA + 1):
248 a.gls[j] = -999.0
249 a.pls[i, j] = -999.0
250 a.p100ls[i, j] = -999.0
251 a.t100ls[i, j] = -999.0
252
253 # Open the file (Fortran lines 120–129).
254 try:
255 iounits_mod.open(ioatma, fallard, status='OLD')
256 except OSError:
257 # label 899
258 print("")
259 print(" ALFILEIN: Allard File OPEN Failure")
260 print(f" Unit-Number, FIle-Name are: {ioatma} {fallard}")
261 _error_exit()
262
263 # label 900: the first record decides the format (FORMAT 901, (a),
264 # into a CHARACTER*35) (Fortran lines 131–143).
265 inrec = (_read(ioatma) or '')[:35].ljust(35)
266 iounits_mod.rewind(ioatma)
267 if inrec[3:7] == '3.50':
268 _read_old(ioatma) # old NextGen file type
269 elif inrec[3:9] == 'ALLARD':
270 # label 200: the new Allard file type (Fortran lines 184–200).
271 a.loldnextgen = False
272 # FORMAT 911, (i2,X,a): the number of header records to skip.
273 rec = _read(ioatma)
274 nhdr = int(rec[0:2])
275 inrec2 = rec[3:259].ljust(256)
276 iounits_mod.write(ishort, "ALFilein: New Allard Atm: File Description: "
277 + inrec2[0:47]) # 912
278 if nhdr > 1:
279 for _ in range(2, nhdr + 1):
280 _read(ioatma)
281 a.ngl = 0
282 a.nteff = 0
283 a.nfeh = 0
284 a.nalpha = 0
285
286 # Pass 1 (Fortran lines 202–271).
287 try:
288 _first_pass(ioatma)
289 except _ReadError:
290 # label 399
291 print("")
292 print(" AtTabInit Pass 1 File Error ")
293 print("")
294 _error_exit()
295
296 # label 400: sort the values found (Fortran lines 273–281).
297 sort_shell(a.nteff, teffs)
298 sort_shell(a.ngl, a.gls)
299 sort_shell(a.nfeh, a.fehs)
300 sort_shell(a.nalpha, a.alphas)
301
302 # Pass 2 (Fortran lines 283–346).
303 try:
304 _second_pass(ioatma, nhdr)
305 except _ReadError:
306 # label 499
307 print("")
308 print(" AtTabInit File Read error")
309 print("")
310 _error_exit()
311 else:
312 for show in (lambda t: iounits_mod.write(ishort, t), print):
313 show(" *** Invalid Allard Atmosphere input file ***")
314 show(f" First record was '{inrec}'")
315 _error_exit()
316
317 # label 500 (Fortran lines 348–352).
318 altabinit()
319
320
321def sort_shell(nn: int, a: np.ndarray) -> None:
322 """Shell's sort (Numerical Recipes), ascending, in place, on a[1..nn],
323 with the increments 1, 4, 13, 40, ...
324
325 Translates: alfilein.f#SORT_SHELL (7be4133)
326
327 Arguments: the count nn and the array a (changed in place). Nothing is
328 returned.
329
330 """
331
332 # Fortran lines 388–407.
333 if nn == 1:
334 return
335 n = nn
336 inc = 1
337 while True: # label 1: the starting increment
338 inc = 3 * inc + 1
339 if inc > n:
340 break
341 while True: # label 2: loop over the partial sorts
342 inc = inc // 3
343 for i in range(inc + 1, n + 1):
344 v = a[i] # straight insertion
345 j = i
346 while a[j - inc] > v: # label 3
347 a[j] = a[j - inc]
348 j = j - inc
349 if j <= inc:
350 break
351 a[j] = v # label 4
352 if inc <= 1:
353 break