lightweaver 0.16.1__cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. lightweaver/Data/AbundancesAsplund09.pickle +0 -0
  2. lightweaver/Data/AtomicMassesNames.pickle +0 -0
  3. lightweaver/Data/Barklem_dfdata.dat +41 -0
  4. lightweaver/Data/Barklem_pddata.dat +40 -0
  5. lightweaver/Data/Barklem_spdata.dat +46 -0
  6. lightweaver/Data/DefaultMolecules/C2.molecule +27 -0
  7. lightweaver/Data/DefaultMolecules/CH/CH_X-A.asc +46409 -0
  8. lightweaver/Data/DefaultMolecules/CH/CH_X-A_12.asc +28322 -0
  9. lightweaver/Data/DefaultMolecules/CH/CH_X-B.asc +4272 -0
  10. lightweaver/Data/DefaultMolecules/CH/CH_X-B_12.asc +2583 -0
  11. lightweaver/Data/DefaultMolecules/CH/CH_X-C.asc +20916 -0
  12. lightweaver/Data/DefaultMolecules/CH/CH_X-C_12.asc +13106 -0
  13. lightweaver/Data/DefaultMolecules/CH.molecule +35 -0
  14. lightweaver/Data/DefaultMolecules/CN.molecule +30 -0
  15. lightweaver/Data/DefaultMolecules/CO/vmax=3_Jmax=49_dv=1_26 +296 -0
  16. lightweaver/Data/DefaultMolecules/CO/vmax=9_Jmax=120_dv=1_26 +2162 -0
  17. lightweaver/Data/DefaultMolecules/CO.molecule +30 -0
  18. lightweaver/Data/DefaultMolecules/CO_NLTE.molecule +29 -0
  19. lightweaver/Data/DefaultMolecules/CaH.molecule +29 -0
  20. lightweaver/Data/DefaultMolecules/H2+.molecule +27 -0
  21. lightweaver/Data/DefaultMolecules/H2.molecule +27 -0
  22. lightweaver/Data/DefaultMolecules/H2O.molecule +27 -0
  23. lightweaver/Data/DefaultMolecules/HF.molecule +29 -0
  24. lightweaver/Data/DefaultMolecules/LiH.molecule +27 -0
  25. lightweaver/Data/DefaultMolecules/MgH.molecule +34 -0
  26. lightweaver/Data/DefaultMolecules/N2.molecule +28 -0
  27. lightweaver/Data/DefaultMolecules/NH.molecule +27 -0
  28. lightweaver/Data/DefaultMolecules/NO.molecule +27 -0
  29. lightweaver/Data/DefaultMolecules/O2.molecule +27 -0
  30. lightweaver/Data/DefaultMolecules/OH.molecule +27 -0
  31. lightweaver/Data/DefaultMolecules/SiO.molecule +26 -0
  32. lightweaver/Data/DefaultMolecules/TiO.molecule +30 -0
  33. lightweaver/Data/Quadratures.pickle +0 -0
  34. lightweaver/Data/pf_Kurucz.input +0 -0
  35. lightweaver/DefaultIterSchemes/.placeholder +0 -0
  36. lightweaver/DefaultIterSchemes/SimdImpl_AVX2FMA.cpython-312-x86_64-linux-gnu.so +0 -0
  37. lightweaver/DefaultIterSchemes/SimdImpl_AVX512.cpython-312-x86_64-linux-gnu.so +0 -0
  38. lightweaver/DefaultIterSchemes/SimdImpl_SSE2.cpython-312-x86_64-linux-gnu.so +0 -0
  39. lightweaver/LwCompiled.cpython-312-x86_64-linux-gnu.so +0 -0
  40. lightweaver/__init__.py +33 -0
  41. lightweaver/atmosphere.py +1767 -0
  42. lightweaver/atomic_model.py +852 -0
  43. lightweaver/atomic_set.py +1286 -0
  44. lightweaver/atomic_table.py +653 -0
  45. lightweaver/barklem.py +151 -0
  46. lightweaver/benchmark.py +113 -0
  47. lightweaver/broadening.py +605 -0
  48. lightweaver/collisional_rates.py +337 -0
  49. lightweaver/config.py +106 -0
  50. lightweaver/constants.py +22 -0
  51. lightweaver/crtaf.py +197 -0
  52. lightweaver/fal.py +440 -0
  53. lightweaver/iterate_ctx.py +241 -0
  54. lightweaver/iteration_update.py +134 -0
  55. lightweaver/libenkiTS.so +0 -0
  56. lightweaver/molecule.py +225 -0
  57. lightweaver/multi.py +113 -0
  58. lightweaver/nr_update.py +106 -0
  59. lightweaver/rh_atoms.py +19743 -0
  60. lightweaver/simd_management.py +42 -0
  61. lightweaver/utils.py +509 -0
  62. lightweaver/version.py +34 -0
  63. lightweaver/wittmann.py +1375 -0
  64. lightweaver/zeeman.py +157 -0
  65. lightweaver-0.16.1.dist-info/METADATA +81 -0
  66. lightweaver-0.16.1.dist-info/RECORD +69 -0
  67. lightweaver-0.16.1.dist-info/WHEEL +6 -0
  68. lightweaver-0.16.1.dist-info/licenses/LICENSE +21 -0
  69. lightweaver-0.16.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1375 @@
