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.
- lightweaver/Data/AbundancesAsplund09.pickle +0 -0
- lightweaver/Data/AtomicMassesNames.pickle +0 -0
- lightweaver/Data/Barklem_dfdata.dat +41 -0
- lightweaver/Data/Barklem_pddata.dat +40 -0
- lightweaver/Data/Barklem_spdata.dat +46 -0
- lightweaver/Data/DefaultMolecules/C2.molecule +27 -0
- lightweaver/Data/DefaultMolecules/CH/CH_X-A.asc +46409 -0
- lightweaver/Data/DefaultMolecules/CH/CH_X-A_12.asc +28322 -0
- lightweaver/Data/DefaultMolecules/CH/CH_X-B.asc +4272 -0
- lightweaver/Data/DefaultMolecules/CH/CH_X-B_12.asc +2583 -0
- lightweaver/Data/DefaultMolecules/CH/CH_X-C.asc +20916 -0
- lightweaver/Data/DefaultMolecules/CH/CH_X-C_12.asc +13106 -0
- lightweaver/Data/DefaultMolecules/CH.molecule +35 -0
- lightweaver/Data/DefaultMolecules/CN.molecule +30 -0
- lightweaver/Data/DefaultMolecules/CO/vmax=3_Jmax=49_dv=1_26 +296 -0
- lightweaver/Data/DefaultMolecules/CO/vmax=9_Jmax=120_dv=1_26 +2162 -0
- lightweaver/Data/DefaultMolecules/CO.molecule +30 -0
- lightweaver/Data/DefaultMolecules/CO_NLTE.molecule +29 -0
- lightweaver/Data/DefaultMolecules/CaH.molecule +29 -0
- lightweaver/Data/DefaultMolecules/H2+.molecule +27 -0
- lightweaver/Data/DefaultMolecules/H2.molecule +27 -0
- lightweaver/Data/DefaultMolecules/H2O.molecule +27 -0
- lightweaver/Data/DefaultMolecules/HF.molecule +29 -0
- lightweaver/Data/DefaultMolecules/LiH.molecule +27 -0
- lightweaver/Data/DefaultMolecules/MgH.molecule +34 -0
- lightweaver/Data/DefaultMolecules/N2.molecule +28 -0
- lightweaver/Data/DefaultMolecules/NH.molecule +27 -0
- lightweaver/Data/DefaultMolecules/NO.molecule +27 -0
- lightweaver/Data/DefaultMolecules/O2.molecule +27 -0
- lightweaver/Data/DefaultMolecules/OH.molecule +27 -0
- lightweaver/Data/DefaultMolecules/SiO.molecule +26 -0
- lightweaver/Data/DefaultMolecules/TiO.molecule +30 -0
- lightweaver/Data/Quadratures.pickle +0 -0
- lightweaver/Data/pf_Kurucz.input +0 -0
- lightweaver/DefaultIterSchemes/.placeholder +0 -0
- lightweaver/DefaultIterSchemes/SimdImpl_AVX2FMA.cpython-312-x86_64-linux-gnu.so +0 -0
- lightweaver/DefaultIterSchemes/SimdImpl_AVX512.cpython-312-x86_64-linux-gnu.so +0 -0
- lightweaver/DefaultIterSchemes/SimdImpl_SSE2.cpython-312-x86_64-linux-gnu.so +0 -0
- lightweaver/LwCompiled.cpython-312-x86_64-linux-gnu.so +0 -0
- lightweaver/__init__.py +33 -0
- lightweaver/atmosphere.py +1767 -0
- lightweaver/atomic_model.py +852 -0
- lightweaver/atomic_set.py +1286 -0
- lightweaver/atomic_table.py +653 -0
- lightweaver/barklem.py +151 -0
- lightweaver/benchmark.py +113 -0
- lightweaver/broadening.py +605 -0
- lightweaver/collisional_rates.py +337 -0
- lightweaver/config.py +106 -0
- lightweaver/constants.py +22 -0
- lightweaver/crtaf.py +197 -0
- lightweaver/fal.py +440 -0
- lightweaver/iterate_ctx.py +241 -0
- lightweaver/iteration_update.py +134 -0
- lightweaver/libenkiTS.so +0 -0
- lightweaver/molecule.py +225 -0
- lightweaver/multi.py +113 -0
- lightweaver/nr_update.py +106 -0
- lightweaver/rh_atoms.py +19743 -0
- lightweaver/simd_management.py +42 -0
- lightweaver/utils.py +509 -0
- lightweaver/version.py +34 -0
- lightweaver/wittmann.py +1375 -0
- lightweaver/zeeman.py +157 -0
- lightweaver-0.16.1.dist-info/METADATA +81 -0
- lightweaver-0.16.1.dist-info/RECORD +69 -0
- lightweaver-0.16.1.dist-info/WHEEL +6 -0
- lightweaver-0.16.1.dist-info/licenses/LICENSE +21 -0
- lightweaver-0.16.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import TYPE_CHECKING, Any, List, Optional, Sequence
|
|
3
|
+
|
|
4
|
+
import astropy.units as u
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
import lightweaver.constants as Const
|
|
8
|
+
from .atomic_table import PeriodicTable
|
|
9
|
+
from .barklem import Barklem
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .atmosphere import Atmosphere
|
|
13
|
+
from .atomic_model import AtomicLine
|
|
14
|
+
from .atomic_set import SpeciesStateTable
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class LineBroadeningResult:
|
|
18
|
+
'''
|
|
19
|
+
Result expected from instances of `LineBroadening.broaden`.
|
|
20
|
+
'''
|
|
21
|
+
natural: np.ndarray
|
|
22
|
+
Qelast: np.ndarray
|
|
23
|
+
other: Optional[List] = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class LineBroadener:
|
|
28
|
+
'''
|
|
29
|
+
Base class for broadening implementations. To be used if your broadener
|
|
30
|
+
does something special and can't just return an array.
|
|
31
|
+
'''
|
|
32
|
+
def __repr__(self):
|
|
33
|
+
raise NotImplementedError
|
|
34
|
+
|
|
35
|
+
def setup(self, line: 'AtomicLine'):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> Any:
|
|
39
|
+
raise NotImplementedError
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class StandardLineBroadener(LineBroadener):
|
|
43
|
+
'''
|
|
44
|
+
Standard base class for broadening implementations. Unless you need to do
|
|
45
|
+
something weird, inherit from this one.
|
|
46
|
+
'''
|
|
47
|
+
def __repr__(self):
|
|
48
|
+
raise NotImplementedError
|
|
49
|
+
|
|
50
|
+
def setup(self, line: 'AtomicLine'):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> np.ndarray:
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class LineBroadening:
|
|
59
|
+
'''
|
|
60
|
+
Standard component of AtomicLine to compute the broadening parameters in
|
|
61
|
+
a flexible way.
|
|
62
|
+
|
|
63
|
+
For most Voigt-like situations, this should be usable without
|
|
64
|
+
modifications, but this class can be inherited from to make substantial
|
|
65
|
+
modifications.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
natural : list of StandardLineBroadener
|
|
70
|
+
List of broadening terms that are not elastic collisions (separated for PRD).
|
|
71
|
+
elastic : list of StandardLineBroadener
|
|
72
|
+
List of elastic broadening terms.
|
|
73
|
+
other : list of LineBroadener, optional
|
|
74
|
+
List of other broadening terms, not used by the VoigtLine by default,
|
|
75
|
+
but existing to provide _options_ (default: None)
|
|
76
|
+
'''
|
|
77
|
+
natural: List[StandardLineBroadener]
|
|
78
|
+
elastic: List[StandardLineBroadener]
|
|
79
|
+
other: Optional[List[LineBroadener]] = None
|
|
80
|
+
|
|
81
|
+
def __repr__(self):
|
|
82
|
+
otherStr = '' if self.other is None else ', other=%s' % repr(self.other)
|
|
83
|
+
s = 'LineBroadening(natural=%s, elastic=%s%s)' % (repr(self.natural),
|
|
84
|
+
repr(self.elastic), otherStr)
|
|
85
|
+
return s
|
|
86
|
+
|
|
87
|
+
def __post_init__(self):
|
|
88
|
+
if len(self.natural) == 0 and len(self.elastic) == 0:
|
|
89
|
+
raise ValueError('No standard broadening terms provided to LineBroadening')
|
|
90
|
+
|
|
91
|
+
def setup(self, line: 'AtomicLine'):
|
|
92
|
+
b: LineBroadener
|
|
93
|
+
for b in self.natural:
|
|
94
|
+
b.setup(line)
|
|
95
|
+
|
|
96
|
+
for b in self.elastic:
|
|
97
|
+
b.setup(line)
|
|
98
|
+
|
|
99
|
+
if self.other is not None:
|
|
100
|
+
for b in self.other:
|
|
101
|
+
b.setup(line)
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def sum_broadening_list(broadeners: List[StandardLineBroadener], atmos: 'Atmosphere',
|
|
105
|
+
eqPops: 'SpeciesStateTable') -> Optional[np.ndarray]:
|
|
106
|
+
'''
|
|
107
|
+
Sums a list of StandardLineBroadeners.
|
|
108
|
+
'''
|
|
109
|
+
if len(broadeners) == 0:
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
result = broadeners[0].broaden(atmos, eqPops)
|
|
113
|
+
for b in broadeners[1:]:
|
|
114
|
+
result += b.broaden(atmos, eqPops)
|
|
115
|
+
return result
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def compute_other_broadening(broadeners: Optional[List[LineBroadener]],
|
|
119
|
+
atmos: 'Atmosphere',
|
|
120
|
+
eqPops: 'SpeciesStateTable') -> Optional[List]:
|
|
121
|
+
'''
|
|
122
|
+
Returns a list of the computed broadening terms.
|
|
123
|
+
'''
|
|
124
|
+
|
|
125
|
+
if broadeners is None:
|
|
126
|
+
return None
|
|
127
|
+
if len(broadeners) == 0:
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
result = [b.broaden(atmos, eqPops) for b in broadeners]
|
|
131
|
+
return result
|
|
132
|
+
|
|
133
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> LineBroadeningResult:
|
|
134
|
+
'''
|
|
135
|
+
Computes the broadening, this function is called by the AtomicLine object.
|
|
136
|
+
'''
|
|
137
|
+
natural = self.sum_broadening_list(self.natural, atmos, eqPops)
|
|
138
|
+
Qelast = self.sum_broadening_list(self.elastic, atmos, eqPops)
|
|
139
|
+
|
|
140
|
+
others = self.compute_other_broadening(self.other, atmos, eqPops)
|
|
141
|
+
|
|
142
|
+
if natural is None:
|
|
143
|
+
if Qelast is None:
|
|
144
|
+
raise ValueError(f'Insufficient information provided to {self}')
|
|
145
|
+
natural = np.zeros_like(Qelast) # type: ignore
|
|
146
|
+
elif Qelast is None:
|
|
147
|
+
Qelast = np.zeros_like(natural)
|
|
148
|
+
|
|
149
|
+
return LineBroadeningResult(natural=natural,
|
|
150
|
+
Qelast=Qelast, other=others)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@dataclass(eq=False)
|
|
154
|
+
class VdwApprox(StandardLineBroadener):
|
|
155
|
+
'''
|
|
156
|
+
Base class for van der Waals approximation using a list of coefficients
|
|
157
|
+
(vals).
|
|
158
|
+
'''
|
|
159
|
+
vals: Sequence[float]
|
|
160
|
+
line: 'AtomicLine' = field(init=False)
|
|
161
|
+
|
|
162
|
+
def setup(self, line: 'AtomicLine'):
|
|
163
|
+
self.line = line
|
|
164
|
+
|
|
165
|
+
def __repr__(self):
|
|
166
|
+
s = '%s(vals=%s)' % (type(self).__name__, repr(self.vals))
|
|
167
|
+
return s
|
|
168
|
+
|
|
169
|
+
def __eq__(self, other):
|
|
170
|
+
if type(self) is not type(other):
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
if self.vals != other.vals:
|
|
174
|
+
return False
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
if self.line != other.line:
|
|
178
|
+
return False
|
|
179
|
+
except AttributeError:
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@dataclass(eq=False, repr=False)
|
|
186
|
+
class VdwUnsold(VdwApprox):
|
|
187
|
+
'''
|
|
188
|
+
Implementation of the Unsold method for van der Waals broadening.
|
|
189
|
+
'''
|
|
190
|
+
def setup(self, line: 'AtomicLine'):
|
|
191
|
+
self.line = line
|
|
192
|
+
if len(self.vals) != 2:
|
|
193
|
+
raise ValueError('VdwUnsold expects 2 coefficients (%s)' % repr(line))
|
|
194
|
+
|
|
195
|
+
Z = line.jLevel.stage + 1
|
|
196
|
+
cont = line.overlyingContinuumLevel
|
|
197
|
+
|
|
198
|
+
deltaR = (Const.ERydberg / (cont.E_SI - line.jLevel.E_SI))**2 \
|
|
199
|
+
- (Const.ERydberg / (cont.E_SI - line.iLevel.E_SI))**2
|
|
200
|
+
fourPiEps0 = 4.0 * np.pi * Const.Epsilon0
|
|
201
|
+
self.C625 = (2.5 * Const.QElectron**2 / fourPiEps0 * Const.ABarH / fourPiEps0 \
|
|
202
|
+
* 2 * np.pi * (Z * Const.RBohr)**2 / Const.HPlanck * deltaR)**0.4
|
|
203
|
+
|
|
204
|
+
element = line.atom.element
|
|
205
|
+
|
|
206
|
+
self.vRel35He = (8.0 * Const.KBoltzmann / (np.pi*Const.Amu * element.mass)\
|
|
207
|
+
* (1.0 + element.mass / PeriodicTable[2].mass))**0.3
|
|
208
|
+
self.vRel35H = (8.0 * Const.KBoltzmann / (np.pi*Const.Amu * element.mass)\
|
|
209
|
+
* (1.0 + element.mass / PeriodicTable[1].mass))**0.3
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> np.ndarray:
|
|
213
|
+
'''
|
|
214
|
+
The function that is called by LineBroadening.
|
|
215
|
+
|
|
216
|
+
Parameters
|
|
217
|
+
----------
|
|
218
|
+
atmos : Atmosphere
|
|
219
|
+
The atmosphere in which to compute the broadening.
|
|
220
|
+
eqPops : SpeciesStateTable
|
|
221
|
+
The populations to use for computing the broadening.
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
broad : np.ndarray
|
|
226
|
+
An array detailing the broadening at each location in the
|
|
227
|
+
atmosphere [Nspace].
|
|
228
|
+
'''
|
|
229
|
+
heAbund = eqPops.abundance[PeriodicTable[2]]
|
|
230
|
+
cross = 8.08 * (self.vals[0] * self.vRel35H \
|
|
231
|
+
+ self.vals[1] * heAbund * self.vRel35He) * self.C625
|
|
232
|
+
nHGround = eqPops['H'][0, :]
|
|
233
|
+
broad = cross * atmos.temperature**0.3 * nHGround
|
|
234
|
+
return broad
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@dataclass(eq=False, repr=False)
|
|
238
|
+
class VdwBarklem(VdwApprox):
|
|
239
|
+
'''
|
|
240
|
+
Implementation of the Barklem method for van der Waals broadening.
|
|
241
|
+
'''
|
|
242
|
+
def setup(self, line: 'AtomicLine'):
|
|
243
|
+
self.line = line
|
|
244
|
+
if len(self.vals) != 2:
|
|
245
|
+
raise ValueError('VdwBarklem expects 2 coefficients (%s)' % (repr(line)))
|
|
246
|
+
newVals = Barklem.get_active_cross_section(line.atom, line, self.vals)
|
|
247
|
+
|
|
248
|
+
self.barklemVals = newVals
|
|
249
|
+
|
|
250
|
+
Z = line.jLevel.stage + 1
|
|
251
|
+
cont = line.overlyingContinuumLevel
|
|
252
|
+
|
|
253
|
+
deltaR = (Const.ERydberg / (cont.E_SI - line.jLevel.E_SI))**2 \
|
|
254
|
+
- (Const.ERydberg / (cont.E_SI - line.iLevel.E_SI))**2
|
|
255
|
+
fourPiEps0 = 4.0 * np.pi * Const.Epsilon0
|
|
256
|
+
self.C625 = (2.5 * Const.QElectron**2 / fourPiEps0 * Const.ABarH / fourPiEps0 \
|
|
257
|
+
* 2 * np.pi * (Z * Const.RBohr)**2 / Const.HPlanck * deltaR)**0.4
|
|
258
|
+
|
|
259
|
+
element = line.atom.element
|
|
260
|
+
|
|
261
|
+
self.vRel35He = (8.0 * Const.KBoltzmann / (np.pi*Const.Amu * element.mass)\
|
|
262
|
+
* (1.0 + element.mass / PeriodicTable[2].mass))**0.3
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> np.ndarray:
|
|
266
|
+
'''
|
|
267
|
+
The function that is called by LineBroadening.
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
atmos : Atmosphere
|
|
272
|
+
The atmosphere in which to compute the broadening.
|
|
273
|
+
eqPops : SpeciesStateTable
|
|
274
|
+
The populations to use for computing the broadening.
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
broad : np.ndarray
|
|
279
|
+
An array detailing the broadening at each location in the
|
|
280
|
+
atmosphere [Nspace].
|
|
281
|
+
'''
|
|
282
|
+
heAbund = eqPops.abundance[PeriodicTable[2]]
|
|
283
|
+
nHGround = eqPops['H'][0, :]
|
|
284
|
+
cross = 8.08 * self.barklemVals[2] * heAbund * self.vRel35He * self.C625
|
|
285
|
+
|
|
286
|
+
broad = self.barklemVals[0] * atmos.temperature**(0.5*(1.0-self.barklemVals[1])) \
|
|
287
|
+
+ cross * atmos.temperature**0.3
|
|
288
|
+
broad *= nHGround
|
|
289
|
+
return broad
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@dataclass(eq=False)
|
|
293
|
+
class RadiativeBroadening(StandardLineBroadener):
|
|
294
|
+
'''
|
|
295
|
+
Simple constant radiative broadening with coefficient gamma.
|
|
296
|
+
'''
|
|
297
|
+
gamma: float
|
|
298
|
+
line: 'AtomicLine' = field(init=False)
|
|
299
|
+
|
|
300
|
+
def setup(self, line: 'AtomicLine'):
|
|
301
|
+
self.line = line
|
|
302
|
+
|
|
303
|
+
def __repr__(self):
|
|
304
|
+
s = '%s(gamma=%g)' % (type(self).__name__, self.gamma)
|
|
305
|
+
return s
|
|
306
|
+
|
|
307
|
+
def __eq__(self, other):
|
|
308
|
+
if type(self) is not type(other):
|
|
309
|
+
return False
|
|
310
|
+
|
|
311
|
+
if self.gamma != other.gamma:
|
|
312
|
+
return False
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
if self.line != other.line:
|
|
316
|
+
return False
|
|
317
|
+
except AttributeError:
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
return True
|
|
321
|
+
|
|
322
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> np.ndarray:
|
|
323
|
+
'''
|
|
324
|
+
The function that is called by LineBroadening.
|
|
325
|
+
|
|
326
|
+
Parameters
|
|
327
|
+
----------
|
|
328
|
+
atmos : Atmosphere
|
|
329
|
+
The atmosphere in which to compute the broadening.
|
|
330
|
+
eqPops : SpeciesStateTable
|
|
331
|
+
The populations to use for computing the broadening.
|
|
332
|
+
|
|
333
|
+
Returns
|
|
334
|
+
-------
|
|
335
|
+
broad : np.ndarray
|
|
336
|
+
An array detailing the broadening at each location in the
|
|
337
|
+
atmosphere [Nspace].
|
|
338
|
+
'''
|
|
339
|
+
return np.ones_like(atmos.temperature) * self.gamma
|
|
340
|
+
|
|
341
|
+
@dataclass
|
|
342
|
+
class QuadraticStarkBroadening(StandardLineBroadener):
|
|
343
|
+
'''
|
|
344
|
+
Lindholm theory result for Quadratic Stark broadening by electrons and
|
|
345
|
+
singly ionised particles.
|
|
346
|
+
Follows HM2014 pp. 238-239, uses C4 from Traving 1960 via RH.
|
|
347
|
+
'''
|
|
348
|
+
coeff: float
|
|
349
|
+
line: 'AtomicLine' = field(init=False)
|
|
350
|
+
|
|
351
|
+
def __repr__(self):
|
|
352
|
+
s = '%s(coeff=%g)' % (type(self).__name__, self.coeff)
|
|
353
|
+
return s
|
|
354
|
+
|
|
355
|
+
def __eq__(self, other):
|
|
356
|
+
if type(self) is not type(other):
|
|
357
|
+
return False
|
|
358
|
+
|
|
359
|
+
if self.coeff != other.coeff:
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
if self.line != other.line:
|
|
364
|
+
return False
|
|
365
|
+
except AttributeError:
|
|
366
|
+
pass
|
|
367
|
+
|
|
368
|
+
return True
|
|
369
|
+
|
|
370
|
+
def setup(self, line: 'AtomicLine'):
|
|
371
|
+
self.line = line
|
|
372
|
+
weight = line.atom.element.mass
|
|
373
|
+
C = 8.0 * Const.KBoltzmann / (np.pi * Const.Amu * weight)
|
|
374
|
+
Cm = (1.0 + weight / (Const.MElectron / Const.Amu))**(1.0/6.0)
|
|
375
|
+
# NOTE(cmo): 28.0 is average atomic weight
|
|
376
|
+
Cm += (1.0 + weight / (28.0))**(1.0/6.0)
|
|
377
|
+
self.C = C
|
|
378
|
+
self.Cm = Cm
|
|
379
|
+
|
|
380
|
+
Z = line.iLevel.stage + 1
|
|
381
|
+
cont = line.overlyingContinuumLevel
|
|
382
|
+
|
|
383
|
+
E_Ryd = Const.ERydberg / (1.0 + Const.MElectron / (weight * Const.Amu))
|
|
384
|
+
neff_l = Z * np.sqrt(E_Ryd / (cont.E_SI - line.iLevel.E_SI))
|
|
385
|
+
neff_u = Z * np.sqrt(E_Ryd / (cont.E_SI - line.jLevel.E_SI))
|
|
386
|
+
|
|
387
|
+
C4 = Const.QElectron**2 / (4.0 * np.pi * Const.Epsilon0) \
|
|
388
|
+
* Const.RBohr \
|
|
389
|
+
* (2.0 * np.pi * Const.RBohr**2 / Const.HPlanck) / (18.0 * Z**4) \
|
|
390
|
+
* ((neff_u * (5.0 * neff_u**2 + 1.0))**2 \
|
|
391
|
+
- (neff_l * (5.0 * neff_l**2 + 1.0))**2)
|
|
392
|
+
self.cStark23 = 11.37 * (self.coeff * C4)**(2.0/3.0)
|
|
393
|
+
|
|
394
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> np.ndarray:
|
|
395
|
+
'''
|
|
396
|
+
The function that is called by LineBroadening.
|
|
397
|
+
|
|
398
|
+
Parameters
|
|
399
|
+
----------
|
|
400
|
+
atmos : Atmosphere
|
|
401
|
+
The atmosphere in which to compute the broadening.
|
|
402
|
+
eqPops : SpeciesStateTable
|
|
403
|
+
The populations to use for computing the broadening.
|
|
404
|
+
|
|
405
|
+
Returns
|
|
406
|
+
-------
|
|
407
|
+
broad : np.ndarray
|
|
408
|
+
An array detailing the broadening at each location in the
|
|
409
|
+
atmosphere [Nspace].
|
|
410
|
+
'''
|
|
411
|
+
vRel = (self.C * atmos.temperature)**(1.0/6.0) * self.Cm
|
|
412
|
+
stark = self.cStark23 * vRel * atmos.ne
|
|
413
|
+
return stark
|
|
414
|
+
|
|
415
|
+
@dataclass
|
|
416
|
+
class MultiplicativeStarkBroadening(StandardLineBroadener):
|
|
417
|
+
'''
|
|
418
|
+
Simple expression for multiplicative Stark broadening, assumes that this
|
|
419
|
+
can be expresed as a constant * ne.
|
|
420
|
+
'''
|
|
421
|
+
coeff: float
|
|
422
|
+
|
|
423
|
+
def __repr__(self):
|
|
424
|
+
s = '%s(coeff=%g)' % (type(self).__name__, self.coeff)
|
|
425
|
+
return s
|
|
426
|
+
|
|
427
|
+
def __eq__(self, other):
|
|
428
|
+
if type(self) is not type(other):
|
|
429
|
+
return False
|
|
430
|
+
|
|
431
|
+
if self.coeff != other.coeff:
|
|
432
|
+
return False
|
|
433
|
+
|
|
434
|
+
return True
|
|
435
|
+
|
|
436
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> np.ndarray:
|
|
437
|
+
'''
|
|
438
|
+
The function that is called by LineBroadening.
|
|
439
|
+
|
|
440
|
+
Parameters
|
|
441
|
+
----------
|
|
442
|
+
atmos : Atmosphere
|
|
443
|
+
The atmosphere in which to compute the broadening.
|
|
444
|
+
eqPops : SpeciesStateTable
|
|
445
|
+
The populations to use for computing the broadening.
|
|
446
|
+
|
|
447
|
+
Returns
|
|
448
|
+
-------
|
|
449
|
+
broad : np.ndarray
|
|
450
|
+
An array detailing the broadening at each location in the
|
|
451
|
+
atmosphere [Nspace].
|
|
452
|
+
'''
|
|
453
|
+
return self.coeff * atmos.ne # type: ignore
|
|
454
|
+
|
|
455
|
+
@dataclass
|
|
456
|
+
class HydrogenLinearStarkBroadening(StandardLineBroadener):
|
|
457
|
+
'''
|
|
458
|
+
Linear Stark broadening for the case of Hydrogen from Sutton 1978 (like
|
|
459
|
+
RH).
|
|
460
|
+
|
|
461
|
+
`reproduceOldRHBug` allows for reproducing an issue in older versions of RH
|
|
462
|
+
where the broadening effects was a factor os ~2pi too small as it had not
|
|
463
|
+
been converted to angular frequency.
|
|
464
|
+
'''
|
|
465
|
+
line: 'AtomicLine' = field(init=False)
|
|
466
|
+
reproduceOldRHBug: bool = False
|
|
467
|
+
|
|
468
|
+
def __repr__(self):
|
|
469
|
+
s = f'{type(self).__name__}(reproduceOldRHBug={self.reproduceOldRHBug})'
|
|
470
|
+
return s
|
|
471
|
+
|
|
472
|
+
def __eq__(self, other):
|
|
473
|
+
if type(self) is not type(other):
|
|
474
|
+
return False
|
|
475
|
+
|
|
476
|
+
try:
|
|
477
|
+
if self.line != other.line:
|
|
478
|
+
return False
|
|
479
|
+
except AttributeError:
|
|
480
|
+
pass
|
|
481
|
+
|
|
482
|
+
return True
|
|
483
|
+
|
|
484
|
+
def setup(self, line: 'AtomicLine'):
|
|
485
|
+
self.line = line
|
|
486
|
+
|
|
487
|
+
if line.atom.element.Z != 1:
|
|
488
|
+
raise ValueError('HydrogenicLinearStarkBroadening applied to non-Hydrogen line')
|
|
489
|
+
|
|
490
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> np.ndarray:
|
|
491
|
+
'''
|
|
492
|
+
The function that is called by LineBroadening.
|
|
493
|
+
|
|
494
|
+
Parameters
|
|
495
|
+
----------
|
|
496
|
+
atmos : Atmosphere
|
|
497
|
+
The atmosphere in which to compute the broadening.
|
|
498
|
+
eqPops : SpeciesStateTable
|
|
499
|
+
The populations to use for computing the broadening.
|
|
500
|
+
|
|
501
|
+
Returns
|
|
502
|
+
-------
|
|
503
|
+
broad : np.ndarray
|
|
504
|
+
An array detailing the broadening at each location in the
|
|
505
|
+
atmosphere [Nspace].
|
|
506
|
+
'''
|
|
507
|
+
nUpper = int(np.round(np.sqrt(0.5*self.line.jLevel.g)))
|
|
508
|
+
nLower = int(np.round(np.sqrt(0.5*self.line.iLevel.g)))
|
|
509
|
+
|
|
510
|
+
a1 = 0.642 if nUpper - nLower == 1 else 1.0
|
|
511
|
+
C = a1 * 0.6 * (nUpper**2 - nLower**2)
|
|
512
|
+
if not self.reproduceOldRHBug:
|
|
513
|
+
C *= 4.0 * np.pi * 0.425
|
|
514
|
+
GStark = C * u.Unit('m-2').to('cm-2') * atmos.ne**(2.0/3.0)
|
|
515
|
+
return GStark
|
|
516
|
+
|
|
517
|
+
@dataclass(eq=False)
|
|
518
|
+
class ScaledExponentBroadening(StandardLineBroadener):
|
|
519
|
+
'''
|
|
520
|
+
Broadening implementation following the CRTAF ScaledExponents recipe.
|
|
521
|
+
scaling * T**a * n_H(0)**b * n_e**c
|
|
522
|
+
|
|
523
|
+
scaling : float
|
|
524
|
+
Scalar multiplication term
|
|
525
|
+
temperatureExp : float
|
|
526
|
+
Temperature exponent
|
|
527
|
+
hydrogenExp : float
|
|
528
|
+
Neutral (ground-state) hydrogen density exponent
|
|
529
|
+
electronExp : float
|
|
530
|
+
Electron density exponent
|
|
531
|
+
'''
|
|
532
|
+
scaling: float
|
|
533
|
+
temperatureExp: float
|
|
534
|
+
hydrogenExp: float
|
|
535
|
+
electronExp: float
|
|
536
|
+
line: 'AtomicLine' = field(init=False)
|
|
537
|
+
|
|
538
|
+
def setup(self, line: 'AtomicLine'):
|
|
539
|
+
self.line = line
|
|
540
|
+
|
|
541
|
+
def __repr__(self):
|
|
542
|
+
s = '%s(scaling=%g, temperatureExp=%g, hydrogenExp=%g, electronExp=%g)' % (type(self).__name__,
|
|
543
|
+
self.scaling, self.temperatureExp, self.hydrogenExp, self.electronExp)
|
|
544
|
+
return s
|
|
545
|
+
|
|
546
|
+
def __eq__(self, other):
|
|
547
|
+
if type(self) is not type(other):
|
|
548
|
+
return False
|
|
549
|
+
|
|
550
|
+
if self.scaling != other.scaling:
|
|
551
|
+
return False
|
|
552
|
+
if self.temperatureExp != other.temperatureExp:
|
|
553
|
+
return False
|
|
554
|
+
if self.hydrogenExp != other.hydrogenExp:
|
|
555
|
+
return False
|
|
556
|
+
if self.electronExp != other.electronExp:
|
|
557
|
+
return False
|
|
558
|
+
|
|
559
|
+
try:
|
|
560
|
+
if self.line != other.line:
|
|
561
|
+
return False
|
|
562
|
+
except AttributeError:
|
|
563
|
+
pass
|
|
564
|
+
|
|
565
|
+
return True
|
|
566
|
+
|
|
567
|
+
def broaden(self, atmos: 'Atmosphere', eqPops: 'SpeciesStateTable') -> np.ndarray:
|
|
568
|
+
'''
|
|
569
|
+
The function that is called by LineBroadening.
|
|
570
|
+
|
|
571
|
+
Parameters
|
|
572
|
+
----------
|
|
573
|
+
atmos : Atmosphere
|
|
574
|
+
The atmosphere in which to compute the broadening.
|
|
575
|
+
eqPops : SpeciesStateTable
|
|
576
|
+
The populations to use for computing the broadening.
|
|
577
|
+
|
|
578
|
+
Returns
|
|
579
|
+
-------
|
|
580
|
+
broad : np.ndarray
|
|
581
|
+
An array detailing the broadening at each location in the
|
|
582
|
+
atmosphere [Nspace].
|
|
583
|
+
'''
|
|
584
|
+
result = np.ones_like(atmos.temperature)
|
|
585
|
+
|
|
586
|
+
if self.temperatureExp > 0.0:
|
|
587
|
+
if self.temperatureExp == 1.0:
|
|
588
|
+
result *= atmos.temperature
|
|
589
|
+
else:
|
|
590
|
+
result *= atmos.temperature**self.temperatureExp
|
|
591
|
+
|
|
592
|
+
if self.hydrogenExp > 0.0:
|
|
593
|
+
nHGround = eqPops['H'][0, :]
|
|
594
|
+
if self.hydrogenExp == 1.0:
|
|
595
|
+
result *= nHGround
|
|
596
|
+
else:
|
|
597
|
+
result *= nHGround**self.hydrogenExp
|
|
598
|
+
|
|
599
|
+
if self.electronExp > 0.0:
|
|
600
|
+
if self.electronExp == 1.0:
|
|
601
|
+
result *= atmos.ne
|
|
602
|
+
else:
|
|
603
|
+
result *= atmos.ne**self.electronExp
|
|
604
|
+
|
|
605
|
+
return self.scaling * result
|