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,42 @@
1
+ from os import path
2
+ from typing import List
3
+
4
+ from numpy.core._multiarray_umath import __cpu_features__
5
+
6
+ # NOTE(cmo): These are added in reverse order of preference (due to width), i.e.
7
+ # try to use the key furthest down the list.
8
+ LwSimdImplsAndFlags = {
9
+ 'SSE2': ['SSE2'],
10
+ 'AVX2FMA': ['AVX2', 'FMA3'],
11
+ 'AVX512': ['AVX512F', 'AVX512DQ']
12
+ }
13
+
14
+ def get_available_simd_suffixes() -> List[str]:
15
+ '''
16
+ Verifies the necessary flags against the features NumPy indicates are
17
+ available, and returns a list of available LightweaverSimdImpls
18
+ '''
19
+ validExts = []
20
+ for impl, flags in LwSimdImplsAndFlags.items():
21
+ if all(__cpu_features__[flag] for flag in flags):
22
+ validExts.append(impl)
23
+ return validExts
24
+
25
+ def filter_usable_simd_impls(implLibs: List[str]) -> List[str]:
26
+ '''
27
+ Filter a list of SimdImpl library names, returning those that are usable on
28
+ the current machine.
29
+ '''
30
+ usableImpls = get_available_simd_suffixes()
31
+ result = []
32
+ for lib in implLibs:
33
+ _, libName = path.split(lib)
34
+ # NOTE(cmo): A lib name is expected to be of the form
35
+ # SimdImpl_{SimdType}.{pep3149}.so. So we split at the underscore and
36
+ # check what the name starts with.
37
+ nameEnd = libName.split('_')[1]
38
+ for simdType in usableImpls:
39
+ if nameEnd.startswith(simdType):
40
+ result.append(lib)
41
+ break
42
+ return result
lightweaver/utils.py ADDED
@@ -0,0 +1,509 @@
1
+ import importlib
2
+ import os
3
+ from dataclasses import dataclass
4
+ from enum import Enum, auto
5
+ from os import path
6
+ from typing import TYPE_CHECKING, Optional, Sequence, Tuple, Union
7
+
8
+ import numpy as np
9
+ from astropy import units
10
+ from numba import njit
11
+ from scipy import special
12
+ from scipy.integrate import trapezoid
13
+ from weno4 import weno4
14
+
15
+ import lightweaver.constants as C
16
+ from .simd_management import filter_usable_simd_impls
17
+
18
+ if TYPE_CHECKING:
19
+ from .atomic_model import AtomicLine, AtomicModel
20
+
21
+ @dataclass
22
+ class NgOptions:
23
+ '''
24
+ Container for the options related to Ng acceleration.
25
+ Attributes
26
+ ----------
27
+ Norder : int, optional
28
+ The order of the acceleration scheme to use (default: 0, i.e. none).
29
+ Nperiod : int, optional
30
+ The number of iterations to run between accelerations.
31
+ Ndelay : int, optional
32
+ The number of iterations to run before starting acceleration.
33
+ threshold : float, optional
34
+ The threshold which all historic iterations (Norder+2) must be below for
35
+ acceleration. Default, 5e-2
36
+ lowerThreshold : float, optional
37
+ The threshold below which to disable Ng acceleration. Default, 2e-4
38
+ '''
39
+ Norder: int = 0
40
+ Nperiod: int = 0
41
+ Ndelay: int = 0
42
+ threshold: float = 5e-2
43
+ lowerThreshold: float = 2e-4
44
+
45
+
46
+ class InitialSolution(Enum):
47
+ '''
48
+ Initial solutions to use for atomic populations, either LTE, Zero
49
+ radiation (not yet supported), or second order escape probability.
50
+ '''
51
+ Lte = auto()
52
+ Zero = auto()
53
+ EscapeProbability = auto()
54
+
55
+ def voigt_H(a, v):
56
+ '''
57
+ Scalar Voigt profile.
58
+
59
+ Parameters
60
+ ----------
61
+ a : float or array-like
62
+ The a damping parameter to be used in the Voigt profile.
63
+ v : float or array-like
64
+ The position in the line profile in Doppler units.
65
+ '''
66
+ z = (v + 1j * a)
67
+ return special.wofz(z).real
68
+
69
+ @njit
70
+ def planck(temp, wav):
71
+ '''
72
+ Planck black-body function B_nu(T) from wavelength.
73
+
74
+ Parameters
75
+ ----------
76
+ temp : float or array-like
77
+ Temperature [K]
78
+ wav : float or array-like
79
+ The wavelength at which to compute B_nu [nm].
80
+
81
+ Returns
82
+ -------
83
+ result : float or array-like
84
+ B_nu(T)
85
+ '''
86
+ hc_Tkla = C.HC / (C.KBoltzmann * C.NM_TO_M * wav) / temp
87
+ twohnu3_c2 = (2.0 * C.HC) / (C.NM_TO_M * wav)**3
88
+
89
+ return twohnu3_c2 / (np.exp(hc_Tkla) - 1.0)
90
+
91
+ def gaunt_bf(wvl, nEff, charge) -> float:
92
+ '''
93
+ Gaunt factor for bound-free transitions, from Seaton (1960), Rep. Prog.
94
+ Phys. 23, 313, as used in RH.
95
+
96
+ Parameters
97
+ ----------
98
+ wvl : float or array-like
99
+ The wavelength at which to compute the Gaunt factor [nm].
100
+ nEff : float
101
+ Principal quantum number.
102
+ charge : float
103
+ Charge of free state.
104
+
105
+ Returns
106
+ -------
107
+ result : float or array-like
108
+ Gaunt factor for bound-free transitions.
109
+ '''
110
+ # /* --- M. J. Seaton (1960), Rep. Prog. Phys. 23, 313 -- ----------- */
111
+ # Copied from RH, ensuring vectorisation support
112
+ x = C.HC / (wvl * C.NM_TO_M) / (C.ERydberg * charge**2)
113
+ x3 = x**(1.0/3.0)
114
+ nsqx = 1.0 / (nEff**2 *x)
115
+
116
+ return 1.0 + 0.1728 * x3 * (1.0 - 2.0 * nsqx) - 0.0496 * x3**2 \
117
+ * (1.0 - (1.0 - nsqx) * (2.0 / 3.0) * nsqx)
118
+
119
+ class ConvergenceError(Exception):
120
+ '''
121
+ Raised by some iteration schemes, can also be used in user code.
122
+ '''
123
+ pass
124
+
125
+ class ExplodingMatrixError(Exception):
126
+ '''
127
+ Raised by the linear system matrix solver in the case of unsolvable
128
+ systems.
129
+ '''
130
+ pass
131
+
132
+ def get_code_location():
133
+ '''
134
+ Returns the directory containing the Lightweaver Python source.
135
+ '''
136
+ directory, _ = path.split(path.realpath(__file__))
137
+ return directory
138
+
139
+ def get_data_path():
140
+ '''
141
+ Returns the location of the Lightweaver support data.
142
+ '''
143
+ return path.join(get_code_location(), 'Data') + path.sep
144
+
145
+ def get_default_molecule_path():
146
+ '''
147
+ Returns the location of the default molecules taken from RH.
148
+ '''
149
+ return path.join(get_code_location(), 'Data', 'DefaultMolecules') + path.sep
150
+
151
+ def filter_fs_iter_libs(libs: Sequence[str], exts: Sequence[str]) -> Sequence[str]:
152
+ '''
153
+ Filter a list of libraries (e.g. SimdImpl_{SimdType}.{pep3149}.so) with a
154
+ valid collection of extensions. (As .so is a valid extension, we can't just
155
+ check the end of the file name).
156
+ '''
157
+ result = []
158
+ for libName in libs:
159
+ libPrefix = libName.split('.')[0]
160
+ for ext in exts:
161
+ if libPrefix + ext == libName:
162
+ result.append(libName)
163
+ return result
164
+
165
+ def get_fs_iter_libs() -> Sequence[str]:
166
+ '''
167
+ Returns the paths of the default FsIterationScheme libraries usable on the
168
+ current machine (due to available SIMD optimisations -- these are detected by NumPy).
169
+ '''
170
+ validExts = importlib.machinery.EXTENSION_SUFFIXES
171
+ iterSchemesDir = path.join(get_code_location(), 'DefaultIterSchemes')
172
+ schemes = [path.join(iterSchemesDir, x) for x in
173
+ filter_usable_simd_impls(
174
+ filter_fs_iter_libs(os.listdir(iterSchemesDir), validExts)
175
+ )]
176
+ return schemes
177
+
178
+ def vac_to_air(wavelength: np.ndarray) -> np.ndarray:
179
+ '''
180
+ Convert vacuum wavelength to air.
181
+
182
+ Parameters
183
+ ----------
184
+ wavelength : float or array-like or astropy.Quantity
185
+ If no units then the wavelength is assumed to be in [nm], otherwise the
186
+ provided units are used.
187
+
188
+ Returns
189
+ -------
190
+ result : float or array-like or astropy.Quantity
191
+ The converted wavelength in [nm].
192
+ '''
193
+ # NOTE(cmo): Moved this import here as it's very slow
194
+ ### HACK
195
+ from specutils.utils.wcs_utils import vac_to_air as spec_vac_to_air
196
+ if not isinstance(wavelength, units.quantity.Quantity):
197
+ return spec_vac_to_air(wavelength << units.nm, method='edlen1966').value
198
+ return spec_vac_to_air(wavelength, method='edlen1966')
199
+
200
+ def air_to_vac(wavelength: np.ndarray) -> np.ndarray:
201
+ '''
202
+ Convert air wavelength to vacuum.
203
+
204
+ Parameters
205
+ ----------
206
+ wavelength : float or array-like or astropy.Quantity
207
+ If no units then the wavelength is assumed to be in [nm], otherwise the
208
+ provided units are used.
209
+
210
+ Returns
211
+ -------
212
+ result : float or array-like or astropy.Quantity
213
+ The converted wavelength in [nm].
214
+ '''
215
+ # NOTE(cmo): Moved this import here as it's very slow
216
+ ### HACK
217
+ from specutils.utils.wcs_utils import air_to_vac as spec_air_to_vac
218
+ if not isinstance(wavelength, units.quantity.Quantity):
219
+ return spec_air_to_vac(wavelength << units.nm, scheme='iteration',
220
+ method='edlen1966').value
221
+ return spec_air_to_vac(wavelength, scheme='iteration',
222
+ method='edlen1966')
223
+
224
+ def convert_specific_intensity(wavelength: np.ndarray,
225
+ specInt: np.ndarray,
226
+ outUnits) -> units.quantity.Quantity:
227
+ '''
228
+ Convert a specific intensity between different units.
229
+
230
+ Parameters
231
+ ----------
232
+ wavelength : np.ndarray or astropy.Quantity
233
+ If no units are provided then this is assumed to be in nm.
234
+ specInt : np.ndarray or astropy.Quantity
235
+ If no units are provided then this is assumed to be in J/s/m2/sr/Hz,
236
+ the default for Lightweaver.
237
+ outUnits : str or astropy.Unit
238
+ The units to convert specInt to e.g. 'erg/s/cm2/sr/Angstrom'
239
+
240
+ Returns
241
+ -------
242
+ result : astropy.Quantity
243
+ specInt converted to the desired units.
244
+ '''
245
+ if not isinstance(wavelength, units.Quantity):
246
+ wavelength = wavelength << units.nm
247
+
248
+ if not isinstance(specInt, units.Quantity):
249
+ specInt = specInt << units.J / units.s / units.m**2 / units.sr / units.Hz
250
+
251
+ return specInt.to(outUnits, equivalencies=units.spectral_density(wavelength))
252
+
253
+ class CrswIterator:
254
+ '''
255
+ Basic iterator to be used for controlling the scale of the collisional
256
+ radiative switching (of Hummer & Voels) multiplicative paramter. Can be
257
+ inherited to provide different behaviour. By default starts from a factor
258
+ of 1e3 and scales this factor by 0.1**(1.0/value) each iteration, as is
259
+ the default behaviour in RH.
260
+ '''
261
+ def __init__(self, initVal=1e3):
262
+ self.val = initVal
263
+
264
+ def __call__(self):
265
+ self.val = max(1.0, self.val * 0.1**(1.0/self.val))
266
+ return self.val
267
+
268
+ class UnityCrswIterator(CrswIterator):
269
+ '''
270
+ A specific case representing no collisional radiative switching (i.e.
271
+ parameter always 1).
272
+ '''
273
+ def __init__(self):
274
+ super().__init__(1.0)
275
+
276
+ def __call__(self):
277
+ return self.val
278
+
279
+ def sequence_repr(x: Sequence) -> str:
280
+ '''
281
+ Uniform representation of arrays and lists as lists for use in
282
+ round-tripping AtomicModels.
283
+ '''
284
+ if isinstance(x, np.ndarray):
285
+ return repr(x.tolist())
286
+
287
+ return repr(x)
288
+
289
+ def view_flatten(x: np.ndarray) -> np.ndarray:
290
+ '''
291
+ Return a flattened view over an array, will raise an Exception if it
292
+ cannot be represented as a flat array without copy.
293
+ '''
294
+ y = x.view()
295
+ y.shape = (x.size,)
296
+ return y
297
+
298
+ def check_shape_exception(a: np.ndarray, shape: Union[int, Tuple[int]],
299
+ ndim: Optional[int]=1, name: Optional[str]='array'):
300
+ '''
301
+ Ensure that an array matches the expected number of dimensions and shape.
302
+ Raise a ValueError if not, quoting the array's name (if provided)
303
+
304
+ Parameters
305
+ ----------
306
+ a : np.ndarray
307
+ The array to verify.
308
+ shape : int or Tuple[int]
309
+ The length (for a 1D array), or shape for a multi-dimensional array.
310
+ ndim : int, optional
311
+ The expected number of dimensions (default: 1)
312
+ name : str, optional
313
+ The name to in any exception (default: array)
314
+
315
+ '''
316
+ if isinstance(shape, int):
317
+ shape = (shape,)
318
+
319
+ if a.ndim != ndim:
320
+ raise ValueError(f'Array ({name}) does not have the expected number '
321
+ f'of dimensions: {ndim} (got: {a.ndim}).')
322
+
323
+ if a.shape != shape:
324
+ raise ValueError(f'Array ({name}) does not have the expected shape: '
325
+ f'{shape} (got: {a.shape}).')
326
+
327
+ def compute_radiative_losses(ctx) -> np.ndarray:
328
+ '''
329
+ Compute the radiative gains and losses for each wavelength in the grid
330
+ used by the context. Units of J/s/m3/Hz. Includes
331
+ background/contributions from overlapping lines. Convention of positive
332
+ => radiative gain, negative => radiative loss.
333
+
334
+ Parameters
335
+ ----------
336
+ ctx : Context
337
+ A context with full depth-dependent data (i.e. ctx.depthData.fill =
338
+ True set before the most recent formal solution).
339
+
340
+ Returns
341
+ -------
342
+ loss : np.ndarray
343
+ The radiative gains losses for each depth and wavelength in the
344
+ simulation.
345
+ '''
346
+ atmos = ctx.kwargs['atmos']
347
+
348
+ chiTot = ctx.depthData.chi
349
+ S = (ctx.depthData.eta + (ctx.background.sca * ctx.spect.J)[:, None, None, :]) / chiTot
350
+ Idepth = ctx.depthData.I
351
+ loss = ((chiTot * (S - Idepth)) * 0.5).sum(axis=2).transpose(0, 2, 1) @ atmos.wmu
352
+
353
+ return loss
354
+
355
+
356
+ def integrate_line_losses(ctx, loss : np.ndarray,
357
+ lines : Union['AtomicLine', Sequence['AtomicLine']],
358
+ extendGridNm: float=0.0) -> Union[Sequence[np.ndarray], np.ndarray]:
359
+ '''
360
+ Integrate the radiative gains and losses over the band associated with a
361
+ line or list of lines. Units of J/s/m3. Includes background/contributions
362
+ from overlapping lines. Convention of positive => radiative gain,
363
+ negative => radiative loss.
364
+
365
+ Parameters
366
+ ----------
367
+ ctx : Context
368
+ A context with the full depth-dependent data (i.e. ctx.depthData.fill
369
+ = True set before the most recent formal solution).
370
+ loss : np.ndarray
371
+ The radiative gains/losses for each wavelength and depth computed by
372
+ `compute_radiative_losses`.
373
+ lines : AtomicLine or list of AtomicLine
374
+ The lines for which to compute losses.
375
+ extendGridNm : float, optional
376
+ Set this to a positive value to add an additional point at each end
377
+ of the integration range to include a wider continuum/far-wing
378
+ contribution. Units: nm, default: 0.0.
379
+
380
+ Returns
381
+ -------
382
+ linesLosses : array or list of array
383
+ The radiative gain/losses per line at each depth.
384
+ '''
385
+ from .atomic_model import AtomicLine
386
+
387
+ if isinstance(lines, AtomicLine):
388
+ lines = [lines]
389
+
390
+ spect = ctx.kwargs['spect']
391
+
392
+ lineLosses = []
393
+ for line in lines:
394
+ transId = line.transId
395
+ grid = spect.transWavelengths[transId]
396
+ blueIdx = spect.blueIdx[transId]
397
+ blue = ctx.spect.wavelength[blueIdx]
398
+ redIdx = blueIdx + grid.shape[0]
399
+ red = ctx.spect.wavelength[redIdx-1]
400
+
401
+ if extendGridNm != 0.0:
402
+ wav = np.concatenate(((blue-extendGridNm,),
403
+ ctx.spect.wavelength[blueIdx:redIdx],
404
+ (red+extendGridNm,)))
405
+ else:
406
+ wav = ctx.spect.wavelength[blueIdx:redIdx]
407
+
408
+ # NOTE(cmo): There's a sneaky transpose going on here for the integration
409
+ lineLoss = np.zeros((loss.shape[1], wav.shape[0]))
410
+ for k in range(loss.shape[1]):
411
+ lineLoss[k, :] = weno4(wav, ctx.spect.wavelength, loss[:, k])
412
+ lineLosses.append(trapezoid(lineLoss,
413
+ (wav << units.nm).to(units.Hz,
414
+ equivalencies=units.spectral()).value)
415
+ )
416
+ return lineLosses[0] if len(lineLosses) == 1 else lineLosses
417
+
418
+
419
+ def compute_contribution_fn(ctx, mu : int=-1, outgoing : bool=True) -> np.ndarray:
420
+ '''
421
+ Computes the contribution function for all wavelengths in the simulation,
422
+ for a chosen angular index.
423
+
424
+ Parameters
425
+ ----------
426
+ ctx : Context
427
+ A context with the full depth-dependent data (i.e. ctx.depthData.fill
428
+ = True set before the most recent formal solution).
429
+ mu : Optional[int]
430
+ The angular index to use (corresponding to the order of the angular
431
+ quadratures in atmosphere), default: -1.
432
+ outgoing : Optional[bool]
433
+ Whether to compute the contribution for outgoing or incoming
434
+ radiation (wrt to the atmosphere). Default: outgoing==True, i.e. to
435
+ observer.
436
+
437
+ Returns
438
+ -------
439
+ cfn : np.ndarray
440
+ The contribution function in terms of depth and wavelength.
441
+ '''
442
+ upDown = 1 if outgoing else 0
443
+ tau = np.zeros_like(ctx.depthData.chi[:, mu, upDown, :])
444
+ chi = ctx.depthData.chi
445
+ atmos = ctx.kwargs['atmos']
446
+
447
+ # NOTE(cmo): Compute tau for all wavelengths
448
+ tau[:, 0] = 1e-20
449
+ for k in range(1, tau.shape[1]):
450
+ tau[:, k] = tau[:, k-1] + 0.5 * (chi[:, mu, upDown, k] + chi[:, mu, upDown, k-1]) \
451
+ * (atmos.height[k-1] - atmos.height[k])
452
+
453
+ # NOTE(cmo): Source function.
454
+ Sfn = ((ctx.depthData.eta
455
+ + (ctx.background.sca * ctx.spect.J)[:, None, None, :])
456
+ / chi)
457
+
458
+ # NOTE(cmo): Contribution function for all wavelengths.
459
+ cfn = ctx.depthData.chi[:, mu, upDown, :] / atmos.muz[mu] \
460
+ * np.exp(-tau / atmos.muz[mu]) * Sfn[:, mu, upDown, :]
461
+
462
+ return cfn
463
+
464
+
465
+ def compute_wavelength_edges(ctx) -> np.ndarray:
466
+ '''
467
+ Compute the edges of the wavelength bins associated with the wavelength
468
+ array used in a simulation, typically used in conjunction with a plot
469
+ using pcolormesh.
470
+
471
+ Parameters
472
+ ----------
473
+ ctx : Context
474
+ The context from which to construct the wavelength edges.
475
+
476
+ Returns
477
+ -------
478
+ wlEdges : np.ndarray
479
+ The edges of the wavelength bins.
480
+ '''
481
+ wav = ctx.spect.wavelength
482
+ wlEdges = np.concatenate(((wav[0] - 0.5 * (wav[1] - wav[0]),),
483
+ 0.5 * (wav[1:] + wav[:-1]),
484
+ (wav[-1] + 0.5 * (wav[-1] - wav[-2]),)
485
+ ))
486
+ return wlEdges
487
+
488
+
489
+ def compute_height_edges(ctx) -> np.ndarray:
490
+ '''
491
+ Compute the edges of the height bins associated with the stratified
492
+ altitude array used in a simulation, typically used in conjunction with a
493
+ plot using pcolormesh.
494
+
495
+ Parameters
496
+ ----------
497
+ ctx : Context
498
+ The context from which to construct the height edges.
499
+
500
+ Returns
501
+ -------
502
+ heightEdges : np.ndarray
503
+ The edges of the height bins.
504
+ '''
505
+ atmos = ctx.kwargs['atmos']
506
+ heightEdges = np.concatenate(((atmos.height[0] + 0.5 * (atmos.height[0] - atmos.height[1]),),
507
+ 0.5 * (atmos.height[1:] + atmos.height[:-1]),
508
+ (atmos.height[-1] - 0.5 * (atmos.height[-2] - atmos.height[-1]),)))
509
+ return heightEdges
lightweaver/version.py ADDED
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.16.1'
32
+ __version_tuple__ = version_tuple = (0, 16, 1)
33
+
34
+ __commit_id__ = commit_id = 'g11f47573b'