1** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2* This subroutine interpolates the electron thermal conductivity *
3* from the data file "condall.d" *
4* --------------------------------------------------- Version 23.05.99
5 subroutine CondOpacP(Zion,TLG,RLG,CK,DRK,DTK)
6C 10/14/26 MHP via Claude
7C code changed to convert COMMON/IOUNITS/ into IOUNITS_MOD.
8 USE IOUNITS_MOD
9C 10/20/26 MHP via Claude
10C code changed to replace implicit typing with IMPLICIT NONE and
11C explicit declarations.
12 implicit none
13 real*8 akap,ar,at,az,ck,ckt0,ckt0z0,ckt0z1,ckt1,ckt1z0,ckt1z1,
14 * * cktm,cktmz0,cktmz1,cktp,cktpz0,cktpz1,dr2kt0,dr2kt0z0,
15 * * dr2kt0z1,dr2kt1,dr2kt1z0,dr2kt1z1,dr2ktm,dr2ktmz0,dr2ktmz1,
16 * * dr2ktp,dr2ktpz0,dr2ktpz1,drk,drkt0,drkt0z0,drkt0z1,drkt1,
17 * * drkt1z0,drkt1z1,drktm,drktmz0,drktmz1,drktp,drktpz0,
18 * * drktpz1,drt2k,drtk,dt2k,dtk,rlg,tlg,xr,xt,xz0,xz1,z,zion,
19 * * zlg
20 integer ip,ir,irm,irp,it,itm,itp,iz,krun,maxr,maxt,maxz
21* Input: Zion - ion charge, TLG - lg(T[K]), RLG - lg(rho[g/cc])
22* Output: CK - Log_{10} thermal conductivity (kappa) [CGS units]
23* DRK - d log kappa / d log rho
24* DTK - d log kappa / d log T
25*** (it is also possible to obtain all second derivatives) ***
26
27C This Subroutine interpolates thermal conductivity from
28C a precalculated table condall.d available at
29C http://www.ioffe.rssi.ru/astro/conduct
30C For theoretical base of this calculation, see
31C A.Y.Potekhin, D.A.Baiko, P.Haensel, D.G.Yakovlev, 1999
32C Astron. Astrophys. 346, 345.
33C Extension from strongly- to weakly-degenerate regime
34C has been done using the thermal averaging - for example
35C A.Y.Potekhin, 1999, Astron. Astrophys. 351, 787.
36C Please quote these publication when using this program
37C SOURCE DATA FILE 'condall.d' MUST BE IN YOUR
38C CURRENT DIRECTORY
39C Please address any questions/comments to Alexander Potekhin:
40C e-mail: palex@astro.ioffe.rssi.ru
41
42 save
43 parameter (MAXT=19,MAXR=64,MAXZ=15)
44!!! NB: These parameters must be consistent with the table "condall.d"!!!
45 dimension AT(MAXT),AR(MAXR),AZ(MAXZ),AKAP(MAXT,MAXR,MAXZ)
46
47C The following three lines provide and interface to PARMIN in order to
48C locate the Potekhin files.
49C MHP 8/25 Removed character file names from common block
50C 9/24/26 MHP via Claude
51C code changed to merge COMMON /MISCOPAC/ into COMMON /NEWOPAC/
52C (this file has no IMPLICIT LOGICAL*4(L), so the merged-in L-prefixed
53C members need an explicit LOGICAL*4 declaration, matching how they
54C are implicitly typed everywhere else this block is declared)
55C 9/25/26 MHP via Claude
56C code changed to consolidate all I/O logical unit numbers, previously
57C scattered across COMMON/LUOUT/, COMMON/LUNUM/, COMMON/ALEX06/,
58C COMMON/ACDPTH/, COMMON/NEWOPAC/, COMMON/LOPAL95/, COMMON/ATMOS2/,
59C COMMON/ALATM03/, COMMON/OPALEOS/, and COMMON/SCV2/, into a single
60C COMMON/IOUNITS/
61C 10/13/26 MHP via Claude: removed the unused COMMON/NMLPAR/ declaration
62C (no member referenced here; residue of the obsolete-option removals).
63C 10/13/26 MHP via Claude: removed the unused COMMON/NEWOPAC/ declaration
64C (no member referenced here; residue of the obsolete-option removals).
65 LOGICAL*4 LALEX06,LOPAL95,L2Z,LLAOL,LPUREZ,LcondOpacP
66
67C removed unused variables
68C CHARACTER*256 FKUR2,FcondOpacP
69
70 data KRUN/-1/
71
72 if (KRUN.ne.12345) then ! Reading
73 IP = IcondOpacP
74C MHP 8/25 file opening moved to parmin
75C open(IP,file=FcondOpacP,status='OLD')
76c print*,'Reading thermal conductivity data...'
77 read(IP,'(A)') ! skip the first line
78 do IZ=1,MAXZ
79 read(IP,*) Z,(AT(IT),IT=1,MAXT)
80 AZ(IZ)=dlog10(Z)
81 do IR=1,MAXR
82 read(IP,*) AR(IR),(AKAP(IT,IR,IZ),IT=1,MAXT)
83 enddo
84 enddo
85 close(IP)
86 KRUN=12345
87C KC 2025-05-30 fixed -Winteger-division
88C IZ=MAXZ/2+1
89C IT=MAXT/2+1
90C IR=MAXR/2+1
91 IZ=INT(MAXZ/2.)+1
92 IT=INT(MAXT/2.)+1
93 IR=INT(MAXR/2.)+1
94c print*,'Potekhin Conductivity File read in.'
95 endif
96 ZLG=dlog10(Zion)
97 call HUNT(AZ,MAXZ,ZLG,IZ)
98 if (IZ.eq.0.or.IZ.eq.MAXZ) stop'CONINTER: Z out of range'
99 call HUNT(AT,MAXT,TLG,IT)
100
101C if (IT.eq.0.or.IT.eq.MAXT) stop'CONINTER: T out of range'
102 if (IT.eq.0.or.IT.eq.MAXT) then
103 print*, AZ
104 print*, MAXZ, TLG, IZ
105 stop'CONINTER: T out of range'
106 endif
107
108 call HUNT(AR,MAXR,RLG,IR)
109 if (IR.eq.0.or.IR.eq.MAXR) stop'CONINTER: rho out of range'
110 ITM=max0(1,IT-1)
111 ITP=min0(MAXT,IT+2)
112 IRM=max0(1,IR-1)
113 IRP=min0(MAXR,IR+2)
114* Cubic interpolation in RLG:
115* Z0:
116 call CINTERP3(AR(IRM),AR(IR),AR(IR+1),AR(IRP),RLG,IR,MAXR,
117 * * AKAP(ITM,IRM,IZ),AKAP(ITM,IR,IZ),
118 * * AKAP(ITM,IR+1,IZ),AKAP(ITM,IRP,IZ),
119 * * CKTMZ0,DRKTMZ0,DR2KTMZ0,XR)
120 call CINTERP3(AR(IRM),AR(IR),AR(IR+1),AR(IRP),RLG,IR,MAXR,
121 * * AKAP(IT,IRM,IZ),AKAP(IT,IR,IZ),
122 * * AKAP(IT,IR+1,IZ),AKAP(IT,IRP,IZ),
123 * * CKT0Z0,DRKT0Z0,DR2KT0Z0,XR)
124 call CINTERP3(AR(IRM),AR(IR),AR(IR+1),AR(IRP),RLG,IR,MAXR,
125 * * AKAP(IT+1,IRM,IZ),AKAP(IT+1,IR,IZ),
126 * * AKAP(IT+1,IR+1,IZ),AKAP(IT+1,IRP,IZ),
127 * * CKT1Z0,DRKT1Z0,DR2KT1Z0,XR)
128 call CINTERP3(AR(IRM),AR(IR),AR(IR+1),AR(IRP),RLG,IR,MAXR,
129 * * AKAP(ITP,IRM,IZ),AKAP(ITP,IR,IZ),
130 * * AKAP(ITP,IR+1,IZ),AKAP(ITP,IRP,IZ),
131 * * CKTPZ0,DRKTPZ0,DR2KTPZ0,XR)
132* Z1:
133 call CINTERP3(AR(IRM),AR(IR),AR(IR+1),AR(IRP),RLG,IR,MAXR,
134 * * AKAP(ITM,IRM,IZ+1),AKAP(ITM,IR,IZ+1),
135 * * AKAP(ITM,IR+1,IZ+1),AKAP(ITM,IRP,IZ+1),
136 * * CKTMZ1,DRKTMZ1,DR2KTMZ1,XR)
137 call CINTERP3(AR(IRM),AR(IR),AR(IR+1),AR(IRP),RLG,IR,MAXR,
138 * * AKAP(IT,IRM,IZ+1),AKAP(IT,IR,IZ+1),
139 * * AKAP(IT,IR+1,IZ+1),AKAP(IT,IRP,IZ+1),
140 * * CKT0Z1,DRKT0Z1,DR2KT0Z1,XR)
141 call CINTERP3(AR(IRM),AR(IR),AR(IR+1),AR(IRP),RLG,IR,MAXR,
142 * * AKAP(IT+1,IRM,IZ+1),AKAP(IT+1,IR,IZ+1),
143 * * AKAP(IT+1,IR+1,IZ+1),AKAP(IT+1,IRP,IZ+1),
144 * * CKT1Z1,DRKT1Z1,DR2KT1Z1,XR)
145 call CINTERP3(AR(IRM),AR(IR),AR(IR+1),AR(IRP),RLG,IR,MAXR,
146 * * AKAP(ITP,IRM,IZ+1),AKAP(ITP,IR,IZ+1),
147 * * AKAP(ITP,IR+1,IZ+1),AKAP(ITP,IRP,IZ+1),
148 * * CKTPZ1,DRKTPZ1,DR2KTPZ1,XR)
149* Linear interpolation in ZLG:
150 XZ1=(ZLG-AZ(IZ))/(AZ(IZ+1)-AZ(IZ))
151 XZ0=1D0-XZ1
152 CKTM=XZ0*CKTMZ0+XZ1*CKTMZ1
153 DRKTM=XZ0*DRKTMZ0+XZ1*DRKTMZ1
154 DR2KTM=XZ0*DR2KTMZ0+XZ1*DR2KTMZ1
155 CKT0=XZ0*CKT0Z0+XZ1*CKT0Z1
156 DRKT0=XZ0*DRKT0Z0+XZ1*DRKT0Z1
157 DR2KT0=XZ0*DR2KT0Z0+XZ1*DR2KT0Z1
158 CKT1=XZ0*CKT1Z0+XZ1*CKT1Z1
159 DRKT1=XZ0*DRKT1Z0+XZ1*DRKT1Z1
160 DR2KT1=XZ0*DR2KT1Z0+XZ1*DR2KT1Z1
161 CKTP=XZ0*CKTPZ0+XZ1*CKTPZ1
162 DRKTP=XZ0*DRKTPZ0+XZ1*DRKTPZ1
163 DR2KTP=XZ0*DR2KTPZ0+XZ1*DR2KTPZ1
164* Cubic interpolation in TLG:
165 call CINTERP3(AT(ITM),AT(IT),AT(IT+1),AT(ITP),TLG,IT,MAXT,
166 * * CKTM,CKT0,CKT1,CKTP, ! input: values of lg kappa
167 * * CK,DTK,DT2K,XT) ! lg kappa, d lg k / d lg T, d2 lg k / d2 lg T
168 call CINTERP3(AT(ITM),AT(IT),AT(IT+1),AT(ITP),TLG,IT,MAXT,
169 * * DRKTM,DRKT0,DRKT1,DRKTP, ! input: values of d lg k / d lg rho
170 * * DRK,DRTK,DRT2K,XT) ! d lg k / d lg rho, d2 lgk/(d lgT d lg rho)
171 return
172 end
173
174 subroutine CINTERP3(ZM,Z0,Z1,ZP,Z,N0,MXNV,VM,V0,V1,VP,
175 & & VF,DF,D2,XH)
176C 10/20/26 MHP via Claude
177C code changed to replace implicit typing with IMPLICIT NONE and
178C explicit declarations.
179 implicit none
180 real*8 c2,c3,d2,df,h,hm,hp,v0,v01,v1,v11,vf,vm,vp,x,xh,z,z0,z1,zm,
181 * * zp
182 integer mxnv,n0
183* Given 4 values of Z and 4 values of V, find VF corresponding to 5th Z
184* Version 23.05.99
185* Output: VF - interpolated value of function
186* DF - interpolated derivative
187* D2 - interpolated second derivative
188* XH - fraction of the path from N0 to N0+1
189 if (N0.le.0.or.N0.ge.MXNV) stop'CINTERP: N0 out of range'
190 X=Z-Z0
191 H=Z1-Z0 ! basic interval
192 XH=X/H
193 if (N0.gt.1) then
194 HM=Z0-ZM ! left adjoint interval
195 V01=((V1-V0)/H**2+(V0-VM)/HM**2)/
196 / / (1D0/H+1D0/HM) ! left derivative
197 endif
198 if (N0.lt.MXNV-1) then
199 HP=ZP-Z1 ! right adjoint interval
200 V11=((V1-V0)/H**2+(VP-V1)/HP**2)/
201 / / (1D0/H+1D0/HP) ! right derivative
202 endif
203 if (N0.gt.1.and.N0.lt.MXNV-1) then ! Cubic interpolation
204 C2=3D0*(V1-V0)-H*(V11+2D0*V01)
205 C3=H*(V01+V11)-2D0*(V1-V0)
206 VF=V0+V01*X+C2*XH**2+C3*XH**3
207 DF=V01+(2D0*C2*XH+3D0*C3*XH**2)/H
208 D2=(2D0*C2+6D0*C3*XH)/H**2
209 goto 10
210 endif
211 if (N0.eq.1) then ! Quadratic interpolation
212 C2=V0-V1+V11*H
213 VF=V1-V11*(H-X)+C2*(1D0-XH)**2
214 DF=V11-2D0*C2*(1D0-XH)/H
215 D2=2D0*C2/H**2
216 else ! N0=MXNV-1
217 C2=V1-V0-V01*H
218 VF=V0+V01*X+C2*XH**2
219 DF=V01+2D0*C2*XH/H
220 D2=2D0*C2/H**2
221 endif
222 10 10 return
223 end
224
225 subroutine HUNT(XX,N,X,JLO)
226C 10/20/26 MHP via Claude
227C code changed to replace implicit typing with IMPLICIT NONE and
228C explicit declarations.
229 implicit none
230 real*8 x,xx
231 integer inc,jhi,jlo,jm,n
232* W.H.Press, B.P.Flannery, S.A.Teukolsky, W.T.Vetterling
233* Numerical Receipes(Cambridge Univ., 1986)
234* Given an array XX of length N, and given a value X,
235* returns a value JLO such that X is between XX(JLO) and XX(JLO+1).
236* XX must be monotonic, either increasing or decreasing.
237* JLO=0 or JLO=N is returned to indicate that X is out of range.
238* JLO on input is taken as the initial guess for JLO on output.
239 dimension XX(*)
240 logical ASCND
241 ASCND=XX(N).gt.XX(1) ! true if ascending order, false otherwise
242 if (JLO.le.0.or.JLO.gt.N) then ! Input guess not useful.
243 JLO=0
244 JHI=N+1
245 goto 3 ! go immediately to bisection
246 endif
247 INC=1 ! set the hunting increment
248 if (X.ge.XX(JLO).eqv.ASCND) then ! Hunt up:
249 1 1 JHI=JLO+INC
250 if (JHI.gt.N) then ! Done hunting, since off end of table
251 JHI=N+1
252 elseif (X.ge.XX(JHI).eqv.ASCND) then ! Not done hunting
253 JLO=JHI
254 INC=INC+INC
255 goto 1
256 endif
257 else ! Hunt down:
258 JHI=JLO
259 2 2 JLO=JHI-INC
260 if (JLO.lt.1) then ! Done hunting, since off end of table
261 JLO=0
262 elseif (X.lt.XX(JLO).eqv.ASCND) then ! Not done hunting
263 JHI=JLO
264 INC=INC+INC ! so double the increment
265 goto 2 ! and try again
266 endif ! Done hunting, value bracketed
267 endif
268* Hunt is done, so begin the final bisection phase:
269 3 3 if (JHI-JLO.eq.1) return
270 JM=(JHI+JLO)/2
271 if (X.ge.XX(JM).eqv.ASCND) then
272 JLO=JM
273 else
274 JHI=JM
275 endif
276 goto 3
277 end1"""Python version of condopacp.f (YREC 7be4133): the electron thermal
2conductivity interpolated in A. Y. Potekhin's table (condall.d)."""
3
4import math
5
6import numpy as np
7
8from pyrec import iounits_mod
9
10# The table's dimensions; they must match the file.
11MAXT = 19
12MAXR = 64
13MAXZ = 15
14
15# Locals kept between calls (SAVE, DATA): the table, read at the first
16# call, and the last indices found, HUNT's first guesses at the next call.
17at = np.zeros(MAXT + 1)
18ar = np.zeros(MAXR + 1)
19az = np.zeros(MAXZ + 1)
20akap = np.zeros((MAXT + 1, MAXR + 1, MAXZ + 1))
21krun = -1
22iz = 0
23it = 0
24ir = 0
25
26
27def _read_values(unit, n):
28 """A list-directed READ of n numbers: records are read until n values
29 are found."""
30
31 vals = []
32 while len(vals) < n:
33 rec = iounits_mod.read(unit)
34 vals.extend(float(v.replace('D', 'E').replace('d', 'e'))
35 for v in rec.replace(',', ' ').split())
36 return vals[:n]
37
38
39def condopacp(zion: float, tlg: float, rlg: float, ck: float, drk: float, dtk: float) -> tuple[float, float, float]:
40 """Interpolate log10 of the electron thermal conductivity, and its
41 derivatives in log rho and log T, in Potekhin's table: cubic in
42 log rho and log T, linear in log Z_ion. Reads the table at the first
43 call.
44
45 Translates: condopacp.f#CONDOPACP (7be4133)
46
47 Arguments: zion, the ion charge; tlg, log T (K); rlg, log rho
48 (g/cm^3); ck, drk, dtk, passed in (with any value).
49
50 Returns (ck, drk, dtk): log10 K (cgs), d log K / d log rho and
51 d log K / d log T.
52
53 """
54
55 global krun, iz, it, ir
56
57 # Read the table at the first call; the file is opened by PARMIN (Fortran lines 72–86).
58 if krun != 12345:
59 ip = iounits_mod.icondopacp
60 iounits_mod.read(ip) # skip the first line
61 for iz in range(1, MAXZ + 1):
62 vals = _read_values(ip, 1 + MAXT)
63 z = vals[0]
64 for it in range(1, MAXT + 1):
65 at[it] = vals[it]
66 az[iz] = math.log10(z)
67 for ir in range(1, MAXR + 1):
68 vals = _read_values(ip, 1 + MAXT)
69 ar[ir] = vals[0]
70 for it in range(1, MAXT + 1):
71 akap[it, ir, iz] = vals[it]
72 iounits_mod.close(ip)
73 krun = 12345
74
75 # The middle of the table, HUNT's first guesses: INT(MAXZ/2.)+1, the same as
76 # MAXZ/2+1, KC 2025-05-30 (Fortran lines 87–93).
77 iz = int(MAXZ / 2.0) + 1
78 it = int(MAXT / 2.0) + 1
79 ir = int(MAXR / 2.0) + 1
80
81 # The charge in its grid (Fortran lines 96–98).
82 zlg = math.log10(zion)
83 iz = hunt(az, MAXZ, zlg, iz)
84 if iz == 0 or iz == MAXZ:
85 raise SystemExit('CONINTER: Z out of range')
86
87 # The temperature in its grid (Fortran lines 99–106).
88 it = hunt(at, MAXT, tlg, it)
89 # Suspected bug (see the page): a point exactly at the last grid value
90 # (log T = 9) also stops here.
91 if it == 0 or it == MAXT:
92 # A debugging leftover: the Z grid is printed before stopping.
93 print(*az[1:])
94 print(MAXZ, tlg, iz)
95 raise SystemExit('CONINTER: T out of range')
96
97 # The density in its grid (Fortran lines 108–109).
98 ir = hunt(ar, MAXR, rlg, ir)
99 # Suspected bug (see the page): a point exactly at the last grid value
100 # (log rho = 9.75) also stops here.
101 if ir == 0 or ir == MAXR:
102 raise SystemExit('CONINTER: rho out of range')
103
104 # The neighbors used for the cubics (Fortran lines 110–113).
105 itm = max(1, it - 1)
106 itp = min(MAXT, it + 2)
107 irm = max(1, ir - 1)
108 irp = min(MAXR, ir + 2)
109
110 # Cubic interpolation in log rho, at the 4 temperatures ITM, IT,
111 # IT+1, ITP and the 2 charges IZ, IZ+1 (Fortran lines 114–148).
112 def in_rho(jt, jz):
113 vf, df, d2, xr = cinterp3(ar[irm], ar[ir], ar[ir + 1], ar[irp],
114 rlg, ir, MAXR, akap[jt, irm, jz],
115 akap[jt, ir, jz], akap[jt, ir + 1, jz],
116 akap[jt, irp, jz], 0.0, 0.0, 0.0, 0.0)
117 return vf, df, d2
118
119 cktmz0, drktmz0, dr2ktmz0 = in_rho(itm, iz)
120 ckt0z0, drkt0z0, dr2kt0z0 = in_rho(it, iz)
121 ckt1z0, drkt1z0, dr2kt1z0 = in_rho(it + 1, iz)
122 cktpz0, drktpz0, dr2ktpz0 = in_rho(itp, iz)
123 cktmz1, drktmz1, dr2ktmz1 = in_rho(itm, iz + 1)
124 ckt0z1, drkt0z1, dr2kt0z1 = in_rho(it, iz + 1)
125 ckt1z1, drkt1z1, dr2kt1z1 = in_rho(it + 1, iz + 1)
126 cktpz1, drktpz1, dr2ktpz1 = in_rho(itp, iz + 1)
127
128 # Linear interpolation in log Z (Fortran lines 149–163).
129 xz1 = (zlg - az[iz]) / (az[iz + 1] - az[iz])
130 xz0 = 1.0 - xz1
131 cktm = xz0 * cktmz0 + xz1 * cktmz1
132 drktm = xz0 * drktmz0 + xz1 * drktmz1
133 dr2ktm = xz0 * dr2ktmz0 + xz1 * dr2ktmz1 # noqa: F841 (unused in the Fortran too)
134 ckt0 = xz0 * ckt0z0 + xz1 * ckt0z1
135 drkt0 = xz0 * drkt0z0 + xz1 * drkt0z1
136 dr2kt0 = xz0 * dr2kt0z0 + xz1 * dr2kt0z1 # noqa: F841 (unused in the Fortran too)
137 ckt1 = xz0 * ckt1z0 + xz1 * ckt1z1
138 drkt1 = xz0 * drkt1z0 + xz1 * drkt1z1
139 dr2kt1 = xz0 * dr2kt1z0 + xz1 * dr2kt1z1 # noqa: F841 (unused in the Fortran too)
140 cktp = xz0 * cktpz0 + xz1 * cktpz1
141 drktp = xz0 * drktpz0 + xz1 * drktpz1
142 dr2ktp = xz0 * dr2ktpz0 + xz1 * dr2ktpz1 # noqa: F841 (unused in the Fortran too)
143
144 # Cubic interpolation in log T: of log K (giving d log K / d log T),
145 # and of d log K / d log rho (Fortran lines 164–170).
146 ck, dtk, dt2k, xt = cinterp3(at[itm], at[it], at[it + 1], at[itp], tlg,
147 it, MAXT, cktm, ckt0, ckt1, cktp,
148 0.0, 0.0, 0.0, 0.0)
149 drk, drtk, drt2k, xt = cinterp3(at[itm], at[it], at[it + 1], at[itp],
150 tlg, it, MAXT, drktm, drkt0, drkt1,
151 drktp, 0.0, 0.0, 0.0, 0.0)
152 return ck, drk, dtk
153
154
155def cinterp3(zm: float, z0: float, z1: float, zp: float, z: float, n0: int, mxnv: int, vm: float, v0: float, v1: float,
156 vp: float, vf: float, df: float, d2: float, xh: float) -> tuple[float, float, float, float]:
157 """Interpolate at z on [z0, z1] from 4 tabulated points: a cubic
158 Hermite polynomial with end slopes from the neighboring secants, and
159 quadratic on the table's first and last intervals.
160
161 Translates: condopacp.f#CINTERP3 (7be4133)
162
163 Arguments: zm, z0, z1, zp and vm, v0, v1, vp, the 4 points; z; n0, the
164 index of z0 in the table; mxnv, the table's length; vf, df, d2, xh,
165 passed in (with any value).
166
167 Returns (vf, df, d2, xh): the value, the first and the second
168 derivative, and the fraction of the way from z0 to z1.
169
170 """
171 # The interval checked (Fortran line 189).
172 if n0 <= 0 or n0 >= mxnv:
173 raise SystemExit('CINTERP: N0 out of range')
174
175 # The position in the interval (Fortran lines 190–192).
176 x = z - z0
177 h = z1 - z0 # the basic interval
178 xh = x / h
179
180 # The slopes at the two ends, where there is a neighbor (Fortran lines 193–202).
181 if n0 > 1:
182 hm = z0 - zm # the left adjoining interval
183 v01 = (((v1 - v0) / h ** 2 + (v0 - vm) / hm ** 2)
184 / (1.0 / h + 1.0 / hm)) # the left derivative
185 if n0 < mxnv - 1:
186 hp = zp - z1 # the right adjoining interval
187 v11 = (((v1 - v0) / h ** 2 + (vp - v1) / hp ** 2)
188 / (1.0 / h + 1.0 / hp)) # the right derivative
189
190 if n0 > 1 and n0 < mxnv - 1:
191 # Cubic interpolation (Fortran lines 203–210).
192 c2 = 3.0 * (v1 - v0) - h * (v11 + 2.0 * v01)
193 c3 = h * (v01 + v11) - 2.0 * (v1 - v0)
194 vf = v0 + v01 * x + c2 * xh ** 2 + c3 * xh ** 3
195 df = v01 + (2.0 * c2 * xh + 3.0 * c3 * xh ** 2) / h
196 d2 = (2.0 * c2 + 6.0 * c3 * xh) / h ** 2
197 return vf, df, d2, xh # GOTO 10
198
199 if n0 == 1:
200 # Quadratic interpolation, first interval (Fortran lines 211–215).
201 c2 = v0 - v1 + v11 * h
202 vf = v1 - v11 * (h - x) + c2 * (1.0 - xh) ** 2
203 df = v11 - 2.0 * c2 * (1.0 - xh) / h
204 d2 = 2.0 * c2 / h ** 2
205 else:
206 # Quadratic interpolation, last interval, N0 = MXNV-1 (Fortran lines 216–221).
207 c2 = v1 - v0 - v01 * h
208 vf = v0 + v01 * x + c2 * xh ** 2
209 df = v01 + 2.0 * c2 * xh / h
210 d2 = 2.0 * c2 / h ** 2
211 return vf, df, d2, xh
212
213
214def hunt(xx: np.ndarray, n: int, x: float, jlo: int) -> int:
215 """Numerical Recipes' HUNT: find jlo with x between xx[jlo] and
216 xx[jlo+1] in a monotonic table, starting from the guess jlo and
217 doubling the step before bisecting.
218
219 Translates: condopacp.f#HUNT (7be4133)
220
221 Arguments: xx[1..n], the table; n; x; jlo, the first guess.
222
223 Returns jlo: 0 or n outside the table (n also for x equal to xx[n]).
224
225 """
226 # The table's order (Fortran line 241).
227 ascnd = xx[n] > xx[1] # True if ascending
228
229 if jlo <= 0 or jlo > n:
230 # The guess is not useful: go to bisection (Fortran lines 242–246).
231 jlo = 0
232 jhi = n + 1
233 else:
234 inc = 1
235 if (x >= xx[jlo]) == ascnd:
236 # Hunt up, label 1 (Fortran lines 247–256).
237 while True:
238 jhi = jlo + inc
239 if jhi > n:
240 jhi = n + 1 # off the end of the table
241 break
242 elif (x >= xx[jhi]) == ascnd:
243 jlo = jhi
244 inc = inc + inc
245 continue
246 break
247 else:
248 # Hunt down, label 2 (Fortran lines 257–267).
249 jhi = jlo
250 while True:
251 jlo = jhi - inc
252 if jlo < 1:
253 jlo = 0 # off the end of the table
254 break
255 elif (x < xx[jlo]) == ascnd:
256 jhi = jlo
257 inc = inc + inc
258 continue
259 break
260
261 # Bisection, label 3 (Fortran lines 268–276).
262 while jhi - jlo != 1:
263 jm = (jhi + jlo) // 2
264 if (x >= xx[jm]) == ascnd:
265 jlo = jm
266 else:
267 jhi = jm
268 return jlo