pythonradex 2.0.1__py3-none-any.whl → 3.0.0__py3-none-any.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.
- pythonradex/LAMDA_file.py +27 -28
- pythonradex/atomic_transition.py +68 -33
- pythonradex/escape_probability.py +5 -3
- pythonradex/escape_probability_functions.py +11 -7
- pythonradex/helpers.py +2 -1
- pythonradex/intensity.py +12 -8
- pythonradex/molecule.py +36 -33
- pythonradex/radiative_transfer.py +23 -11
- pythonradex/rate_equations.py +9 -7
- {pythonradex-2.0.1.dist-info → pythonradex-3.0.0.dist-info}/METADATA +6 -3
- pythonradex-3.0.0.dist-info/RECORD +14 -0
- {pythonradex-2.0.1.dist-info → pythonradex-3.0.0.dist-info}/WHEEL +1 -1
- pythonradex-2.0.1.dist-info/RECORD +0 -14
- {pythonradex-2.0.1.dist-info → pythonradex-3.0.0.dist-info}/licenses/LICENSE.txt +0 -0
pythonradex/LAMDA_file.py
CHANGED
|
@@ -4,19 +4,20 @@ Created on Sun Nov 12 17:05:01 2017
|
|
|
4
4
|
|
|
5
5
|
@author: gianni
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
from pythonradex import atomic_transition
|
|
8
9
|
from scipy import constants
|
|
9
10
|
import numpy as np
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def is_comment(line):
|
|
13
|
-
if line.
|
|
14
|
+
if line.lstrip().startswith("!"):
|
|
14
15
|
return True
|
|
15
16
|
else:
|
|
16
17
|
return False
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def read(datafilepath
|
|
20
|
+
def read(datafilepath):
|
|
20
21
|
"""
|
|
21
22
|
Read a data file that follows the LAMDA format (https://home.strw.leidenuniv.nl/~moldata/molformat.html).
|
|
22
23
|
Such files can for example be downloaded from the EMAA (https://emaa.osug.fr/)
|
|
@@ -24,15 +25,6 @@ def read(datafilepath, read_frequencies, read_quantum_numbers=False):
|
|
|
24
25
|
|
|
25
26
|
Args:
|
|
26
27
|
datafilepath (:obj:`str`): The filepath to the LAMDA file.
|
|
27
|
-
read_frequencies (:obj:`bool`): Whether to read the frequencies of the
|
|
28
|
-
radiative transitions from the file or not. If False, calculates the
|
|
29
|
-
frequencies from the level energies given in the file. Setting this to
|
|
30
|
-
True can be useful since frequencies are sometimes given with more
|
|
31
|
-
significant digits than level energies. However, the LAMDA format does not
|
|
32
|
-
force a file to contain the frequencies, so they might not be present.
|
|
33
|
-
read_quantum_numbers (:obj:`bool`): Whether to read the quantum numbers
|
|
34
|
-
from the file. Defaults to False. The LAMDA format does not force a
|
|
35
|
-
file to list quantum numbers, so they might not be present.
|
|
36
28
|
|
|
37
29
|
Returns:
|
|
38
30
|
dict: A dictionary containing the data read from the file.
|
|
@@ -43,8 +35,9 @@ def read(datafilepath, read_frequencies, read_quantum_numbers=False):
|
|
|
43
35
|
pythonradex.atomic_transitions.RadiativeTransition objects);
|
|
44
36
|
'collisional transitions' (a list of
|
|
45
37
|
pythonradex.atomic_transition.CollisionalTransition objects);
|
|
46
|
-
'
|
|
47
|
-
|
|
38
|
+
'frequencies' (list of float containing the frequencies of the
|
|
39
|
+
radiative transitions form the file; empty
|
|
40
|
+
if file does not contain frequencies)
|
|
48
41
|
"""
|
|
49
42
|
# identifiers used in the LAMDA database files:
|
|
50
43
|
LAMDA_coll_ID = {
|
|
@@ -59,11 +52,12 @@ def read(datafilepath, read_frequencies, read_quantum_numbers=False):
|
|
|
59
52
|
datafile = open(datafilepath, "r")
|
|
60
53
|
levels = []
|
|
61
54
|
rad_transitions = []
|
|
55
|
+
frequencies = []
|
|
62
56
|
coll_transitions = {}
|
|
63
|
-
quantum_numbers = []
|
|
64
57
|
comment_offset = 0
|
|
65
58
|
for i, line in enumerate(datafile):
|
|
66
|
-
|
|
59
|
+
line = line.lstrip()
|
|
60
|
+
if is_comment(line) or line == "":
|
|
67
61
|
comment_offset += 1
|
|
68
62
|
continue
|
|
69
63
|
if i == 0 + comment_offset:
|
|
@@ -76,9 +70,8 @@ def read(datafilepath, read_frequencies, read_quantum_numbers=False):
|
|
|
76
70
|
if 3 + comment_offset <= i < 3 + comment_offset + n_levels:
|
|
77
71
|
line_entries = line.split()
|
|
78
72
|
leveldata = [float(string) for string in line_entries[:3]]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
), "level numeration not consistent"
|
|
73
|
+
if int(leveldata[0]) != i - 2 - comment_offset:
|
|
74
|
+
raise RuntimeError("level numeration not consistent")
|
|
82
75
|
# transforming energy from cm-1 to J; level indices starting from 0:
|
|
83
76
|
lev = atomic_transition.Level(
|
|
84
77
|
g=leveldata[2],
|
|
@@ -86,8 +79,6 @@ def read(datafilepath, read_frequencies, read_quantum_numbers=False):
|
|
|
86
79
|
index=int(leveldata[0]) - 1,
|
|
87
80
|
)
|
|
88
81
|
levels.append(lev)
|
|
89
|
-
if read_quantum_numbers:
|
|
90
|
-
quantum_numbers.append(line_entries[3])
|
|
91
82
|
continue
|
|
92
83
|
if i == 3 + comment_offset + n_levels:
|
|
93
84
|
n_rad_transitions = int(line)
|
|
@@ -101,15 +92,23 @@ def read(datafilepath, read_frequencies, read_quantum_numbers=False):
|
|
|
101
92
|
up = next(level for level in levels if level.index == radtransdata[1] - 1)
|
|
102
93
|
low = next(level for level in levels if level.index == radtransdata[2] - 1)
|
|
103
94
|
rad_trans_kwargs = {"up": up, "low": low, "A21": radtransdata[3]}
|
|
104
|
-
if read_frequencies:
|
|
105
|
-
nu0 = radtransdata[4] * constants.giga
|
|
106
|
-
Delta_E = up.E - low.E
|
|
107
|
-
assert np.isclose(
|
|
108
|
-
nu0, Delta_E / constants.h, atol=0, rtol=1e-3
|
|
109
|
-
), "read frequency is inconsistent with level energies"
|
|
110
|
-
rad_trans_kwargs["nu0"] = nu0
|
|
111
95
|
rad_trans = atomic_transition.RadiativeTransition(**rad_trans_kwargs)
|
|
112
96
|
rad_transitions.append(rad_trans)
|
|
97
|
+
if len(radtransdata) >= 5:
|
|
98
|
+
# frequencies are there, so let's read them
|
|
99
|
+
frequencies.append(radtransdata[4] * constants.giga)
|
|
100
|
+
# Delta_E = up.E - low.E
|
|
101
|
+
# if not helpers.DeltaE_nu0_consistent(Delta_E=Delta_E,nu0=nu0):
|
|
102
|
+
# filename = Path(datafilepath).name
|
|
103
|
+
# nu0_from_Delta_E = Delta_E/constants.h
|
|
104
|
+
# mean_nu0 = (nu0+nu0_from_Delta_E)/2
|
|
105
|
+
# relative_diff = abs(nu0-nu0_from_Delta_E)/mean_nu0
|
|
106
|
+
# warnings.warn(f"{filename}: transition from level {up.index+1} to {low.index+1}: "
|
|
107
|
+
# f"frequency ({nu0/constants.giga} GHz) inconsistent "
|
|
108
|
+
# +"with level energies (DeltaE corresponds "
|
|
109
|
+
# +f"to {nu0_from_Delta_E/constants.giga} GHz). "
|
|
110
|
+
# +f"relative difference: {relative_diff*100:.2g}%")
|
|
111
|
+
# rad_trans_kwargs["nu0"] = nu0
|
|
113
112
|
continue
|
|
114
113
|
if i == 4 + comment_offset + n_levels + n_rad_transitions:
|
|
115
114
|
coll_partner_offset = 0
|
|
@@ -160,5 +159,5 @@ def read(datafilepath, read_frequencies, read_quantum_numbers=False):
|
|
|
160
159
|
"levels": levels,
|
|
161
160
|
"radiative transitions": rad_transitions,
|
|
162
161
|
"collisional transitions": coll_transitions,
|
|
163
|
-
"
|
|
162
|
+
"frequencies": frequencies,
|
|
164
163
|
}
|
pythonradex/atomic_transition.py
CHANGED
|
@@ -5,10 +5,22 @@ Created on Sun Nov 12 17:03:55 2017
|
|
|
5
5
|
@author: gianni
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
# note about frequencies:
|
|
9
|
+
# in LAMDA files, the listed frequencies are only for "informational pruposes"
|
|
10
|
+
# indeed, RADEX does not use them for any calculation, but just for the final
|
|
11
|
+
# output to help the user to identify the transition
|
|
12
|
+
# on the other hand, the frequencies have sometimes better accuracy than DeltaE/h
|
|
13
|
+
# (e.g. for CO)
|
|
14
|
+
# here, I adopt a similar approach as RADEX: I use DeltaE/h for cases where the position
|
|
15
|
+
# of the line is not important, but I use the specific nu0 for cases where
|
|
16
|
+
# the spectral position of the line matters (e.g. spectra etc.)
|
|
17
|
+
|
|
18
|
+
|
|
8
19
|
from pythonradex import helpers
|
|
9
20
|
import numpy as np
|
|
10
21
|
from scipy import constants
|
|
11
22
|
import numba as nb
|
|
23
|
+
import warnings
|
|
12
24
|
|
|
13
25
|
|
|
14
26
|
@nb.jit(nopython=True, cache=True)
|
|
@@ -71,7 +83,7 @@ class LineProfile:
|
|
|
71
83
|
v (float or numpy.ndarray): velocity in [m/s]
|
|
72
84
|
|
|
73
85
|
Returns:
|
|
74
|
-
float or numpy.
|
|
86
|
+
float or numpy.ndarray: The line profile in [(m/s)\ :sup:`-1`]
|
|
75
87
|
"""
|
|
76
88
|
nu = self.nu0 * (1 - v / constants.c)
|
|
77
89
|
return self.phi_nu(nu) * self.nu0 / constants.c
|
|
@@ -245,26 +257,35 @@ class RadiativeTransition(Transition):
|
|
|
245
257
|
low (pythonradex.atomic_transition.Level): the lower level of the transition
|
|
246
258
|
Delta_E (:obj:`float`): the energy difference between the upper and lower level
|
|
247
259
|
name (:obj:`str`): name of the transition
|
|
248
|
-
|
|
260
|
+
Delta_E_over_h (:obj:`float`): Delta_E/h, corresponding to rest frequency in [Hz]
|
|
249
261
|
A21 (:obj:`float`): Einstein A21 coefficient in [s\ :sup:`-1`]
|
|
250
262
|
B21 (:obj:`float`): Einstein B21 coefficient in [sr m\ :sup:`2` Hz / Jy]
|
|
251
263
|
B12 (:obj:`float`): Einstein B12 coefficient in [sr m\ :sup:`2` Hz / Jy]
|
|
252
264
|
"""
|
|
253
265
|
|
|
254
|
-
def __init__(self, up, low, A21
|
|
255
|
-
|
|
256
|
-
|
|
266
|
+
def __init__(self, up, low, A21):
|
|
267
|
+
super().__init__(up=up, low=low)
|
|
268
|
+
if self.Delta_E <= 0:
|
|
269
|
+
raise RuntimeError("non-positive Delta_E for radiative transition")
|
|
257
270
|
self.A21 = A21
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
271
|
+
# rest frequency, but I do want to distinguish it from the frequency listed
|
|
272
|
+
# in the LAMDA file, because sometimes those are not consistent with DeltaE/h
|
|
273
|
+
self.Delta_E_over_h = self.Delta_E / constants.h
|
|
274
|
+
# nu0_from_Delta_E = self.Delta_E / constants.h
|
|
275
|
+
# if nu0 is None:
|
|
276
|
+
# self.nu0 = nu0_from_Delta_E
|
|
277
|
+
# else:
|
|
278
|
+
# # when reading LAMDA files, it is useful to read nu0 rather than calculate
|
|
279
|
+
# #from level energies, since nu0 is sometimes given with more significant
|
|
280
|
+
# #digits than the level energies
|
|
281
|
+
# if not helpers.DeltaE_nu0_consistent(Delta_E=self.Delta_E,nu0=nu0):
|
|
282
|
+
# warnings.warn(f"Delta_E and nu0 are inconsistent (up.index = {up.index}, "
|
|
283
|
+
# +f"low.index={low.index}, A21={A21} s-1, nu0={nu0/constants.giga} GHz)")
|
|
284
|
+
# self.nu0 = nu0
|
|
285
|
+
self.B21 = helpers.B21(A21=self.A21, nu=self.Delta_E_over_h)
|
|
286
|
+
self.B12 = helpers.B12(
|
|
287
|
+
A21=self.A21, nu=self.Delta_E_over_h, g1=self.low.g, g2=self.up.g
|
|
288
|
+
)
|
|
268
289
|
|
|
269
290
|
def source_function(self, x1, x2):
|
|
270
291
|
S = np.where(
|
|
@@ -286,7 +307,6 @@ class EmissionLine(RadiativeTransition):
|
|
|
286
307
|
Delta_E (:obj:`float`): the energy difference between the upper and lower level
|
|
287
308
|
in [J]
|
|
288
309
|
name (:obj:`str`): name of the transition
|
|
289
|
-
nu0 (:obj:`float`): rest frequency in [Hz]
|
|
290
310
|
A21 (:obj:`float`): Einstein A21 coefficient in [s\ :sup:`-1`]
|
|
291
311
|
B21 (:obj:`float`): Einstein B21 coefficient in [sr m\ :sup:`2` Hz / Jy]
|
|
292
312
|
B12 (:obj:`float`): Einstein B12 coefficient in [sr m\ :sup:`2` Hz / Jy]
|
|
@@ -295,27 +315,43 @@ class EmissionLine(RadiativeTransition):
|
|
|
295
315
|
"""
|
|
296
316
|
|
|
297
317
|
def __init__(self, up, low, A21, line_profile_type, width_v, nu0=None):
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
nu0=self.
|
|
301
|
-
|
|
318
|
+
super().__init__(up=up, low=low, A21=A21)
|
|
319
|
+
if nu0 is None:
|
|
320
|
+
nu0 = self.Delta_E_over_h
|
|
321
|
+
else:
|
|
322
|
+
self.check_DeltaE_nu0_consistency(nu0)
|
|
323
|
+
self.line_profile = line_profiles[line_profile_type](nu0=nu0, width_v=width_v)
|
|
302
324
|
self.tau_kwargs = {"A21": self.A21, "g_up": self.up.g, "g_low": self.low.g}
|
|
303
325
|
|
|
304
326
|
@classmethod
|
|
305
327
|
def from_radiative_transition(
|
|
306
|
-
cls, radiative_transition, line_profile_type, width_v
|
|
328
|
+
cls, radiative_transition, line_profile_type, width_v, nu0=None
|
|
307
329
|
):
|
|
308
330
|
"""Alternative constructor, taking an instance of RadiativeTransition,
|
|
309
|
-
a line profile class
|
|
331
|
+
a line profile class, the width of the line and optionally the rest
|
|
332
|
+
frequency"""
|
|
310
333
|
return cls(
|
|
311
334
|
up=radiative_transition.up,
|
|
312
335
|
low=radiative_transition.low,
|
|
313
336
|
A21=radiative_transition.A21,
|
|
314
337
|
line_profile_type=line_profile_type,
|
|
315
338
|
width_v=width_v,
|
|
316
|
-
nu0=
|
|
339
|
+
nu0=nu0,
|
|
317
340
|
)
|
|
318
341
|
|
|
342
|
+
def check_DeltaE_nu0_consistency(self, nu0):
|
|
343
|
+
# TODO needs a test
|
|
344
|
+
if not np.isclose(self.Delta_E_over_h, nu0, atol=0, rtol=1e-2):
|
|
345
|
+
mean = (self.Delta_E_over_h + nu0) / 2
|
|
346
|
+
relative_diff = abs(self.Delta_E_over_h - nu0) / mean
|
|
347
|
+
warnings.warn(
|
|
348
|
+
"inconsistency between DeltaE/h and the frequency listed in LAMDA file"
|
|
349
|
+
+ f" for transition {self.name}. "
|
|
350
|
+
+ f" DeltaE/h={self.Delta_E_over_h/constants.giga} GHz, "
|
|
351
|
+
+ f"listed frequency = {nu0/constants.giga}, relative difference ="
|
|
352
|
+
+ f"{relative_diff*100:.2g}%"
|
|
353
|
+
)
|
|
354
|
+
|
|
319
355
|
def tau(self, N1, N2, nu):
|
|
320
356
|
r"""Computes the optical depth
|
|
321
357
|
|
|
@@ -351,7 +387,7 @@ class EmissionLine(RadiativeTransition):
|
|
|
351
387
|
phi_nu=self.line_profile.phi_nu0,
|
|
352
388
|
N1=N1,
|
|
353
389
|
N2=N2,
|
|
354
|
-
nu=self.nu0,
|
|
390
|
+
nu=self.line_profile.nu0,
|
|
355
391
|
**self.tau_kwargs,
|
|
356
392
|
)
|
|
357
393
|
|
|
@@ -368,8 +404,9 @@ class CollisionalTransition(Transition):
|
|
|
368
404
|
"""
|
|
369
405
|
|
|
370
406
|
def __init__(self, up, low, K21_data, Tkin_data):
|
|
371
|
-
|
|
372
|
-
|
|
407
|
+
super().__init__(up=up, low=low)
|
|
408
|
+
if np.any(K21_data < 0):
|
|
409
|
+
raise ValueError("negative K21 values")
|
|
373
410
|
self.K21_data = K21_data
|
|
374
411
|
self.Tkin_data = Tkin_data
|
|
375
412
|
self.Tkin_data_limits = np.min(self.Tkin_data), np.max(self.Tkin_data)
|
|
@@ -386,15 +423,13 @@ class CollisionalTransition(Transition):
|
|
|
386
423
|
at the requested temperature(s)
|
|
387
424
|
|
|
388
425
|
Raises:
|
|
389
|
-
|
|
426
|
+
ValueError: If Tkin is outside the available temperature range.
|
|
390
427
|
"""
|
|
391
428
|
Tmin, Tmax = self.Tkin_data_limits
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
Tkin <= Tmax
|
|
397
|
-
), "requested temperature above maximum collider temperature"
|
|
429
|
+
if np.any(Tkin < Tmin):
|
|
430
|
+
raise ValueError("requested temperature below minimum collider temperature")
|
|
431
|
+
if np.any(Tkin > Tmax):
|
|
432
|
+
raise ValueError("requested temperature above maximum collider temperature")
|
|
398
433
|
K21 = np.interp(Tkin, self.Tkin_data, self.K21_data)
|
|
399
434
|
K12 = compute_K12(
|
|
400
435
|
K21=K21, g_up=self.up.g, g_low=self.low.g, Delta_E=self.Delta_E, Tkin=Tkin
|
|
@@ -4,6 +4,7 @@ Created on Sun Nov 12 17:31:02 2017
|
|
|
4
4
|
|
|
5
5
|
@author: gianni
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import numpy as np
|
|
8
9
|
import numba as nb
|
|
9
10
|
from pythonradex import escape_probability_functions
|
|
@@ -44,7 +45,7 @@ class StaticSphere(EscapeProbabilityStaticSphere):
|
|
|
44
45
|
# for small tau, the Osterbrock formula becomes numerically unstable,
|
|
45
46
|
# so we use a Taylor expansion
|
|
46
47
|
min_tau = 1e-2
|
|
47
|
-
stable_region = tau > min_tau
|
|
48
|
+
stable_region = np.abs(tau) > min_tau
|
|
48
49
|
flux_nu = (
|
|
49
50
|
2
|
|
50
51
|
* np.pi
|
|
@@ -59,7 +60,8 @@ class StaticSphere(EscapeProbabilityStaticSphere):
|
|
|
59
60
|
* (tau / 3 - tau**2 / 8 + tau**3 / 30 - tau**4 / 144)
|
|
60
61
|
) # from Wolfram Alpha
|
|
61
62
|
flux_nu = np.where(stable_region, flux_nu, flux_nu_Taylor)
|
|
62
|
-
|
|
63
|
+
if not np.all(np.isfinite(flux_nu)):
|
|
64
|
+
raise RuntimeError
|
|
63
65
|
return flux_nu / np.pi
|
|
64
66
|
|
|
65
67
|
|
|
@@ -67,7 +69,7 @@ class StaticSphere(EscapeProbabilityStaticSphere):
|
|
|
67
69
|
def exp_tau_factor(tau):
|
|
68
70
|
# for very small tau, the exp can introduce numerical errors, so we
|
|
69
71
|
# take care of that when computing (1-exp(-tau))
|
|
70
|
-
return np.where(tau < 1e-5, tau, 1 - np.exp(-tau))
|
|
72
|
+
return np.where(np.abs(tau) < 1e-5, tau, 1 - np.exp(-tau))
|
|
71
73
|
|
|
72
74
|
|
|
73
75
|
class Flux1D:
|
|
@@ -9,7 +9,6 @@ Created on Thu May 23 11:10:38 2024
|
|
|
9
9
|
import numba as nb
|
|
10
10
|
import numpy as np
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
# It would be much more elegant to put these functions directly into the
|
|
14
13
|
# classes defined in escape_probability.py. But I want to compile the beta functions
|
|
15
14
|
# with numba, so I have to put these functions outside the class definitions
|
|
@@ -41,7 +40,8 @@ def identify_tau_regions(tau):
|
|
|
41
40
|
+ negative_tau_region.sum()
|
|
42
41
|
+ unreliable_negative_region.sum()
|
|
43
42
|
)
|
|
44
|
-
|
|
43
|
+
if total_region_points != len(tau):
|
|
44
|
+
raise RuntimeError("selection needs to cover all points of tau")
|
|
45
45
|
return (
|
|
46
46
|
normal_tau_region,
|
|
47
47
|
small_tau_region,
|
|
@@ -130,7 +130,8 @@ def generate_Taylor_beta(beta_ana, beta_Taylor):
|
|
|
130
130
|
prob[negative] = beta_ana(tau[negative])
|
|
131
131
|
# here I just use abs(tau) to stabilize the code
|
|
132
132
|
prob[unreliable] = beta_ana(np.abs(tau[unreliable]))
|
|
133
|
-
|
|
133
|
+
if not np.all(np.isfinite(prob)):
|
|
134
|
+
raise RuntimeError("negative escape probability")
|
|
134
135
|
return clip_prob(prob)
|
|
135
136
|
|
|
136
137
|
return beta
|
|
@@ -185,7 +186,8 @@ def beta_static_slab(tau):
|
|
|
185
186
|
# should be ignored for tau < -1
|
|
186
187
|
int_term = interpolated_integral_term(tau=tau)
|
|
187
188
|
prob = np.where(tau < min_grid_tau, 1, int_term / tau)
|
|
188
|
-
|
|
189
|
+
if not np.all(np.isfinite(prob)):
|
|
190
|
+
raise RuntimeError("negative escape probability")
|
|
189
191
|
return clip_prob(prob)
|
|
190
192
|
|
|
191
193
|
|
|
@@ -213,14 +215,15 @@ def beta_LVG_sphere_RADEX(tau):
|
|
|
213
215
|
# I use the same formulae as RADEX, except that I take care of negative tau
|
|
214
216
|
# handling negative tau turns out to be important, e.g. for CO, Tkin=200,
|
|
215
217
|
# width_v=3 km/s, Ntot=1e20 cm-2, RADEX gives an invalid solution
|
|
216
|
-
|
|
218
|
+
if not (-7 < min_reliable_tau < -0.01):
|
|
219
|
+
raise RuntimeError("unexpected value for min_reliable_tau")
|
|
217
220
|
gtr7 = 7 <= tau
|
|
218
221
|
less7 = (0.001 <= tau) & (tau < 7)
|
|
219
222
|
small = (-0.001 <= tau) & (tau < 0.001)
|
|
220
223
|
negative = (min_reliable_tau <= tau) & (tau < -0.001)
|
|
221
224
|
unreliable_less7 = (-7 <= tau) & (tau < min_reliable_tau)
|
|
222
225
|
unreliable_gtr7 = tau < -7
|
|
223
|
-
|
|
226
|
+
if not (
|
|
224
227
|
gtr7.sum()
|
|
225
228
|
+ less7.sum()
|
|
226
229
|
+ small.sum()
|
|
@@ -228,7 +231,8 @@ def beta_LVG_sphere_RADEX(tau):
|
|
|
228
231
|
+ unreliable_less7.sum()
|
|
229
232
|
+ unreliable_gtr7.sum()
|
|
230
233
|
== tau.size
|
|
231
|
-
)
|
|
234
|
+
):
|
|
235
|
+
raise RuntimeError
|
|
232
236
|
beta = np.empty_like(tau)
|
|
233
237
|
beta[gtr7] = beta_LVG_sphere_RADEX_gtr7(tau[gtr7])
|
|
234
238
|
beta[less7] = beta_LVG_sphere_RADEX_less7(tau[less7])
|
pythonradex/helpers.py
CHANGED
|
@@ -74,7 +74,8 @@ def relative_difference(a, b):
|
|
|
74
74
|
a=0 and b!=0: return 1"""
|
|
75
75
|
abs_diff = np.abs(a - b)
|
|
76
76
|
rel_diff = np.where((a == 0) & (b == 0), 0, np.where(a == 0, 1, abs_diff / a))
|
|
77
|
-
|
|
77
|
+
if np.any(np.isnan(rel_diff)):
|
|
78
|
+
raise RuntimeError("relative difference calculation returned nan values")
|
|
78
79
|
return np.abs(rel_diff)
|
|
79
80
|
|
|
80
81
|
|
pythonradex/intensity.py
CHANGED
|
@@ -46,9 +46,10 @@ class IntensityCalculator:
|
|
|
46
46
|
if self.is_LVG_sphere
|
|
47
47
|
else self.n_nu_elements["regular"]
|
|
48
48
|
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if n_nu_elements % 2 != 1:
|
|
50
|
+
raise RuntimeError(
|
|
51
|
+
"need odd number of elements to include nu0 in the array"
|
|
52
|
+
)
|
|
52
53
|
middle_index = n_nu_elements // 2
|
|
53
54
|
nu0 = self.emitting_molecule.nu0[transitions]
|
|
54
55
|
width_nu = self.emitting_molecule.width_v / constants.c * nu0
|
|
@@ -168,7 +169,7 @@ class IntensityCalculator:
|
|
|
168
169
|
|
|
169
170
|
def construct_tau_individual_line(self, line, nu, tau_nu0):
|
|
170
171
|
# tau is prop to phi_nu/nu**2
|
|
171
|
-
normalisation = line.line_profile.phi_nu0 / line.nu0**2 / tau_nu0
|
|
172
|
+
normalisation = line.line_profile.phi_nu0 / line.line_profile.nu0**2 / tau_nu0
|
|
172
173
|
return line.line_profile.phi_nu(nu) / nu**2 / normalisation
|
|
173
174
|
|
|
174
175
|
def set_tau_lines(self):
|
|
@@ -224,14 +225,15 @@ class IntensityCalculator:
|
|
|
224
225
|
+ " are optically thin!"
|
|
225
226
|
)
|
|
226
227
|
for dust_func in (self.S_dust, self.tau_dust):
|
|
227
|
-
|
|
228
|
+
if not np.all(dust_func(self.nu) == 0):
|
|
229
|
+
raise RuntimeError("LVG does not support dust")
|
|
228
230
|
I = np.zeros_like(self.nu)
|
|
229
231
|
for line_index, line, tau_line in zip(
|
|
230
232
|
self.nu_selected_line_indices, self.nu_selected_lines, self.tau_lines
|
|
231
233
|
):
|
|
232
234
|
LVG_sphere_kwargs = {
|
|
233
235
|
"nu": self.nu,
|
|
234
|
-
"nu0": line.nu0,
|
|
236
|
+
"nu0": line.line_profile.nu0,
|
|
235
237
|
"V": self.V_LVG_sphere,
|
|
236
238
|
}
|
|
237
239
|
source_function = helpers.B_nu(T=self.Tex[line_index], nu=self.nu)
|
|
@@ -251,7 +253,8 @@ class IntensityCalculator:
|
|
|
251
253
|
# this function works only if there are no overlapping lines
|
|
252
254
|
# faster than calling the spectrum function, since I don't need to loop
|
|
253
255
|
# over the lines
|
|
254
|
-
|
|
256
|
+
if self.emitting_molecule.any_line_has_overlap(transitions):
|
|
257
|
+
raise ValueError("overlapping lines not allowed in this function")
|
|
255
258
|
nu = self.emitting_molecule.nu0[transitions]
|
|
256
259
|
tau_lines = self.tau_nu0_individual_transitions[transitions]
|
|
257
260
|
Tex = self.Tex[transitions]
|
|
@@ -265,7 +268,8 @@ class IntensityCalculator:
|
|
|
265
268
|
kwargs = {"tau": tau_tot, "source_function": source_function}
|
|
266
269
|
if self.is_LVG_sphere:
|
|
267
270
|
for dust in (S_dust, tau_dust):
|
|
268
|
-
|
|
271
|
+
if not np.all(dust == 0):
|
|
272
|
+
raise RuntimeError("LVG does not support dust")
|
|
269
273
|
return escape_probability.specific_intensity_nu0_lvg_sphere(**kwargs)
|
|
270
274
|
else:
|
|
271
275
|
return self.specific_intensity(**kwargs)
|
pythonradex/molecule.py
CHANGED
|
@@ -4,6 +4,7 @@ Created on Sun Nov 12 16:48:00 2017
|
|
|
4
4
|
|
|
5
5
|
@author: gianni
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
from scipy import constants
|
|
8
9
|
import numpy as np
|
|
9
10
|
from pythonradex import LAMDA_file, atomic_transition
|
|
@@ -38,19 +39,12 @@ class Molecule:
|
|
|
38
39
|
datafilepath (:obj:`str`): The filepath to the LAMDA file.
|
|
39
40
|
partition_function (func): A user-supplied partition function of one argument
|
|
40
41
|
(temperature). Defaults to None. If equal to None, the partition function
|
|
41
|
-
will be calculated by using the data from the
|
|
42
|
+
will be calculated by using the data from the LAMDA file.
|
|
42
43
|
"""
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# so they might not be present.
|
|
48
|
-
data = LAMDA_file.read(datafilepath=datafilepath, read_frequencies=True)
|
|
49
|
-
except:
|
|
50
|
-
data = LAMDA_file.read(datafilepath=datafilepath, read_frequencies=False)
|
|
51
|
-
self.levels = data["levels"]
|
|
52
|
-
self.rad_transitions = data["radiative transitions"]
|
|
53
|
-
self.coll_transitions = data["collisional transitions"]
|
|
44
|
+
self.lamda_data = LAMDA_file.read(datafilepath=datafilepath)
|
|
45
|
+
self.levels = self.lamda_data["levels"]
|
|
46
|
+
self.rad_transitions = self.lamda_data["radiative transitions"]
|
|
47
|
+
self.coll_transitions = self.lamda_data["collisional transitions"]
|
|
54
48
|
self.n_levels = len(self.levels)
|
|
55
49
|
self.n_rad_transitions = len(self.rad_transitions)
|
|
56
50
|
self.set_partition_function(partition_function=partition_function)
|
|
@@ -58,7 +52,9 @@ class Molecule:
|
|
|
58
52
|
self.A21 = np.array([line.A21 for line in self.rad_transitions])
|
|
59
53
|
self.B21 = np.array([line.B21 for line in self.rad_transitions])
|
|
60
54
|
self.B12 = np.array([line.B12 for line in self.rad_transitions])
|
|
61
|
-
self.
|
|
55
|
+
self.Delta_E_over_h = np.array(
|
|
56
|
+
[line.Delta_E_over_h for line in self.rad_transitions]
|
|
57
|
+
)
|
|
62
58
|
self.gup_rad_transitions = np.array(
|
|
63
59
|
[line.up.g for line in self.rad_transitions]
|
|
64
60
|
)
|
|
@@ -148,7 +144,8 @@ class Molecule:
|
|
|
148
144
|
for i, line in enumerate(self.rad_transitions)
|
|
149
145
|
if line.name == transition_name
|
|
150
146
|
]
|
|
151
|
-
|
|
147
|
+
if len(candidate_numbers) != 1:
|
|
148
|
+
raise RuntimeError("unable to get unique transition number")
|
|
152
149
|
return candidate_numbers[0]
|
|
153
150
|
|
|
154
151
|
|
|
@@ -174,8 +171,8 @@ class EmittingMolecule(Molecule):
|
|
|
174
171
|
datafilepath,
|
|
175
172
|
line_profile_type,
|
|
176
173
|
width_v,
|
|
177
|
-
read_frequencies=False,
|
|
178
174
|
partition_function=None,
|
|
175
|
+
use_datafile_frequencies=True,
|
|
179
176
|
):
|
|
180
177
|
"""Constructs a new instance of the EmittingMolecule class using a LAMDA datafile
|
|
181
178
|
|
|
@@ -185,30 +182,33 @@ class EmittingMolecule(Molecule):
|
|
|
185
182
|
'rectangular' or 'Gaussian'
|
|
186
183
|
width_v (:obj:`float`): The width of the line profile in [m/s]. For a Gaussian
|
|
187
184
|
line profile, this corresponds to the FWHM.
|
|
188
|
-
read_frequencies (:obj:`bool`): Whether to read the frequencies of the
|
|
189
|
-
radiative transitions from the file or not. If False, calculates the
|
|
190
|
-
frequencies from the level energies given in the file. Setting this to
|
|
191
|
-
True can be useful since frequencies are sometimes given with more
|
|
192
|
-
significant digits than level energies. However, the LAMDA format does not
|
|
193
|
-
force a file to contain the frequencies, so they might not be present.
|
|
194
185
|
partition_function (func): A user-supplied partition function of one argument
|
|
195
186
|
(temperature). Defaults to None. If equal to None, the partition function
|
|
196
187
|
will be calculated by using the data from the provided LAMDA file.
|
|
188
|
+
use_datafile_frequencies (bool): whether to use the frequencies read from the LAMDA
|
|
189
|
+
file (if False, will use DeltaE/h).
|
|
197
190
|
"""
|
|
198
|
-
|
|
199
|
-
|
|
191
|
+
super().__init__(
|
|
192
|
+
datafilepath=datafilepath, partition_function=partition_function
|
|
200
193
|
)
|
|
201
194
|
self.width_v = width_v
|
|
202
195
|
self.line_profile_type = line_profile_type
|
|
203
|
-
# convert
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
# convert RadiativeTransition to EmissionLine (but keep the same attribute name)
|
|
197
|
+
converted_transitions = []
|
|
198
|
+
for i, line in enumerate(self.rad_transitions):
|
|
199
|
+
if use_datafile_frequencies:
|
|
200
|
+
nu0 = self.lamda_data["frequencies"][i]
|
|
201
|
+
else:
|
|
202
|
+
nu0 = None
|
|
203
|
+
converted = atomic_transition.EmissionLine.from_radiative_transition(
|
|
204
|
+
radiative_transition=line,
|
|
207
205
|
line_profile_type=line_profile_type,
|
|
208
206
|
width_v=width_v,
|
|
207
|
+
nu0=nu0,
|
|
209
208
|
)
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
converted_transitions.append(converted)
|
|
210
|
+
self.rad_transitions = converted_transitions
|
|
211
|
+
self.nu0 = np.array([line.line_profile.nu0 for line in self.rad_transitions])
|
|
212
212
|
self.identify_overlapping_lines()
|
|
213
213
|
if self.any_line_has_overlap(line_indices=range(self.n_rad_transitions)):
|
|
214
214
|
self.has_overlapping_lines = True
|
|
@@ -218,16 +218,18 @@ class EmittingMolecule(Molecule):
|
|
|
218
218
|
self.Tkin_data_limits = {}
|
|
219
219
|
for collider, coll_transitions in self.coll_transitions.items():
|
|
220
220
|
self.Tkin_data[collider] = coll_transitions[0].Tkin_data
|
|
221
|
-
|
|
221
|
+
if np.any(self.Tkin_data[collider][:-1] >= self.Tkin_data[collider][1:]):
|
|
222
|
+
raise RuntimeError
|
|
222
223
|
for coll_trans in coll_transitions:
|
|
223
|
-
|
|
224
|
+
if np.any(coll_trans.Tkin_data != self.Tkin_data[collider]):
|
|
225
|
+
raise RuntimeError
|
|
224
226
|
self.Tkin_data_limits[collider] = np.min(self.Tkin_data[collider]), np.max(
|
|
225
227
|
self.Tkin_data[collider]
|
|
226
228
|
)
|
|
227
229
|
self.set_K21_matrix()
|
|
228
230
|
# collecting phi_nu0 and coll parameters, needed for numba-compiled functions:
|
|
229
231
|
self.phi_nu0 = np.array(
|
|
230
|
-
[line.line_profile.
|
|
232
|
+
[line.line_profile.phi_nu0 for line in self.rad_transitions]
|
|
231
233
|
)
|
|
232
234
|
self.coll_gups = {}
|
|
233
235
|
self.coll_glows = {}
|
|
@@ -364,7 +366,8 @@ class EmittingMolecule(Molecule):
|
|
|
364
366
|
|
|
365
367
|
def interpolate_K(self, Tkin, collider):
|
|
366
368
|
Tlimits = self.Tkin_data_limits[collider]
|
|
367
|
-
|
|
369
|
+
if not (Tlimits[0] <= Tkin <= Tlimits[1]):
|
|
370
|
+
raise ValueError("Tkin outside bounds")
|
|
368
371
|
output = {}
|
|
369
372
|
Tkin_data = self.Tkin_data[collider]
|
|
370
373
|
j = np.searchsorted(Tkin_data, Tkin, side="left")
|
|
@@ -66,6 +66,7 @@ class Source:
|
|
|
66
66
|
geometry,
|
|
67
67
|
line_profile_type,
|
|
68
68
|
width_v,
|
|
69
|
+
use_datafile_frequencies=True,
|
|
69
70
|
use_Ng_acceleration=True,
|
|
70
71
|
treat_line_overlap=False,
|
|
71
72
|
warn_negative_tau=True,
|
|
@@ -99,6 +100,14 @@ class Source:
|
|
|
99
100
|
we can also say that width_v=dv/dr*2*R. For "LVG slab", width_v=dv/dz*Z
|
|
100
101
|
where Z is the depth of the slab and dv/dz the constant velocity
|
|
101
102
|
gradient of the slab.
|
|
103
|
+
use_datafile_frequencies (bool): whether to use the frequencies read from the LAMDA
|
|
104
|
+
file (if False, will use Delta_E/h). Defaults to True. In principle, the
|
|
105
|
+
frequencies in a LAMDA-formatted file are only for informational purposes.
|
|
106
|
+
However, they are sometimes more accurate than Delta_E/h, where Delta_E is
|
|
107
|
+
the energy difference between the two levels of the transition, as computed
|
|
108
|
+
from the level energies in the file. Using the frequencies can have
|
|
109
|
+
advantages especially if one models spectra. But the frequencies and level
|
|
110
|
+
energies in the files are not always fully consistent.
|
|
102
111
|
use_Ng_acceleration (:obj:`bool`): Whether to use Ng acceleration. Defaults
|
|
103
112
|
to True.
|
|
104
113
|
treat_line_overlap (:obj:`bool`): Whether to treat the overlap of emission
|
|
@@ -116,13 +125,15 @@ class Source:
|
|
|
116
125
|
by general user. Defaults to False.
|
|
117
126
|
|
|
118
127
|
"""
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
if width_v >= 10000 * constants.kilo:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
"assumption of small nu0/Delta_nu for flux calculation not satisfied"
|
|
131
|
+
)
|
|
122
132
|
self.emitting_molecule = EmittingMolecule(
|
|
123
133
|
datafilepath=datafilepath,
|
|
124
134
|
line_profile_type=line_profile_type,
|
|
125
135
|
width_v=width_v,
|
|
136
|
+
use_datafile_frequencies=use_datafile_frequencies,
|
|
126
137
|
)
|
|
127
138
|
self.treat_line_overlap = treat_line_overlap
|
|
128
139
|
self.geometry_name = geometry
|
|
@@ -264,9 +275,10 @@ class Source:
|
|
|
264
275
|
"tau_dust": tau_dust,
|
|
265
276
|
}
|
|
266
277
|
for p_name, p in params.items():
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
278
|
+
if p is None:
|
|
279
|
+
raise ValueError(
|
|
280
|
+
f"for initial setup, all params need to be defined; {p_name} missing"
|
|
281
|
+
)
|
|
270
282
|
self.rate_equations = rate_equations.RateEquations(
|
|
271
283
|
molecule=self.emitting_molecule,
|
|
272
284
|
treat_line_overlap=self.treat_line_overlap,
|
|
@@ -418,13 +430,13 @@ class Source:
|
|
|
418
430
|
self.emitting_molecule.rad_transitions[i]
|
|
419
431
|
for i in negative_tau_transition_indices
|
|
420
432
|
]
|
|
421
|
-
|
|
433
|
+
warn_message = "negative optical depth!"
|
|
422
434
|
for i, trans in zip(
|
|
423
435
|
negative_tau_transition_indices, negative_tau_transitions
|
|
424
436
|
):
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
437
|
+
warn_message += f" {trans.name}: tau_nu0 = {self.tau_nu0_individual_transitions[i]:.3g};"
|
|
438
|
+
warn_message = warn_message[:-1]
|
|
439
|
+
warnings.warn(warn_message)
|
|
428
440
|
self.level_pop = level_pop
|
|
429
441
|
self.Tex = self.emitting_molecule.get_Tex(level_pop)
|
|
430
442
|
self.intensity_calculator = intensity.IntensityCalculator(
|
|
@@ -684,7 +696,7 @@ class Source:
|
|
|
684
696
|
for i, line in enumerate(self.emitting_molecule.rad_transitions):
|
|
685
697
|
output = (
|
|
686
698
|
f"{line.up.index:>4d} {line.low.index:>4d} "
|
|
687
|
-
+ f"{line.nu0/constants.giga:>14.6f} {self.Tex[i]:>10.2f} "
|
|
699
|
+
+ f"{line.line_profile.nu0/constants.giga:>14.6f} {self.Tex[i]:>10.2f} "
|
|
688
700
|
+ f"{self.level_pop[line.low.index]:>14g} "
|
|
689
701
|
+ f"{self.level_pop[line.up.index]:>14g} "
|
|
690
702
|
+ f"{self.tau_nu0_individual_transitions[i]:>14g}"
|
pythonradex/rate_equations.py
CHANGED
|
@@ -65,9 +65,10 @@ class RateEquations:
|
|
|
65
65
|
|
|
66
66
|
def assign_func_nu(self, func_name, argument):
|
|
67
67
|
if isinstance(argument, numbers.Number):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
if argument < 0:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
"ext_background, T_dust or tau_dust cannot be negative"
|
|
71
|
+
)
|
|
71
72
|
setattr(self, func_name, self.generate_constant_func(const_value=argument))
|
|
72
73
|
else:
|
|
73
74
|
setattr(self, func_name, argument)
|
|
@@ -390,8 +391,9 @@ class RateEquations:
|
|
|
390
391
|
Gamma = self.GammaR(level_population=level_population) + self.GammaC
|
|
391
392
|
Gamma[0, :] = np.ones(self.molecule.n_levels)
|
|
392
393
|
fractional_population = np.linalg.solve(Gamma, b=self.b)
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
394
|
+
if np.any(fractional_population < 0):
|
|
395
|
+
raise RuntimeError(
|
|
396
|
+
"negative level population, potentially due to high column"
|
|
397
|
+
+ "density and/or low collider density"
|
|
398
|
+
)
|
|
397
399
|
return fractional_population
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: pythonradex
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: 1D non-LTE radiative transfer
|
|
5
5
|
Project-URL: Documentation, https://pythonradex.readthedocs.io/en/latest/
|
|
6
6
|
Project-URL: Issues, https://github.com/gica3618/pythonradex/issues
|
|
@@ -9,12 +9,14 @@ Author-email: Gianni Cataldi <cataldi.gia@gmail.com>
|
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE.txt
|
|
11
11
|
Keywords: non-LTE,radex,radiative transfer
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Programming Language :: Python
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
20
|
Requires-Python: >=3.10
|
|
19
21
|
Requires-Dist: numba>=0.61.0
|
|
20
22
|
Requires-Dist: numpy>=2.0
|
|
@@ -27,6 +29,7 @@ Requires-Dist: nbsphinx; extra == 'dev'
|
|
|
27
29
|
Requires-Dist: pandas; extra == 'dev'
|
|
28
30
|
Requires-Dist: pytest; extra == 'dev'
|
|
29
31
|
Requires-Dist: sphinx; extra == 'dev'
|
|
32
|
+
Requires-Dist: sphinx-rtd-theme; extra == 'dev'
|
|
30
33
|
Provides-Extra: notebooks
|
|
31
34
|
Requires-Dist: jupyter; extra == 'notebooks'
|
|
32
35
|
Requires-Dist: matplotlib; extra == 'notebooks'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
pythonradex/LAMDA_file.py,sha256=oVUVYNjuJl3ZMBd_T8dknWb0WExL12KuZbYsh1ZMalo,6716
|
|
2
|
+
pythonradex/__init__.py,sha256=iLQfJx9dbUoHmti29jtlTNopYzTdS7O1mBbZfb0Kin0,223
|
|
3
|
+
pythonradex/atomic_transition.py,sha256=1WSQsoxqkuv7HQ21xBe9uZ-I8fXNMZXX-31ztGJ0U7s,16341
|
|
4
|
+
pythonradex/escape_probability.py,sha256=UCs0Ec4AGEN92uN9A79cuFsapa7fUFGKD8tNtODqrV0,5608
|
|
5
|
+
pythonradex/escape_probability_functions.py,sha256=oLoY8h8zXqpdz0cYq3Cc7TFKVV1Lqa6Si6pGd4xopE4,9419
|
|
6
|
+
pythonradex/helpers.py,sha256=IY_EUtvMA12uuQxwFz-CZNEPuQqPBLl7xl7FVb5-w6k,3285
|
|
7
|
+
pythonradex/intensity.py,sha256=6oOltX6-h_6QsIr5jvscN0yCN_JsTpwXm-3uTmsTX2M,12199
|
|
8
|
+
pythonradex/molecule.py,sha256=hMZqidSXIIGYOx_zqE7Ccylsuo6718GOESkI3Mbk5B8,17489
|
|
9
|
+
pythonradex/radiative_transfer.py,sha256=DvKyRss9ByDX4sXG5PSV112ACOK4GYmc9gpTWmST-Es,34012
|
|
10
|
+
pythonradex/rate_equations.py,sha256=x8CYB6O7ErYD65JiBiBQe4XqTMySSowqAijcCCQU9bk,15410
|
|
11
|
+
pythonradex-3.0.0.dist-info/METADATA,sha256=R7V5zXpP_g669YRlnfYxZeFRKfnABFY6SmtdYLyPmTw,2706
|
|
12
|
+
pythonradex-3.0.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
13
|
+
pythonradex-3.0.0.dist-info/licenses/LICENSE.txt,sha256=biAf-Gv0h8Xr2r-CuwVZJsJKnj6WCL6KJfof8mz-M0U,1071
|
|
14
|
+
pythonradex-3.0.0.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
pythonradex/LAMDA_file.py,sha256=Mk_CM5MZTpd-TsirfhkGpv_XvXbPk25iAMB0yeyGcxU,6816
|
|
2
|
-
pythonradex/__init__.py,sha256=iLQfJx9dbUoHmti29jtlTNopYzTdS7O1mBbZfb0Kin0,223
|
|
3
|
-
pythonradex/atomic_transition.py,sha256=8fTmDZ4uSiHT5fHa_ANVqsdZDMNVn7q9eJUxGdr1qOE,14394
|
|
4
|
-
pythonradex/escape_probability.py,sha256=FaK-m2rQUccsr7zwnupPPWBfHItCW_Sj3I61TE1ZgJU,5559
|
|
5
|
-
pythonradex/escape_probability_functions.py,sha256=WNFgF66N8e8SajfJJkwRHxtMDhq7aGC_lOD5kcl0Q0A,9175
|
|
6
|
-
pythonradex/helpers.py,sha256=kBSdLgsoh7wx80veLBtPx-nUyeRWMDdlaTm_LB7CW-I,3210
|
|
7
|
-
pythonradex/intensity.py,sha256=poBcfRqtVURE-UGUI0x-9aIWbWhaH1xpQC0q5ET2Skk,11987
|
|
8
|
-
pythonradex/molecule.py,sha256=4e2-1L8QHr4TavCUnNwolhwjMi6-AO98G6J_tFnSt5o,17658
|
|
9
|
-
pythonradex/radiative_transfer.py,sha256=3tyC_e4CiLIYbaX3IKG1IdclAcKGYT7DyJZaMp_VNJ0,33065
|
|
10
|
-
pythonradex/rate_equations.py,sha256=mFLO6GCT6lADg6niGcVwTamN9gPJXAwgs75o86zjnYE,15338
|
|
11
|
-
pythonradex-2.0.1.dist-info/METADATA,sha256=DdNEF-Ld6Jd10S8FDIBaeugUr54OEfeC85JGFKVOfLg,2543
|
|
12
|
-
pythonradex-2.0.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
13
|
-
pythonradex-2.0.1.dist-info/licenses/LICENSE.txt,sha256=biAf-Gv0h8Xr2r-CuwVZJsJKnj6WCL6KJfof8mz-M0U,1071
|
|
14
|
-
pythonradex-2.0.1.dist-info/RECORD,,
|
|
File without changes
|