1
+ """
2
+ Simple EOS and background opacity package
3
+ Coded in python by J. de la Cruz Rodriguez (ISP-SU 2017)
4
+ """
5
+ import numpy as np
6
+ try:
7
+ import xdrlib
8
+ except ImportError:
9
+ import mda_xdrlib.xdrlib as xdrlib
10
+ from numba import njit
11
+ from .utils import get_data_path
12
+ from dataclasses import dataclass
13
+ from typing import List, Optional, Iterable
14
+ from numba.typed import List as NList
15
+ from math import log, exp, floor, sqrt
16
+
17
+ # Some definitions (from NIST)
18
+
19
+ BK = 1.3806488E-16 # Boltzmann [erg K]
20
+ EE = 4.80320441E-10 # Electron charge
21
+ HH = 6.62606957E-27 # Planck [erg s]
22
+ PI = 3.14159265358979323846 # PI number
23
+ CC = 2.99792458E10 # Speed of light [cm/s]
24
+ AMU = 1.660538921E-24 # Atomic mass unit
25
+ EV = 1.602176565E-12 # Electron Volt to erg
26
+ CM1_TO_EV = HH*CC/EV # CM^-1 to eV
27
+ ME = 9.10938188E-28 # mass of electron
28
+
29
+ @dataclass
30
+ class CgsConstants:
31
+ BK: float = BK # Boltzmann [erg K]
32
+ EE: float = EE # Electron charge
33
+ HH: float = HH # Planck [erg s]
34
+ PI: float = PI # PI number
35
+ CC: float = CC # Speed of light [cm/s]
36
+ AMU: float = AMU # Atomic mass unit
37
+ EV: float = EV # Electron Volt to erg
38
+ CM1_TO_EV: float = CM1_TO_EV # CM^-1 to eV
39
+ ME: float = ME # mass of electron
40
+ cgs = CgsConstants()
41
+
42
+ SahaFac = ((2.0 * PI * ME * BK) / (HH*HH))**1.5
43
+
44
+ # Default abundances
45
+
46
+ defaultAbundances = 10.0**np.float64( [-0.04048,-1.07,-10.95,-10.89, -9.44, -3.48, -3.99,
47
+ -3.11, -7.48, -3.95, -5.71, -4.46, -5.57, -4.49, -6.59, -4.83,
48
+ -6.54, -5.48, -6.82, -5.68, -8.94, -7.05, -8.04, -6.37, -6.65,
49
+ -4.50, -7.12, -5.79, -7.83, -7.44, -9.16, -8.63, -9.67, -8.69,
50
+ -9.41, -8.81, -9.44, -9.14, -9.80, -9.54,-10.62,-10.12,-20.00,
51
+ -10.20,-10.92,-10.35, -11.10,-10.18,-10.58,-10.04,-11.04, -9.80,
52
+ -10.53, -9.81,-10.92, -9.91,-10.82,-10.49,-11.33,-10.54,-20.00,
53
+ -11.04,-11.53,-10.92, -11.94,-10.94,-11.78,-11.11,-12.04,-10.96,
54
+ -11.28,-11.16,-11.91, -10.93,-11.77,-10.59,-10.69,-10.24,-11.03,
55
+ -10.95,-11.14,-10.19, -11.33,-20.00,-20.00,-20.00,-20.00,-20.00,
56
+ -20.00,-11.92,-20.00, -12.51,-20.00,-20.00,-20.00,-20.00,-20.00,
57
+ -20.00,-20.00])
58
+
59
+
60
+ aMass = np.float64([1.008, 4.003, 6.941, 9.012, 10.811, 12.011, 14.007, 15.999,
61
+ 18.998, 20.179, 22.990, 24.305, 26.982, 28.086, 30.974, 32.060,
62
+ 35.453, 39.948, 39.102, 40.080, 44.956, 47.900, 50.941, 51.996,
63
+ 54.938, 55.847, 58.933, 58.710, 63.546, 65.370, 69.720, 72.590,
64
+ 74.922, 78.960, 79.904, 83.800, 85.468, 87.620, 88.906, 91.220,
65
+ 92.906, 95.940, 98.906,101.070,102.905,106.400,107.868,112.400,
66
+ 114.820,118.690,121.750,127.600,126.905,131.300,132.905,137.340,
67
+ 138.906,140.120,140.908,144.240,146.000,150.400,151.960,157.250,
68
+ 158.925,162.500,164.930,167.260,168.934,170.040,174.970,178.490,
69
+ 180.948,183.850,186.200,190.200,192.200,195.090,196.967,200.590,
70
+ 204.370,207.190,208.981,210.000,210.000,222.000,223.000,226.025,
71
+ 227.000,232.038,230.040,238.029,237.048,242.000,242.000,245.000,
72
+ 248.000,252.000,253.000])
73
+
74
+ # Energy of a 6 level H atom in erg and corresponding g-values
75
+
76
+ hEnergy = np.float64((0.0, 82258.211, 97491.219, 102822.76, 105290.508, 109677.617)) * HH * CC
77
+ hStatg = np.float64((2.0, 8.0, 18.0, 32.0, 50.0, 1.0))
78
+
79
+ @njit(cache=True)
80
+ def clip_single(x, a, b):
81
+ # NOTE(cmo): There seems to be a bug in numba where np.clip doesn't work on
82
+ # single numbers (non-array x) currently
83
+ if x < a:
84
+ return a
85
+ elif x > b:
86
+ return b
87
+ return x
88
+
89
+
90
+ @njit(cache=True)
91
+ def clip_abs(x, a, b):
92
+ return np.copysign(clip_single(np.abs(x), a, b), x)
93
+
94
+
95
+ @dataclass
96
+ class PfData: # Just a container for the PF
97
+ pf: NList[np.ndarray]
98
+ eion: NList[np.ndarray]
99
+ Tpf: np.ndarray
100
+
101
+ def __getstate__(self):
102
+ return {
103
+ 'pf': list(self.pf),
104
+ 'eion': list(self.eion),
105
+ 'Tpf': list(self.Tpf)
106
+ }
107
+
108
+ def __setstate__(self, s):
109
+ self.pf = NList(s['pf'])
110
+ self.eion = NList(s['eion'])
111
+ self.Tpf = s['Tpf']
112
+
113
+
114
+
115
+ @njit(cache=True)
116
+ def nsaha(t, xne, u0, u1, eion):
117
+ # operates with t, xne and eion in EV
118
+ return 2.0 * SahaFac * (u1 / u0) * t**1.5 * np.exp(-eion * EV / (t * BK)) / xne
119
+
120
+
121
+ @njit(cache=True)
122
+ def saha(theta, eion, u1, u2, pe):
123
+ # Operates with theta, eion in eV and pe
124
+ return u2*np.exp(2.302585093*(9.0804625434325867-theta*eion))/(u1*pe*theta**2.5)
125
+
126
+
127
+ @njit(cache=True)
128
+ def init_pe_from_pg(hAbund, t, pg):
129
+ # Assume that only H is a electron donnor
130
+ nu = hAbund
131
+ saha = 10.0**( -0.4771+2.5*np.log10(t)-np.log10(pg)-(13.6*5040.0/t))
132
+ aaa = 1.0 + saha
133
+ bbb = -(nu-1.0) * saha
134
+ ccc = -saha*nu
135
+ ybh=(-bbb+np.sqrt(bbb*bbb-4.*aaa*ccc))/(2.*aaa)
136
+ pe=pg*ybh/(1.+ybh)
137
+
138
+ return pe
139
+
140
+
141
+ @njit(cache=True)
142
+ def pe_from_pg_impl(t, pg, abunds, eion, pf, Tpf,
143
+ verbose=False, prec=1e-5, Ncontr=28):
144
+
145
+ dif = 1.1
146
+ pe = init_pe_from_pg(abunds[0], t, pg) # Init Pe assuming only H and increase it 10%
147
+ ope = pe
148
+ it = 0
149
+
150
+ while np.abs(dif) > prec and it < 250:
151
+ pe = (ope + pe)*0.5
152
+ ope = pe
153
+
154
+ pe, fe = pe_pg(t, pe, pg, abunds, eion, pf, Tpf)
155
+ dif = 2.0 * np.abs(pe-ope) / (pe+ope)
156
+ it += 1
157
+
158
+ if(verbose):
159
+ print("Wittmann: pe_from_pg: convergence Niter = ", (it-1))
160
+
161
+ return pe, fe
162
+
163
+
164
+ @njit(cache=True)
165
+ def molecb(X):
166
+ Y = np.empty(2)
167
+ dy = np.empty(2)
168
+ Y[0]=-11.206998+X*(2.7942767+X*(7.9196803E-2-X*2.4790744E-2)) # H2
169
+ Y[1]=-12.533505+X*(4.9251644+X*(-5.6191273E-2+X*3.2687661E-3)) # H
170
+ dx=(-X*X)/5040.
171
+
172
+ dy[0]=dx*(2.7942767+X*(2*7.9196803e-2-X*3*2.4790744e-2))
173
+ dy[1]=dx*(4.9251644+X*(-2*5.6191273e-2-X*3*3.2687661e-3))
174
+
175
+ return Y, dy
176
+
177
+
178
+ @njit(cache=True)
179
+ def partition_f(n, t, eion, pf, Tpf, only=0):
180
+ """
181
+ Computes the partition function of element "n" for a given temperature
182
+
183
+ input:
184
+ n: Atomic number of the atom -1 (H is 0).
185
+ t: Temperature in K.
186
+ Keywords:
187
+ only: (=int) only return this number first levels of the atom.
188
+ The default is to return all the levels
189
+
190
+
191
+ """
192
+ nn = pf[n].shape[0]
193
+
194
+ if only != 0:
195
+ nn = min(nn, only)
196
+
197
+ res = np.zeros(nn)
198
+
199
+ for ii in range(nn):
200
+ res[ii] = np.interp(t, Tpf, pf[n][ii])
201
+
202
+ return res
203
+
204
+
205
+ @njit(cache=True)
206
+ def pe_pg(t, pe, pgas, abunds, eion, pf, Tpf, verbose=False, prec=1e-5, Ncontr=28):
207
+ g1 = 0.0
208
+ theta = 5040.0/t
209
+
210
+
211
+ # Init some values
212
+ if(pe < 0.0):
213
+ pe = 1.e-15
214
+ g4 = 0.0
215
+ g5 = 0.0
216
+ else:
217
+ cmol, dcmol = molecb(theta)
218
+ cmol = np.clip(cmol, -30, 30)
219
+
220
+ g4 = pe * 10.0**cmol[0]
221
+ g5 = pe * 10.0**cmol[1]
222
+
223
+ #
224
+ # H first
225
+ #
226
+ u = partition_f(0, t, eion, pf, Tpf, only=3)
227
+ g2 = saha(theta, eion[0][0], u[0], u[1], pe) # p(h+)/p(h)
228
+ g3 = saha(theta, 0.754, 1.0, u[0], pe) # p(h)/p(h-)
229
+ g3 = 1.0 / clip_single(g3, 1.e-30, 1.0e30)
230
+
231
+ #
232
+ # Now count electrons contributed by the first
233
+ # two ionized stages of all contributing atoms
234
+ #
235
+ for ii in range(1, Ncontr):
236
+ alfai = abunds[ii] / abunds[0] # relative to H abundance
237
+ u = partition_f(ii, t, eion, pf, Tpf, only=3)
238
+
239
+ a = saha(theta, eion[ii][0], u[0], u[1], pe)
240
+ b = saha(theta, eion[ii][1], u[1], u[2], pe)
241
+
242
+ c = 1.+a*(1.+b)
243
+ g1 += alfai/c*a*(1.+2.*b)
244
+
245
+
246
+ # All the math...
247
+
248
+ a=1.+g2+g3
249
+ b=2.*(1.+g2/g5*g4)
250
+ c=g5
251
+ d=g2-g3
252
+ e=g2/g5*g4
253
+
254
+ a = clip_abs(a, 1.e-15, 1.e15);
255
+ d = clip_abs(d, 1.e-15, 1.e15);
256
+
257
+ c1=c*b*b+a*d*b-e*a*a
258
+ c2=2.0*a*e-d*b+a*b*g1
259
+ c3=-(e+b*g1)
260
+ f1=0.5*c2/c1
261
+ f1=-f1 + np.copysign(1.0,c1)* np.sqrt(f1*f1-c3/c1)
262
+ f5=(1.-a*f1)/b
263
+ f4=e*f5
264
+ f3=g3*f1
265
+ f2=g2*f1
266
+ fe = clip_single(f2-f3+f4+g1, 1.e-30, 1.e30)
267
+ phtot = pe/fe
268
+
269
+ # Refinement by Wittmann, not sure this is needed in
270
+ # double prec.
271
+
272
+ if(f5 <= 1.e-4):
273
+ diff = 1.0; const6=g5/pe*f1*f1; const7=f2-f3+g1; it = 0
274
+
275
+ while((diff > 1.e-5) and (it < 5)):
276
+ of5 = f5
277
+ f5=phtot*const6
278
+ f4=e*f5
279
+ fe=const7+f4
280
+ phtot=pe/fe
281
+ diff = 0.5 * np.abs(f5-of5) / (f5 + of5)
282
+ it += 1
283
+
284
+ # Recompute pe
285
+
286
+ abOthers = (1.0 - abunds[0]) / abunds[0]
287
+ pe = pgas / (1.+(f1+f2+f3+f4+f5+abOthers)/fe)
288
+
289
+ if(pe <= 0.0):
290
+ pe = 1.e-15
291
+
292
+ return pe, fe
293
+
294
+
295
+ @njit(cache=True)
296
+ def pe_from_rho_impl(t, xna, abunds, eion, pf, Tpf,
297
+ verbose=False, prec=1e-5, Ncontr=28):
298
+ # xna: fraction of atoms
299
+ BKT = BK * t
300
+
301
+ # now estimate Pgas and iterate
302
+
303
+ if(t > 8000):
304
+ a = 0.5;
305
+ elif(t > 4000):
306
+ a = 0.1;
307
+ elif(t > 2000):
308
+ a = 0.01;
309
+ else:
310
+ a = 0.001;
311
+
312
+ xne = a * xna /(1.0 - a)
313
+ Pgas = (xna+xne)*BKT
314
+
315
+ # Iterate
316
+
317
+ it = 0
318
+ dif = 1.0
319
+
320
+ while (it < 250) and (np.abs(dif) > prec):
321
+ Pe, _ = pe_from_pg_impl(t, Pgas, abunds, eion, pf, Tpf,
322
+ verbose=verbose, prec=prec, Ncontr=Ncontr)
323
+ xna_guessed = (Pgas-Pe)/BKT
324
+ dif = np.abs(xna-xna_guessed)/xna
325
+
326
+ Pgas *= xna/xna_guessed
327
+
328
+ return Pe#, Pgas
329
+
330
+
331
+ @njit(cache=True)
332
+ def gasc(t, pe, abunds, eion, pf, Tpf, verbose=False, prec=1e-5, Ncontr=28):
333
+
334
+ pp = np.zeros(Ncontr+6)
335
+
336
+ theta = 5040. / t
337
+ cmol, dmol = molecb(theta)
338
+
339
+ g4 = 10.0**cmol[0]; g5 = 10.0**cmol[1]
340
+
341
+ # First H
342
+ u = partition_f(0, t, eion, pf, Tpf)
343
+ g2 = saha(theta, eion[0][0], u[0], u[1], pe) # p(h+)/p(h)
344
+ g3 = 1.0 / saha(theta, 0.754, 1.0, u[0], pe) # p(h)/p(h-)
345
+ g1 = 0.0
346
+
347
+
348
+ # Other contributions
349
+ for ii in range(1, Ncontr):
350
+ alfai = abunds[ii] / abunds[0] # relative to H abundance
351
+ u = partition_f(ii, t, eion, pf, Tpf, only=3)
352
+
353
+ a = saha(theta, eion[ii][0], u[0], u[1], pe)
354
+ b = saha(theta, eion[ii][1], u[1], u[2], pe)
355
+
356
+ c=1.+a*(1.+b) # fraction of n0/ntot
357
+
358
+ pp[ii]=alfai/c # abund /ntot (partial pressure of neutral species ii)
359
+
360
+ # 1*n1 + 2*n2 + ... j*n_j so we count how many electrons
361
+ # come from each ionized species
362
+
363
+ g1 += pp[ii]*a*(1.+2.*b)
364
+
365
+
366
+ a=1.+g2+g3
367
+ e=g2/g5*g4
368
+ b=2.0*(1.0 + e)
369
+ c=g5
370
+ d=g2-g3
371
+ c1=c*b*b+a*d*b-e*a*a
372
+ c2=2.*a*e-d*b+a*b*g1
373
+ c3=-(e+b*g1)
374
+ f1=0.5*c2/c1
375
+ f1=-f1+ np.copysign(1.0,c1) * np.sqrt(f1*f1-c3/c1)
376
+ f5=(1.0-a*f1)/b
377
+
378
+ f4=e*f5
379
+ f3=g3*f1
380
+ f2=g2*f1
381
+ fe=f2-f3+f4+g1
382
+ phtot=pe/fe
383
+
384
+ # Refinement by Wittmann
385
+ if(f5 <= 1.e-5):
386
+ diff = 1.0; const6=g5/pe*f1*f1; const7=f2-f3+g1; it = 0
387
+
388
+ while (diff > 1.e-5) and (it < 5):
389
+ of5 = f5
390
+ f5=phtot*const6
391
+ f4=e*f5
392
+ fe=const7+f4
393
+ phtot=pe/fe
394
+ diff = 0.5 * abs(f5-of5) / (f5 + of5)
395
+ it += 1
396
+
397
+ abOthers = (1.0 - abunds[0]) / abunds[0]
398
+ pg=pe*(1.0+(f1+f2+f3+f4+f5+abOthers)/fe)
399
+
400
+ # Store the partial pressures of H too
401
+
402
+ pp[Ncontr+0] = f1 # p(h)/p(h')
403
+ pp[Ncontr+1] = f2 # p(h+)/p(h')
404
+ pp[Ncontr+2] = f5 # p(h2)/p(h')
405
+ pp[Ncontr+3] = f3 # p(h-)/p(h')
406
+ pp[Ncontr+4] = phtot # p(h') (total hydrogen!)
407
+ pp[Ncontr+5] = fe # p(h') (total hydrogen!)
408
+
409
+
410
+ return pg, pp
411
+
412
+
413
+ @njit(cache=True)
414
+ def pg_from_pe_impl(t, pe, abunds, eion, pf, Tpf,
415
+ verbose=False, prec=1e-5, Ncontr=28):
416
+ pg, dum = gasc(t, pe, abunds, eion, pf, Tpf,
417
+ verbose=verbose, prec=prec, Ncontr=Ncontr)
418
+ return pg, dum[-1]
419
+
420
+
421
+ @njit(cache=True)
422
+ def rho_from_pe_impl(temp, pe, abunds, eion, pf, Tpf, rhoFromH,
423
+ verbose=False, prec=1e-5, Ncontr=28):
424
+ pg, fe_out = pg_from_pe_impl(temp, pe, abunds, eion, pf, Tpf,
425
+ verbose=verbose, prec=prec, Ncontr=Ncontr)
426
+ rho = pe * rhoFromH / (fe_out * temp)
427
+ return rho
428
+
429
+
430
+ @njit(cache=True)
431
+ def pg_from_rho_impl(temp, rho, xna, abunds, eion, pf, Tpf, rhoFromH,
432
+ verbose=False, prec=1e-5, Ncontr=28):
433
+ if temp > 8000:
434
+ a = 0.5;
435
+ elif temp > 4000:
436
+ a = 0.1;
437
+ elif temp > 2000:
438
+ a = 0.01;
439
+ else:
440
+ a = 0.001;
441
+
442
+ xne = a * xna /(1.0 - a)
443
+ pgas = (xna+xne)*BK*temp
444
+
445
+ Pe, _ = pe_from_pg_impl(temp, pgas, abunds, eion, pf, Tpf,
446
+ verbose=verbose, prec=prec, Ncontr=Ncontr)
447
+ irho = rho_from_pe_impl(temp, Pe, abunds, eion, pf, Tpf, rhoFromH,
448
+ verbose=verbose, prec=prec, Ncontr=Ncontr)
449
+
450
+ dif = 1.0
451
+ it = 0
452
+ while dif >= prec and it < 100:
453
+ Pe *= (1.0 + rho / irho)*0.5
454
+ irho = rho_from_pe_impl(temp, Pe, abunds, eion, pf, Tpf, rhoFromH,
455
+ verbose=verbose, prec=prec, Ncontr=Ncontr)
456
+ dif = np.abs((irho - rho) / (rho))
457
+ it += 1
458
+
459
+ pgas, _ = pg_from_pe_impl(temp, Pe, abunds, eion, pf, Tpf,
460
+ verbose=verbose, prec=prec, Ncontr=Ncontr)
461
+
462
+ return pgas
463
+
464
+
465
+ @njit(cache=True)
466
+ def Boltzmann(t, u, glow, e_pot):
467
+ # Gives the ratio between the total population of a ionization stage and a given level
468
+ # The energy levels must be in Erg.
469
+ return (glow / u) * np.exp(-(e_pot) / (BK*t))
470
+
471
+
472
+ @njit(cache=True)
473
+ def get_X_parts(iatom, t, pg, pe, abunds, eion, pf, Tpf,
474
+ divide_by_u=False, only=0):
475
+
476
+ # Precompute partial densities of atoms, electrons
477
+ # and partial density of iatom with the abundance
478
+
479
+ TBK = t * BK
480
+ xna = (pg-pe) / TBK
481
+ xne = pe / TBK
482
+ n_tot = xna * abunds[iatom] # / self.abtot = 1.0
483
+
484
+ # Partition function
485
+
486
+ u = partition_f(iatom, t, eion, pf, Tpf, only=only)
487
+ nLev = u.size
488
+ # Solve saha and get the partial density of each ionized stage
489
+
490
+ xpa = np.empty(nLev)
491
+ xpa[0] = 1.0
492
+
493
+ for ii in range(1, nLev):
494
+ xpa[ii] = nsaha(t, xne, u[ii-1], u[ii], eion[iatom][ii-1])
495
+
496
+ for ii in range(nLev-1, 0, -1):
497
+ xpa[0] = 1.0 + xpa[0]*xpa[ii]
498
+
499
+ xpa[0] = 1.0 / xpa[0]
500
+
501
+ for ii in range(1, nLev):
502
+ xpa[ii] *= xpa[ii-1]
503
+
504
+ # Now that we have the ratios between ionized stages, multiply by the partial
505
+ # density of iatom. Divide by the partition function if needed
506
+
507
+ if(divide_by_u):
508
+ xpa[:] *= n_tot / u[:]
509
+ else:
510
+ xpa *= n_tot
511
+
512
+ return xpa, u
513
+
514
+
515
+ class Wittmann:
516
+ """
517
+ EOS described in Mihalas (1970) "Stellar atmospheres"
518
+ It considers in detail H, H+, H- and H2, no other molecules.
519
+
520
+ This particular implementation was taken from Wittmann's routines.
521
+ Partition functions from Kurucz.
522
+
523
+ Coded in python by J. de la Cruz Rodriguez (ISP-SU 2017)
524
+ Messed with by C. Osborne (University of Glasgow 2019-2020)
525
+
526
+ Don't blame Jaime for EOS bugs in Lightweaver!
527
+ **N.B. Everything in here is CGS.**
528
+
529
+ Dependencies: pf_Kurucz.input containing the partition function data.
530
+
531
+ """
532
+
533
+ def __init__(self, abund_init: Optional[Iterable[float]]=None,
534
+ verbose: bool=False, prec: float=1.e-5,
535
+ pf_file: str='pf_Kurucz.input'):
536
+
537
+ self.verbose = verbose
538
+ self.prec = prec
539
+ self.Ncontr = 28 # number of species contributing as electron donors (sorted)
540
+
541
+ self.abund = defaultAbundances.copy()
542
+ if abund_init is not None:
543
+ # Replace default abundances
544
+ Nabund = len(abund_init)
545
+ self.abund[0:Nabund] = abund_init
546
+ if(self.verbose):
547
+ print('Wittmann: replacing default abundances with user provided values')
548
+
549
+ self.abundTot = self.abund.sum()
550
+ self.abund /= self.abundTot
551
+ self.abundTot = 1.0
552
+ self.abOthers = self.abund[1::].sum() / self.abund[0]
553
+
554
+ self.aveMass = (self.abund * aMass).sum()
555
+
556
+ self.massPerH = self.aveMass / aMass[0] / self.abund[0]
557
+ self.rhoFromH = self.massPerH * aMass[0] * AMU / BK
558
+
559
+ self.aveMass *= AMU
560
+
561
+ # init arrays for later
562
+ self.alfai = np.zeros(self.Ncontr)
563
+ self.chi1 = np.zeros(self.Ncontr)
564
+ self.chi2 = np.zeros(self.Ncontr)
565
+ self.u0 = np.zeros(self.Ncontr)
566
+ self.u1 = np.zeros(self.Ncontr)
567
+ self.u2 = np.zeros(self.Ncontr)
568
+
569
+ # Init PF
570
+
571
+ DATA_PATH = get_data_path() + pf_file
572
+ self.init_pf_data(DATA_PATH, True)
573
+
574
+
575
+ def init_pf_data(self, ifile, to_EV=True):
576
+ #
577
+ # Reads Kurucz's partition functions and ionization potentials from a file
578
+ # Taken from RH (Uitenbroek 2001)
579
+ #
580
+ # NOTE(cmo): There is probably a case that could speed this up further
581
+ # by pickling the default set of arguments into a preprepared data
582
+ # structure, if desirable
583
+
584
+ ff = open(ifile,'rb')
585
+ f = ff.read()
586
+ ff.close()
587
+
588
+ data = xdrlib.Unpacker(f)
589
+ npf = data.unpack_uint()
590
+
591
+ nelem = 99
592
+
593
+ tpf = np.float64(data.unpack_farray(npf, data.unpack_double))
594
+ pf = NList()
595
+ eion = NList()
596
+
597
+ for ii in range(nelem):
598
+ pti = data.unpack_uint()
599
+ nstage = data.unpack_uint()
600
+
601
+ pfElement = np.array(data.unpack_farray(npf*nstage,
602
+ data.unpack_double)
603
+ ).reshape((nstage, npf))
604
+ pf.append(pfElement)
605
+ eionElement = np.array(data.unpack_farray(nstage,
606
+ data.unpack_double)
607
+ ) * HH * CC
608
+ if to_EV:
609
+ eionElement /= EV
610
+ eion.append(eionElement)
611
+
612
+ self.pfData = PfData(pf, eion, tpf)
613
+
614
+
615
+ def pe_from_rho(self, t, rho):
616
+ # fraction of atoms
617
+ xna = rho / self.aveMass
618
+ return pe_from_rho_impl(t, xna, self.abund, self.pfData.eion,
619
+ self.pfData.pf, self.pfData.Tpf,
620
+ verbose=self.verbose, prec=self.prec,
621
+ Ncontr=self.Ncontr)
622
+
623
+
624
+ def pe_from_pg(self, t, pg, get_fe = False):
625
+ pe, fe = pe_from_pg_impl(t, pg, self.abund, self.pfData.eion,
626
+ self.pfData.pf, self.pfData.Tpf,
627
+ verbose=self.verbose, prec=self.prec,
628
+ Ncontr=self.Ncontr)
629
+
630
+ if(get_fe):
631
+ return pe, fe
632
+ return pe
633
+
634
+
635
+ def pg_from_rho(self, temp, rho):
636
+ xna = rho / self.aveMass
637
+ return pg_from_rho_impl(temp, rho, xna, self.abund, self.pfData.eion,
638
+ self.pfData.pf, self.pfData.Tpf, self.rhoFromH,
639
+ verbose=self.verbose, prec=self.prec,
640
+ Ncontr=self.Ncontr)
641
+
642
+ def pg_from_pe(self, t, pe, get_fe=False):
643
+ pe, fe = pg_from_pe_impl(t, pe, self.abund, self.pfData.eion,
644
+ self.pfData.pf, self.pfData.Tpf,
645
+ verbose=self.verbose, prec=self.prec,
646
+ Ncontr=self.Ncontr)
647
+ if get_fe:
648
+ return pe, fe
649
+ return pe
650
+
651
+
652
+ def rho_from_pe(self, temp, pe):
653
+ return rho_from_pe_impl(temp, pe, self.abund, self.pfData.eion,
654
+ self.pfData.pf, self.pfData.Tpf, self.rhoFromH,
655
+ verbose=self.verbose, prec=self.prec,
656
+ Ncontr=self.Ncontr)
657
+
658
+
659
+ def rho_from_pg(self, temp, pg):
660
+ pe, fe_out = pe_from_pg_impl(temp, pg, self.abund, self.pfData.eion,
661
+ self.pfData.pf, self.pfData.Tpf,
662
+ verbose=self.verbose, prec=self.prec,
663
+ Ncontr=self.Ncontr)
664
+ rho = pe * self.rhoFromH / (fe_out * temp)
665
+ return rho
666
+
667
+
668
+ def get_H6_pops(self, t, pgas, pe):
669
+
670
+ # Solve ionization for H
671
+ n, u = self.get_X_parts(0, t, pgas, pe, divide_by_u=False, return_u=True)
672
+
673
+ # Define output array
674
+ res = np.empty(6)
675
+ res[-1] = n[1] # number of protons
676
+
677
+ ratios = np.empty(5)
678
+ # Solve level populations for the 5 first levels of H
679
+ for ii in range(5):
680
+ ratios[ii] = Boltzmann(t, u[0], hStatg[ii], hEnergy[ii])
681
+
682
+ # particle conservation (with 6 levels in H this line has no effect)
683
+ #ratios /= ratios.sum()
684
+ # Multiply the neutral H population by the ratios of level populations
685
+ res[0:5] = n[0] * ratios
686
+
687
+ return res
688
+
689
+
690
+ def get_X_parts(self, iatom, t, pg, pe,
691
+ divide_by_u=False, only=0, return_u=False):
692
+
693
+ xpa, u = get_X_parts(iatom, t, pg, pe, self.abund, self.pfData.eion,
694
+ self.pfData.pf, self.pfData.Tpf,
695
+ divide_by_u=divide_by_u, only=only)
696
+
697
+ if(return_u):
698
+ return xpa, u
699
+ return xpa
700
+
701
+
702
+ def get_background_partials(self, t, pg, pe, divide_by_u=True):
703
+
704
+ n = np.empty(17)
705
+ tbk = t * BK
706
+
707
+ # --- He/He+/He++ ---
708
+ xpa = self.get_X_parts(1, t, pg, pe, divide_by_u=divide_by_u)
709
+ n[3] = xpa[0]
710
+ n[4] = xpa[1]
711
+ n[5] = xpa[2]
712
+
713
+ # --- C ---
714
+ xpa = self.get_X_parts(5, t, pg, pe, divide_by_u=divide_by_u)
715
+ n[6] = xpa[0]
716
+
717
+ # --- Al ---
718
+ xpa = self.get_X_parts(12, t, pg, pe, divide_by_u=divide_by_u)
719
+ n[7] = xpa[0]
720
+
721
+ # --- Si/Si+ ---
722
+ xpa = self.get_X_parts(13, t, pg, pe, divide_by_u=divide_by_u)
723
+ n[8] = xpa[0]
724
+ n[9] = xpa[1]
725
+
726
+ # --- Ca/Ca+ ---
727
+ xpa = self.get_X_parts(19, t, pg, pe, divide_by_u=divide_by_u)
728
+ n[10] = xpa[0]
729
+ n[11] = xpa[1]
730
+
731
+ # --- Mg/Mg+ ---
732
+ xpa = self.get_X_parts(11, t, pg, pe, divide_by_u=divide_by_u)
733
+ n[12] = xpa[0]
734
+ n[13] = xpa[1]
735
+
736
+ # --- Fe ---
737
+ xpa = self.get_X_parts(25, t, pg, pe, divide_by_u=divide_by_u)
738
+ n[14] = xpa[0]
739
+
740
+ # --- N ---
741
+ xpa = self.get_X_parts(6, t, pg, pe, divide_by_u=divide_by_u)
742
+ n[15] = xpa[0]
743
+
744
+ # --- O ---
745
+ xpa = self.get_X_parts(7, t, pg, pe, divide_by_u=divide_by_u)
746
+ n[16] = xpa[0]
747
+
748
+ # Get H- and other H densities
749
+ pfH = 0.5 if divide_by_u else 1.0
750
+
751
+ dum, pp = gasc(t, pe, self.abund, self.pfData.eion,
752
+ self.pfData.pf, self.pfData.Tpf,
753
+ verbose=self.verbose, prec=self.prec,
754
+ Ncontr=self.Ncontr)
755
+ n[0] = pp[self.Ncontr+0]*pp[self.Ncontr+4] / tbk * pfH # H / pf[H]
756
+ n[1] = pp[self.Ncontr+1]*pp[self.Ncontr+4] / tbk # H+/ 1.0
757
+ n[2] = pp[self.Ncontr+3]*pp[self.Ncontr+4] / tbk # H-/ 1.0
758
+ return n
759
+
760
+ def cont_opacity(self, iT, Pgas, Pe, w):
761
+
762
+ # Some definitions
763
+ TK = iT * BK
764
+ TKEV = TK / EV
765
+ HTK = HH / TK
766
+ TLOG = np.log(iT)
767
+ xna = (Pgas-Pe) / TK
768
+ xne = Pe / TK
769
+
770
+ # Partial densities of background absorvers,
771
+ # divided by the partition functions
772
+ n = self.get_background_partials(iT, Pgas, Pe, divide_by_u=True)
773
+
774
+ # get background opacity
775
+ opac, scat = cop(iT, TKEV, TK, HTK, TLOG, xna, xne, w, *n)
776
+ return opac
777
+
778
+
779
+
780
+ # ----------------------------------------------------------------------------------------
781
+ #
782
+ # All the following functions are used to compute background opacities.
783
+ # The main call is done to function "cop", that calls all the other auxiliary
784
+ # functions.
785
+ #
786
+ # ----------------------------------------------------------------------------------------
787
+
788
+ @njit(cache=True)
789
+ def SEATON(FREQ0, XSECT, POWER, A, FREQ):
790
+ return XSECT*(A+(1.-A)*(FREQ0/FREQ))* (FREQ0/FREQ)**(floor(2.*POWER+0.01)*0.5)
791
+
792
+ # ----------------------------------------------------------------------------------------
793
+
794
+ Z4LOG=np.float64((0.,1.20412,1.90849,2.40824,2.79588,3.11261))
795
+ A0 = np.float64((
796
+ 5.53,5.49,5.46,5.43,5.40,5.25,5.00,4.69,4.48,4.16,3.85,
797
+ 4.91,4.87,4.84,4.80,4.77,4.63,4.40,4.13,3.87,3.52,3.27,
798
+ 4.29,4.25,4.22,4.18,4.15,4.02,3.80,3.57,3.27,2.98,2.70,
799
+ 3.64,3.61,3.59,3.56,3.54,3.41,3.22,2.97,2.70,2.45,2.20,
800
+ 3.00,2.98,2.97,2.95,2.94,2.81,2.65,2.44,2.21,2.01,1.81,
801
+ 2.41,2.41,2.41,2.41,2.41,2.32,2.19,2.02,1.84,1.67,1.50,
802
+ 1.87,1.89,1.91,1.93,1.95,1.90,1.80,1.68,1.52,1.41,1.30,
803
+ 1.33,1.39,1.44,1.49,1.55,1.56,1.51,1.42,1.33,1.25,1.17,
804
+ 0.90,0.95,1.00,1.08,1.17,1.30,1.32,1.30,1.20,1.15,1.11,
805
+ 0.55,0.58,0.62,0.70,0.85,1.01,1.15,1.18,1.15,1.11,1.08,
806
+ 0.33,0.36,0.39,0.46,0.59,0.76,0.97,1.09,1.13,1.10,1.08,
807
+ 0.19,0.21,0.24,0.28,0.38,0.53,0.76,0.96,1.08,1.09,1.09)).reshape((12,11))
808
+
809
+
810
+ @njit(cache=True)
811
+ def COULFF(TLOG, FREQLG, NZ):
812
+ GAMLOG=10.39638-TLOG/1.15129+Z4LOG[NZ-1]
813
+ IGAM=min(int(GAMLOG+7.),10)
814
+ if(IGAM<1): IGAM=1
815
+
816
+ # HVKTLG=2*log10(HVKT) #
817
+
818
+ HVKTLG=(FREQLG-TLOG)/1.15129-20.63764
819
+ IHVKT=min(int(HVKTLG+9.),11)
820
+ if(IHVKT<1): IHVKT=1
821
+
822
+ P=GAMLOG-(IGAM-7);
823
+ Q=HVKTLG-(IHVKT-9);
824
+ return (1.-P)*((1.-Q)*A0[IHVKT-1,IGAM-1]+Q*A0[IHVKT, IGAM-1])+P*((1.-Q)*A0[IHVKT-1,IGAM]+Q*A0[IHVKT,IGAM])
825
+
826
+
827
+ # ----------------------------------------------------------------------------------------
828
+
829
+ A1 = np.float64((0.9916,1.105,1.101,1.101,1.102,1.0986))
830
+ B1 = np.float64((2.719e3,-2.375e4,-9.863e3,-5.765e3,-3.909e3,-2.704e3))
831
+ C1 = np.float64((-2.268e10,4.077e8,1.035e8,4.593e7,2.371e7,1.229e7))
832
+
833
+ @njit(cache=True)
834
+ def COULX(N, freq, Z):
835
+ n=(N+1.0)**2
836
+
837
+ if(freq>=(Z*Z*3.28805e15/n)):
838
+ FREQ1=freq*1.e-10
839
+ CLX=0.2815/FREQ1/FREQ1/FREQ1/n/n/(N+1.0)*Z*Z*Z*Z
840
+
841
+ if(N>=6):
842
+ return CLX
843
+
844
+ CLX*=(A1[N]+(B1[N]+C1[N]*(Z*Z/FREQ1))*(Z*Z/FREQ1))
845
+ return CLX
846
+
847
+ return 0.0
848
+
849
+ # ----------------------------------------------------------------------------------------
850
+
851
+ @njit(cache=True)
852
+ def HOP( XNE, XH1, XH2, FREQ, FREQLG, T, TLOG, TKEV, STIM, EHVKT):
853
+
854
+ CONT = np.empty(8)
855
+ BOLT = np.empty(8)
856
+
857
+ FREQ3=(FREQ*1.E-10)**3
858
+ CFREE=3.6919E-22/FREQ3
859
+
860
+ n1 = (np.arange(8)+1.0)**2
861
+ BOLT = np.exp(-13.595*(1.-1./n1)/TKEV)*2.*n1*XH1
862
+
863
+
864
+ FREET=XNE*CFREE*XH2/sqrt(T)
865
+ XR=XH1/13.595*TKEV
866
+ BOLTEX=exp(-13.427/TKEV)*XR
867
+ EXLIM=exp(-13.595/TKEV)*XR
868
+
869
+ for N in range(8):
870
+ CONT[N]=COULX(N,FREQ,1.0)
871
+
872
+ C=0.2815/FREQ3;
873
+ if(FREQ < 4.05933E13):
874
+ BOLTEX=EXLIM/EHVKT
875
+ H=(CONT[6]*BOLT[6]+CONT[7]*BOLT[7]+(BOLTEX-EXLIM)*C+
876
+ COULFF(TLOG,FREQLG,1)*FREET)*STIM
877
+
878
+
879
+ H += (CONT[0:6]*BOLT[0:6]).sum()*(1.-EHVKT)
880
+
881
+ return H
882
+
883
+ # ----------------------------------------------------------------------------------------
884
+
885
+ @njit(cache=True)
886
+ def HRAYOP(XH1, FREQ):
887
+
888
+ WAVE=min(FREQ,2.463e15)
889
+ WAVE=2.997925e18/WAVE
890
+ WW=WAVE*WAVE
891
+ WW2 = WW*WW
892
+ SIG=(5.799e-13+1.422e-6/WW+2.784/(WW2))/(WW2)
893
+
894
+ return SIG*XH1*2.0
895
+
896
+ # ----------------------------------------------------------------------------------------
897
+
898
+ @njit(cache=True)
899
+ def H2PLOP(XH1, XH2, FREQ, FREQLG, FREQ15, TKEV, STIM):
900
+
901
+ if(FREQ > 3.28805E15):
902
+ return 0.0
903
+
904
+ FR=-3.0233E3+(3.7797E2+(-1.82496E1+(3.9207E-1-3.1672E-3*FREQLG)*
905
+ FREQLG)*FREQLG)*FREQLG
906
+ ES=-7.342E-3+(-2.409+(1.028+(-0.4230+(0.1224-0.01351*FREQ15)*
907
+ FREQ15)*FREQ15)*FREQ15)*FREQ15
908
+ return exp(-ES/TKEV+FR)*2.*XH1*XH2*STIM
909
+
910
+
911
+ # ----------------------------------------------------------------------------------------
912
+
913
+ @njit(cache=True)
914
+ def HMINOP( XH1, XHMIN, FREQ, T, TKEV, XNE, EHVKT):
915
+
916
+ FREQ1=FREQ*1.E-10
917
+ B=(1.3727E-15+4.3748/FREQ)/FREQ1
918
+ C=-2.5993E-7/FREQ1**2
919
+
920
+ if(FREQ <= 1.8259E14): HMINBF=0.
921
+ elif(FREQ >= 2.111E14): HMINBF=6.801E-10+(5.358E-3+(1.481E3+(-5.519E7+4.808E11/FREQ1)/FREQ1)/FREQ1)/FREQ1
922
+ else: HMINBF=3.695E-6+(-1.251E-1+1.052E3/FREQ1)/FREQ1
923
+
924
+ HMINFF=(B+C/T)*XH1*XNE*2.E-20
925
+
926
+
927
+ #
928
+ # We use the number density / partition function for H-.
929
+ # The partition function for H- is 1
930
+ #
931
+ if(T < 7730.): HMIN=XHMIN
932
+ else: HMIN=exp(0.7552/TKEV)/(2.*2.4148E15*T*sqrt(T))*XH1*XNE
933
+
934
+ H=HMINBF*(1-EHVKT)*HMIN*1.E-10
935
+ return H+HMINFF
936
+
937
+ # ----------------------------------------------------------------------------------------
938
+
939
+ G0 = np.float64((1.,3.,1.,9.,3.,3.,1.,9.,20.,3.))
940
+ HEFREQ0 = np.float64((5.9452090e15,1.1528440e15,0.9803331e15,.8761076e15,
941
+ 0.8147100e15,0.4519048e15,0.4030971e15,.8321191e15,
942
+ 0.3660215e15,0.3627891e15))
943
+ CHI0 = np.float64((0.,19.819,20.615,20.964,21.217,22.718,22.920,23.006,
944
+ 23.073,23.086))
945
+
946
+ @njit(cache=True)
947
+ def HE1OP( XHE1, XHE2, XNE, FREQ, FREQLG, T, TKEV, TLOG, EHVKT, STIM):
948
+
949
+ TRANS = np.zeros(10)
950
+ BOLT=np.exp(-CHI0/TKEV)*G0*XHE1
951
+
952
+ FREET=XNE*1.E-10*XHE2*1.E-10/sqrt(T)*1.E-10
953
+ XRLOG=log(XHE1*(2./13.595)*TKEV)
954
+ BOLTEX=exp(-23.730/TKEV+XRLOG)
955
+ EXLIM=exp(-24.587/TKEV+XRLOG)
956
+ FREQ3=(FREQ*1.E-10)**3
957
+ CFREE=3.6919E8/FREQ3
958
+ C=2.815E-1/FREQ3
959
+
960
+ for NMIN in range(10):
961
+ if(HEFREQ0[NMIN] <= FREQ): break
962
+
963
+ dum = np.float64((33.32-2.*FREQLG, -390.026+(21.035-0.318*FREQLG)*FREQLG, 26.83-1.91*FREQLG, 61.21-2.9*FREQLG, 81.35-3.5*FREQLG, 12.69-1.54*FREQLG, 23.85-1.86*FREQLG, 49.30-2.60*FREQLG,85.20-3.69*FREQLG, 58.81-2.89*FREQLG ))
964
+
965
+ TRANS[NMIN::] = np.exp(dum[NMIN::])
966
+
967
+
968
+ EX = BOLTEX
969
+ if(FREQ < 2.055E14): EX=EXLIM/EHVKT
970
+
971
+ HE1=(EX-EXLIM)*C;
972
+ HE1 += (TRANS*BOLT).sum()
973
+
974
+ return (HE1+COULFF(TLOG,FREQLG,1)*FREET*CFREE)*STIM
975
+
976
+ # ----------------------------------------------------------------------------------------
977
+
978
+ @njit(cache=True)
979
+ def HE2OP( XHE2, XHE3, XNE, FREQ, FREQLG, T, TKEV, TLOG, EHVKT, STIM):
980
+
981
+ #
982
+ # REQUIRES FUNCTIONS COULX AND COULFF
983
+ # FREQUENCIES ARE 4X HYDROGEN,CHI ARE FOR ION POT=54.403
984
+ #
985
+ CONT = np.empty(9)
986
+
987
+ N12 = (np.arange(9)+1.0)**2
988
+ BOLT=np.exp(-(54.403-54.403/N12)/TKEV)*2.*N12*XHE2
989
+
990
+ FREET=XNE*XHE3/sqrt(T)
991
+ XR=XHE2/13.595*TKEV
992
+ BOLTEX=exp(-53.859/TKEV)*XR
993
+ EXLIM=exp(-54.403/TKEV)*XR
994
+
995
+ for N in range(9): CONT[N]=COULX(N,FREQ,2.0)
996
+
997
+ FREQ3=(FREQ*1.E-5)**3
998
+ CFREE=3.6919E-07/FREQ3*4.0
999
+ C=2.815E14*2.0*2.0/FREQ3
1000
+ EX=BOLTEX
1001
+
1002
+ if(FREQ < 1.31522E14): EX=EXLIM/EHVKT
1003
+ HE2=(EX-EXLIM)*C
1004
+
1005
+ HE2 += (CONT*BOLT).sum()
1006
+ HE2=(HE2+COULFF(TLOG,FREQLG,2)*CFREE*FREET)*STIM
1007
+
1008
+ if(HE2 >= 1.E-20): return HE2
1009
+ else: return 0.0
1010
+
1011
+
1012
+ # ----------------------------------------------------------------------------------------
1013
+
1014
+ @njit(cache=True)
1015
+ def HEMIOP( XHE1, FREQ, T, XNE):
1016
+ A= 3.397E-26+(-5.216E-11+7.039E05/FREQ)/FREQ;
1017
+ B=-4.116E-22+( 1.067E-06+8.135E09/FREQ)/FREQ;
1018
+ C= 5.081E-17+(-8.724E-03-5.659E12/FREQ)/FREQ;
1019
+ return (A*T+B+C/T)*XNE*XHE1*1.E-20;
1020
+
1021
+ # ----------------------------------------------------------------------------------------
1022
+
1023
+ @njit(cache=True)
1024
+ def HERAOP(XHE1, FREQ):
1025
+ WW=(2.997925E+03/min(FREQ*1.E-15,5.15))**2
1026
+ arg = 1.+(2.44E5+5.94E10/(WW-2.90E5))/WW
1027
+ SIG=5.484E-14/WW/WW*arg*arg
1028
+ return SIG*XHE1
1029
+
1030
+ # ----------------------------------------------------------------------------------------
1031
+
1032
+ PEACH0 = np.float64((-42.474, -42.350, -42.109, -41.795, -41.467, -41.159, -40.883,#,/* 1500 */
1033
+ -41.808, -41.735, -41.582, -41.363, -41.115, -40.866, -40.631,#,/* 1550 */
1034
+ -41.273, -41.223, -41.114, -40.951, -40.755, -40.549, -40.347,#,/* 1621 */
1035
+ -45.583, -44.008, -42.957, -42.205, -41.639, -41.198, -40.841,#,/* 1622 */
1036
+ -44.324, -42.747, -41.694, -40.939, -40.370, -39.925, -39.566,#,/* 2513 */
1037
+ -50.969, -48.388, -46.630, -45.344, -44.355, -43.568, -42.924,#,/* 2514 */
1038
+ -50.633, -48.026, -46.220, -44.859, -43.803, -42.957, -42.264,#,/* 3756 */
1039
+ -53.028, -49.643, -47.367, -45.729, -44.491, -43.520, -42.736,#,/* 3757 */
1040
+ -51.785, -48.352, -46.050, -44.393, -43.140, -42.157, -41.363,#,/* 6549 */
1041
+ -52.285, -48.797, -46.453, -44.765, -43.486, -42.480, -41.668,#,/* 6550 */
1042
+ -52.028, -48.540, -46.196, -44.507, -43.227, -42.222, -41.408,#,/* 7234 */
1043
+ -52.384, -48.876, -46.513, -44.806, -43.509, -42.488, -41.660,#,/* 7235 */
1044
+ -52.363, -48.856, -46.493, -44.786, -43.489, -42.467, -41.639,#,/* 7291 */
1045
+ -54.704, -50.772, -48.107, -46.176, -44.707, -43.549, -42.611,#,/* 7292 */
1046
+ -54.359, -50.349, -47.643, -45.685, -44.198, -43.027, -42.418)).reshape((15,7))
1047
+ FREQMG = np.float64((1.9341452e15,1.8488510e15,1.1925797e15, 7.9804046e14,4.5772110e14,4.1440977e14,
1048
+ 4.1113514e14))
1049
+ FLOG0 = np.float64((35.32123,35.19844,35.15334,34.71490,34.31318, 33.75728,33.65788,33.64994,33.43947))
1050
+ TLG0=np.float64((8.29405,8.51719,8.69951,8.85367, 8.98720,9.10498,9.21034))
1051
+
1052
+ @njit(cache=True)
1053
+ def Mg1OP(FREQ, FREQLG, T, TLOG):
1054
+ NT=min(6,int(floor(T/1000.))-3)
1055
+ if(NT<1): NT=1
1056
+
1057
+ DT=(TLOG-TLG0[NT-1])/(TLG0[NT]-TLG0[NT-1])
1058
+ for N in range(7):
1059
+ if(FREQ > FREQMG[N]): break
1060
+
1061
+ D=(FREQLG-FLOG0[N])/(FLOG0[N+1]-FLOG0[N])
1062
+ if(N > 1): N=2*N-1
1063
+ D1=1.0-D
1064
+ XWL1=PEACH0[N+1,NT-1]*D + PEACH0[N,NT-1]*D1
1065
+ XWL2=PEACH0[N+1,NT ]*D + PEACH0[N,NT ]*D1
1066
+
1067
+ return exp(XWL1*(1.-DT)+XWL2*DT)
1068
+
1069
+ # ----------------------------------------------------------------------------------------
1070
+
1071
+ @njit(cache=True)
1072
+ def C1OP( FREQ, TKEV):
1073
+
1074
+ # CROSS-SECTION TIMES THE PARTITION FUNCTION
1075
+ C1240=5.*exp(-1.264/TKEV)
1076
+ C1444=exp(-2.683/TKEV)
1077
+ X1444=0.0
1078
+ X1240=0.0
1079
+ X1100=0.0
1080
+
1081
+ if(FREQ >= 2.7254E15): X1100=SEATON(2.7254E15,1.219E-17,2.0E0,3.317E0,FREQ)
1082
+ if(FREQ >= 2.4196E15): X1240=SEATON(2.4196E15,1.030E-17,1.5E0,2.789E0,FREQ)
1083
+ if(FREQ >= 2.0761E15): X1444=SEATON(2.0761E15,9.590E-18,1.5E0,3.501E0,FREQ)
1084
+
1085
+ return X1100*9.+X1240*C1240+X1444*C1444
1086
+
1087
+ # ----------------------------------------------------------------------------------------
1088
+
1089
+ @njit(cache=True)
1090
+ def Al1OP(FREQ):
1091
+ if(FREQ > 1.443E15): return 2.1E-17*((1.443E15/FREQ)**3)*6.0
1092
+ else: return 0.0
1093
+
1094
+ # ----------------------------------------------------------------------------------------
1095
+
1096
+ PEACH1 = np.float64(( 38.136,38.138,38.140,38.141,38.143,38.144,38.144,38.145,38.145,#/* 1200 */
1097
+ 37.834,37.839,37.843,37.847,37.850,37.853,37.855,37.857,37.858,#/* 1400 */
1098
+ 37.898,37.898,37.897,37.897,37.897,37.896,37.895,37.895,37.894,#/* 1519 */
1099
+ 40.737,40.319,40.047,39.855,39.714,39.604,39.517,39.445,39.385,#/* 1520 */
1100
+ 40.581,40.164,39.893,39.702,39.561,39.452,39.366,39.295,39.235,#/* 1676 */
1101
+ 45.521,44.456,43.753,43.254,42.878,42.580,42.332,42.119,41.930,#/* 1677 */
1102
+ 45.520,44.455,43.752,43.251,42.871,42.569,42.315,42.094,41.896,#/* 1978 */
1103
+ 55.068,51.783,49.553,47.942,46.723,45.768,44.997,44.360,43.823,#/* 1979 */
1104
+ 53.868,50.369,48.031,46.355,45.092,44.104,43.308,42.652,42.100,#/* 5379 */
1105
+ 54.133,50.597,48.233,46.539,45.261,44.262,43.456,42.790,42.230,#/* 5380 */
1106
+ 54.051,50.514,48.150,46.454,45.176,44.175,43.368,42.702,42.141,#/* 5624 */
1107
+ 54.442,50.854,48.455,46.733,45.433,44.415,43.592,42.912,42.340,#/* 5625 */
1108
+ 54.320,50.722,48.313,46.583,45.277,44.251,43.423,42.738,42.160,#/* 6260 */
1109
+ 55.691,51.965,49.444,47.615,46.221,45.119,44.223,43.478,42.848,#/* 6261 */
1110
+ 55.661,51.933,49.412,47.582,46.188,45.085,44.189,43.445,42.813,#/* 6349 */
1111
+ 55.973,52.193,49.630,47.769,46.349,45.226,44.314,43.555,42.913,#/* 6350 */
1112
+ 55.922,52.141,49.577,47.715,46.295,45.172,44.259,43.500,42.858,#/* 6491 */
1113
+ 56.828,52.821,50.110,48.146,46.654,45.477,44.522,43.730,43.061,#/* 6492 */
1114
+ 56.657,52.653,49.944,47.983,46.491,45.315,44.360,43.569,42.901)).reshape((19,9))
1115
+ FREQSI1 = np.float64((2.1413750e15,1.97231650e15,1.7879689e15,1.5152920e15,0.55723927e15,5.3295914e14,4.7886458e14,4.72164220e14,4.6185133e14))
1116
+ FLOG1 = np.float64((35.45438,35.30022,35.21799,35.11986,34.95438, 33.95402,33.90947,33.80244,33.78835,33.76626, 33.70518))
1117
+ TLG1 = np.float64((8.29405,8.51719,8.69951,8.85367,8.98720,9.10498,9.21034,9.30565,9.39266))
1118
+
1119
+ @njit(cache=True)
1120
+ def Si1OP(FREQ, FREQLG, T, TLOG):
1121
+ NT=min(8,int(floor(T/1000.))-3)
1122
+ if(NT<1): NT=1
1123
+ DT=(TLOG-TLG1[NT-1])/(TLG1[NT]-TLG1[NT-1])
1124
+
1125
+ for N in range(9):
1126
+ if(FREQ > FREQSI1[N]): break
1127
+
1128
+ D=(FREQLG-FLOG1[N])/(FLOG1[N+1]-FLOG1[N])
1129
+
1130
+ if(N>1): N=2*N-1
1131
+ DD=1.-D
1132
+ XWL1=PEACH1[N+1,NT-1]*D+PEACH1[N,NT-1]*DD
1133
+ XWL2=PEACH1[N+1,NT ]*D+PEACH1[N,NT ]*DD
1134
+
1135
+ return exp(-(XWL1*(1.-DT)+XWL2*DT))*9.
1136
+
1137
+ # ----------------------------------------------------------------------------------------
1138
+
1139
+
1140
+ G1 = np.float64((25.,35.,21.,15., 9.,35.,33.,21.,27.,49., 9.,21.,
1141
+ 27., 9., 9.,25.,33.,15.,35., 3., 5.,11.,15.,13.,
1142
+ 15., 9.,21.,15.,21.,25.,35., 9., 5.,45.,27.,21.,
1143
+ 15.,21.,15.,25.,21.,35., 5.,15.,45.,35.,55.,25.))
1144
+ E1 = np.float64((500., 7500.,12500.,17500.,19000.,19500.,19500.,
1145
+ 21000.,22000.,23000.,23000.,24000.,24000.,24500.,
1146
+ 24500.,26000.,26500.,26500.,27000.,27500.,28500.,
1147
+ 29000.,29500.,29500.,29500.,30000.,31500.,31500.,
1148
+ 33500.,33500.,34000.,34500.,34500.,35000.,35500.,
1149
+ 37000.,37000.,37000.,38500.,40000.,40000.,41000.,
1150
+ 41000.,43000.,43000.,43000.,43000.,44000.))
1151
+ WNO1 = np.float64((63500.,58500.,53500.,59500.,45000.,44500.,44500.,
1152
+ 43000.,58000.,41000.,54000.,40000.,40000.,57500.,
1153
+ 55500.,38000.,57500.,57500.,37000.,54500.,53500.,
1154
+ 55000.,34500.,34500.,34500.,34000.,32500.,32500.,
1155
+ 32500.,32500.,32000.,29500.,29500.,31000.,30500.,
1156
+ 29000.,27000.,54000.,27500.,24000.,47000.,23000.,
1157
+ 44000.,42000.,42000.,21000.,42000.,42000.))
1158
+
1159
+ @njit(cache=True)
1160
+ def Fe1OP(FREQ, HKT):
1161
+ WAVENO=FREQ/2.99792458E10
1162
+ if(WAVENO < 21000.): return 0.0
1163
+ BOLT=G1*np.exp(-E1*2.99792458e10*HKT);
1164
+ XXX=((WNO1+3000.-WAVENO)/WNO1/.1)
1165
+
1166
+
1167
+ XSECT = np.zeros(48)
1168
+ idx = np.where(WNO1 <WAVENO)[0]
1169
+ XSECT[idx] = 3.e-18/(1.+XXX[idx]**4)
1170
+
1171
+ return (XSECT*BOLT).sum()
1172
+
1173
+ # ----------------------------------------------------------------------------------------
1174
+
1175
+ @njit(cache=True)
1176
+ def COOLOP(XC1,XMg1,XAl1,XSi1,XFe1,STIM,FREQ,FREQLG,T,TLOG,TKEV,HKT):
1177
+ # Si I, Mg I, Al I, C I, Fe I
1178
+
1179
+ return ( C1OP(FREQ,TKEV )*XC1 +
1180
+ Mg1OP(FREQ,FREQLG,T,TLOG)*XMg1+
1181
+ Al1OP(FREQ )*XAl1+
1182
+ Si1OP(FREQ,FREQLG,T,TLOG)*XSi1+
1183
+ Fe1OP(FREQ,HKT )*XFe1)*STIM
1184
+
1185
+ # ----------------------------------------------------------------------------------------
1186
+
1187
+ @njit(cache=True)
1188
+ def N1OP( FREQ, TKEV):
1189
+
1190
+ # CROSS-SECTION TIMES PARTITION FUNCTION
1191
+
1192
+ C1130=6.*exp(-3.575/TKEV)
1193
+ C1020=10.*exp(-2.384/TKEV)
1194
+ X1130=0.
1195
+ X1020=0.
1196
+ X853=0.
1197
+
1198
+ if(FREQ >= 3.517915E15): X853 =SEATON(3.517915E15,1.142E-17,2.0E0,4.29E0,FREQ)
1199
+ if(FREQ >= 2.941534E15): X1020=SEATON(2.941534E15,4.410E-18,1.5E0,3.85E0,FREQ)
1200
+ if(FREQ >= 2.653317E15): X1130=SEATON(2.653317E15,4.200E-18,1.5E0,4.34E0,FREQ)
1201
+
1202
+ return X853*4.+X1020*C1020+X1130*C1130
1203
+
1204
+ # ----------------------------------------------------------------------------------------
1205
+
1206
+ @njit(cache=True)
1207
+ def O1OP( FREQ):
1208
+ if(FREQ >= 3.28805E15): return 9.*SEATON(3.28805E15,2.94E-18,1.E0,2.66E0,FREQ)
1209
+ else: return 0.0
1210
+
1211
+ # ----------------------------------------------------------------------------------------
1212
+
1213
+ @njit(cache=True)
1214
+ def Mg2OP( FREQ, TKEV):
1215
+
1216
+ # CROSS-SECTION TIMES PARTITION FUNCTION
1217
+
1218
+ C1169=6.*exp(-4.43/TKEV)
1219
+ X1169=0.0
1220
+ X824=0.0
1221
+
1222
+ if(FREQ >= 3.635492E15): X824 =SEATON(3.635492E15,1.40E-19,4.E0,6.7E0,FREQ)
1223
+ if(FREQ >= 2.564306E15): X1169=5.11E-19*(2.564306E15/FREQ)**3
1224
+
1225
+ return X824*2.+X1169*C1169
1226
+
1227
+ # ----------------------------------------------------------------------------------------
1228
+
1229
+ PEACH2 = np.float64((-43.8941, -43.8941, -43.8941, -43.8941, -43.8941, -43.8941,#/* 500 */
1230
+ -42.2444, -42.2444, -42.2444, -42.2444, -42.2444, -42.2444,#/* 600 */
1231
+ -40.6054, -40.6054, -40.6054, -40.6054, -40.6054, -40.6054,#/* 759 */
1232
+ -54.2389, -52.2906, -50.8799, -49.8033, -48.9485, -48.2490,#/* 760 */
1233
+ -50.4108, -48.4892, -47.1090, -46.0672, -45.2510, -44.5933,#/* 1905 */
1234
+ -52.0936, -50.0741, -48.5999, -47.4676, -46.5649, -45.8246,#/* 1906 */
1235
+ -51.9548, -49.9371, -48.4647, -47.3340, -46.4333, -45.6947,#/* 1975 */
1236
+ -54.2407, -51.7319, -49.9178, -48.5395, -47.4529, -46.5709,#/* 1976 */
1237
+ -52.7355, -50.2218, -48.4059, -47.0267, -45.9402, -45.0592,#/* 3245 */
1238
+ -53.5387, -50.9189, -49.0200, -47.5750, -46.4341, -45.5082,#/* 3246 */
1239
+ -53.2417, -50.6234, -48.7252, -47.2810, -46.1410, -45.2153,#/* 3576 */
1240
+ -53.5097, -50.8535, -48.9263, -47.4586, -46.2994, -45.3581,#/* 3577 */
1241
+ -54.0561, -51.2365, -49.1980, -47.6497, -46.4302, -45.4414,#/* 3900 */
1242
+ -53.8469, -51.0256, -48.9860, -47.4368, -46.2162, -45.2266)).reshape((14,6))
1243
+
1244
+ FREQSI2 = np.float64((4.9965417e15,3.9466738e15,1.5736321e15,1.5171539e15,9.2378947e14,8.3825004e14,7.6869872e14))
1245
+ FLOG2 = np.float64((36.32984,36.14752,35.91165,34.99216,34.95561,34.45941,34.36234,34.27572,34.20161))
1246
+ TLG2 = np.float64((9.21034,9.39266,9.54681,9.68034,9.79813,9.90349))
1247
+
1248
+ @njit(cache=True)
1249
+ def Si2OP( FREQ, FREQLG, T, TLOG):
1250
+
1251
+ NT=min(5,int(floor(T/2000.))-4)
1252
+ if(NT<1): NT=1
1253
+ DT=(TLOG-TLG2[NT-1])/(TLG2[NT]-TLG2[NT-1])
1254
+
1255
+ for N in range(7):
1256
+ if(FREQ>FREQSI2[N]):
1257
+ break
1258
+
1259
+ D=(FREQLG-FLOG2[N])/(FLOG2[N+1]-FLOG2[N])
1260
+
1261
+ if(N>1): N=2*N-2
1262
+ if(N==13): N=12
1263
+
1264
+ D1=1.-D
1265
+ XWL1=PEACH2[N+1,NT-1]*D+PEACH2[N,NT-1]*D1
1266
+ XWL2=PEACH2[N+1,NT ]*D+PEACH2[N,NT ]*D1
1267
+
1268
+ return exp(XWL1*(1.-DT)+XWL2*DT)*6.
1269
+
1270
+ # ----------------------------------------------------------------------------------------
1271
+
1272
+ @njit(cache=True)
1273
+ def Ca2OP( FREQ, TKEV):
1274
+
1275
+ C1218=10.*exp(-1.697/TKEV)
1276
+ C1420=6.*exp(-3.142/TKEV)
1277
+ X1044=0.; X1218=0.; X1420=0.
1278
+
1279
+ if(FREQ>=2.870454e15):
1280
+ XXX=(2.870454e15/FREQ)**3
1281
+ X1044=1.08e-19*XXX
1282
+
1283
+ if(FREQ>=2.460127e15): X1218=1.64e-17*sqrt(2.460127e15/FREQ)
1284
+ if(FREQ>=2.110779e15): X1420=SEATON(2.110779e15,4.13e-18,3.,0.69, FREQ)
1285
+
1286
+ return X1044+X1218*C1218+X1420*C1420
1287
+
1288
+ # ----------------------------------------------------------------------------------------
1289
+
1290
+ @njit(cache=True)
1291
+ def LUKEOP( XN1, XO1, XMg2, XSi2, XCa2, STIM, FREQ, FREQLG, T, TLOG, TKEV):
1292
+
1293
+ # N I, O I, Si II, Mg II, Ca II
1294
+
1295
+ return ( N1OP(FREQ,TKEV )*XN1 +
1296
+ O1OP(FREQ )*XO1 +
1297
+ Mg2OP(FREQ,TKEV )*XMg2+
1298
+ Si2OP(FREQ,FREQLG,T,TLOG)*XSi2+
1299
+ Ca2OP(FREQ,TKEV )*XCa2)*STIM
1300
+
1301
+ # ----------------------------------------------------------------------------------------
1302
+
1303
+ @njit(cache=True)
1304
+ def HOTOP():
1305
+ return 0.0 # dummy function, should fill this
1306
+
1307
+ # ----------------------------------------------------------------------------------------
1308
+
1309
+ @njit(cache=True)
1310
+ def ELECOP( XNE):
1311
+ return 0.6653E-24*XNE
1312
+
1313
+ # ----------------------------------------------------------------------------------------
1314
+
1315
+ @njit(cache=True)
1316
+ def H2RAOP( XH1, FREQ, T, TKEV, TLOG):
1317
+
1318
+
1319
+ WW=(2.997925E18/min(FREQ,2.922E15))**2
1320
+ WW2 = WW*WW
1321
+
1322
+ SIG=(8.14E-13+1.28e-6/WW+1.61e0/WW2)/WW2
1323
+ ARG=4.477/TKEV-4.6628E1+(1.8031E-3+(-5.023E-7+(8.1424E-11-5.0501E-15*T)*T)*T)*T-1.5*TLOG
1324
+ H1=XH1*2.0
1325
+
1326
+ if(ARG > -80.0): return exp(ARG)*H1*H1*SIG
1327
+ else: return 0.0
1328
+
1329
+ # ----------------------------------------------------------------------------------------
1330
+
1331
+ @njit(cache=True)
1332
+ def cop( T, TKEV, TK, HKT, TLOG, XNA, XNE, WLGRID, H1, H2, HMIN, HE1, HE2,
1333
+ HE3, C1, AL1, SI1, SI2, CA1, CA2, MG1, MG2, FE1, N1, O1):
1334
+
1335
+ nW = len(WLGRID)
1336
+ OPACITY = np.zeros(nW)
1337
+ SCATTER = np.zeros(nW)
1338
+
1339
+ for iWL in range(nW):
1340
+ FREQ=2.997925E18/WLGRID[iWL]
1341
+ FREQLG=log(FREQ)
1342
+ FREQ15=FREQ*1.E-15
1343
+ EHVKT=exp(-FREQ*HKT)
1344
+ STIM=1.0-EHVKT
1345
+
1346
+ ACOOL = 0.0; ALUKE = 0.0
1347
+
1348
+ AHYD = HOP(XNE,H1,H2,FREQ,FREQLG,T,TLOG,TKEV,STIM,EHVKT)
1349
+ AH2P = H2PLOP(H1,H2,FREQ,FREQLG,FREQ15,TKEV,STIM)
1350
+ AHMIN = HMINOP(H1,HMIN,FREQ,T,TKEV,XNE,EHVKT)
1351
+ SIGH = HRAYOP(H1,FREQ)
1352
+ AHE1 = HE1OP(HE1,HE2,XNE,FREQ,FREQLG,T,TKEV,TLOG,EHVKT,STIM)
1353
+ AHE2 = HE2OP(HE2, HE3,XNE,FREQ,FREQLG,T, TKEV, TLOG, EHVKT,STIM)
1354
+ AHEMIN = HEMIOP(HE1,FREQ,T,XNE)
1355
+ SIGHE = HERAOP(HE1,FREQ)
1356
+
1357
+ if(T < 12000.):
1358
+ ACOOL = COOLOP(C1 ,MG1, AL1,SI1, FE1,STIM,FREQ,FREQLG,T,TLOG,TKEV,HKT)
1359
+ if(T < 30000.):
1360
+ ALUKE = LUKEOP( N1, O1, MG2, SI2, CA2,STIM,FREQ,FREQLG,T,TLOG,TKEV)
1361
+
1362
+ AHOT = HOTOP()
1363
+ SIGEL = ELECOP(XNE)
1364
+ SIGH2 = H2RAOP(H1,FREQ,T,TKEV,TLOG)
1365
+
1366
+ A=AHYD+AHMIN+AH2P+AHE1+AHE2+AHEMIN+ACOOL+ALUKE+AHOT
1367
+ B=SIGH+SIGHE+SIGEL+SIGH2
1368
+
1369
+ OPACITY[iWL]=A+B
1370
+ SCATTER[iWL]=B
1371
+
1372
+ return OPACITY, SCATTER
1373
+
1374
+ # ----------------------------------------------------------------------------------------
1375
+