lightweaver 0.15.0__cp310-cp310-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.

Potentially problematic release.


This version of lightweaver might be problematic. Click here for more details.

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-310-x86_64-linux-gnu.so +0 -0
  37. lightweaver/DefaultIterSchemes/SimdImpl_AVX512.cpython-310-x86_64-linux-gnu.so +0 -0
  38. lightweaver/DefaultIterSchemes/SimdImpl_SSE2.cpython-310-x86_64-linux-gnu.so +0 -0
  39. lightweaver/LwCompiled.cpython-310-x86_64-linux-gnu.so +0 -0
  40. lightweaver/__init__.py +33 -0
  41. lightweaver/atmosphere.py +1640 -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 +504 -0
  62. lightweaver/version.py +34 -0
  63. lightweaver/wittmann.py +1375 -0
  64. lightweaver/zeeman.py +157 -0
  65. lightweaver-0.15.0.dist-info/METADATA +81 -0
  66. lightweaver-0.15.0.dist-info/RECORD +69 -0
  67. lightweaver-0.15.0.dist-info/WHEEL +6 -0
  68. lightweaver-0.15.0.dist-info/licenses/LICENSE +21 -0
  69. lightweaver-0.15.0.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,504 @@
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
+ return spec_vac_to_air(wavelength << units.nm, method='edlen1966').value
197
+
198
+ def air_to_vac(wavelength: np.ndarray) -> np.ndarray:
199
+ '''
200
+ Convert air wavelength to vacuum.
201
+
202
+ Parameters
203
+ ----------
204
+ wavelength : float or array-like or astropy.Quantity
205
+ If no units then the wavelength is assumed to be in [nm], otherwise the
206
+ provided units are used.
207
+
208
+ Returns
209
+ -------
210
+ result : float or array-like or astropy.Quantity
211
+ The converted wavelength in [nm].
212
+ '''
213
+ # NOTE(cmo): Moved this import here as it's very slow
214
+ ### HACK
215
+ from specutils.utils.wcs_utils import air_to_vac as spec_air_to_vac
216
+ return spec_air_to_vac(wavelength << units.nm, scheme='iteration',
217
+ method='edlen1966').value
218
+
219
+ def convert_specific_intensity(wavelength: np.ndarray,
220
+ specInt: np.ndarray,
221
+ outUnits) -> units.quantity.Quantity:
222
+ '''
223
+ Convert a specific intensity between different units.
224
+
225
+ Parameters
226
+ ----------
227
+ wavelength : np.ndarray or astropy.Quantity
228
+ If no units are provided then this is assumed to be in nm.
229
+ specInt : np.ndarray or astropy.Quantity
230
+ If no units are provided then this is assumed to be in J/s/m2/sr/Hz,
231
+ the default for Lightweaver.
232
+ outUnits : str or astropy.Unit
233
+ The units to convert specInt to e.g. 'erg/s/cm2/sr/Angstrom'
234
+
235
+ Returns
236
+ -------
237
+ result : astropy.Quantity
238
+ specInt converted to the desired units.
239
+ '''
240
+ if not isinstance(wavelength, units.Quantity):
241
+ wavelength = wavelength << units.nm
242
+
243
+ if not isinstance(specInt, units.Quantity):
244
+ specInt = specInt << units.J / units.s / units.m**2 / units.sr / units.Hz
245
+
246
+ return specInt.to(outUnits, equivalencies=units.spectral_density(wavelength))
247
+
248
+ class CrswIterator:
249
+ '''
250
+ Basic iterator to be used for controlling the scale of the collisional
251
+ radiative switching (of Hummer & Voels) multiplicative paramter. Can be
252
+ inherited to provide different behaviour. By default starts from a factor
253
+ of 1e3 and scales this factor by 0.1**(1.0/value) each iteration, as is
254
+ the default behaviour in RH.
255
+ '''
256
+ def __init__(self, initVal=1e3):
257
+ self.val = initVal
258
+
259
+ def __call__(self):
260
+ self.val = max(1.0, self.val * 0.1**(1.0/self.val))
261
+ return self.val
262
+
263
+ class UnityCrswIterator(CrswIterator):
264
+ '''
265
+ A specific case representing no collisional radiative switching (i.e.
266
+ parameter always 1).
267
+ '''
268
+ def __init__(self):
269
+ super().__init__(1.0)
270
+
271
+ def __call__(self):
272
+ return self.val
273
+
274
+ def sequence_repr(x: Sequence) -> str:
275
+ '''
276
+ Uniform representation of arrays and lists as lists for use in
277
+ round-tripping AtomicModels.
278
+ '''
279
+ if isinstance(x, np.ndarray):
280
+ return repr(x.tolist())
281
+
282
+ return repr(x)
283
+
284
+ def view_flatten(x: np.ndarray) -> np.ndarray:
285
+ '''
286
+ Return a flattened view over an array, will raise an Exception if it
287
+ cannot be represented as a flat array without copy.
288
+ '''
289
+ y = x.view()
290
+ y.shape = (x.size,)
291
+ return y
292
+
293
+ def check_shape_exception(a: np.ndarray, shape: Union[int, Tuple[int]],
294
+ ndim: Optional[int]=1, name: Optional[str]='array'):
295
+ '''
296
+ Ensure that an array matches the expected number of dimensions and shape.
297
+ Raise a ValueError if not, quoting the array's name (if provided)
298
+
299
+ Parameters
300
+ ----------
301
+ a : np.ndarray
302
+ The array to verify.
303
+ shape : int or Tuple[int]
304
+ The length (for a 1D array), or shape for a multi-dimensional array.
305
+ ndim : int, optional
306
+ The expected number of dimensions (default: 1)
307
+ name : str, optional
308
+ The name to in any exception (default: array)
309
+
310
+ '''
311
+ if isinstance(shape, int):
312
+ shape = (shape,)
313
+
314
+ if a.ndim != ndim:
315
+ raise ValueError(f'Array ({name}) does not have the expected number '
316
+ f'of dimensions: {ndim} (got: {a.ndim}).')
317
+
318
+ if a.shape != shape:
319
+ raise ValueError(f'Array ({name}) does not have the expected shape: '
320
+ f'{shape} (got: {a.shape}).')
321
+
322
+ def compute_radiative_losses(ctx) -> np.ndarray:
323
+ '''
324
+ Compute the radiative gains and losses for each wavelength in the grid
325
+ used by the context. Units of J/s/m3/Hz. Includes
326
+ background/contributions from overlapping lines. Convention of positive
327
+ => radiative gain, negative => radiative loss.
328
+
329
+ Parameters
330
+ ----------
331
+ ctx : Context
332
+ A context with full depth-dependent data (i.e. ctx.depthData.fill =
333
+ True set before the most recent formal solution).
334
+
335
+ Returns
336
+ -------
337
+ loss : np.ndarray
338
+ The radiative gains losses for each depth and wavelength in the
339
+ simulation.
340
+ '''
341
+ atmos = ctx.kwargs['atmos']
342
+
343
+ chiTot = ctx.depthData.chi
344
+ S = (ctx.depthData.eta + (ctx.background.sca * ctx.spect.J)[:, None, None, :]) / chiTot
345
+ Idepth = ctx.depthData.I
346
+ loss = ((chiTot * (S - Idepth)) * 0.5).sum(axis=2).transpose(0, 2, 1) @ atmos.wmu
347
+
348
+ return loss
349
+
350
+
351
+ def integrate_line_losses(ctx, loss : np.ndarray,
352
+ lines : Union['AtomicLine', Sequence['AtomicLine']],
353
+ extendGridNm: float=0.0) -> Union[Sequence[np.ndarray], np.ndarray]:
354
+ '''
355
+ Integrate the radiative gains and losses over the band associated with a
356
+ line or list of lines. Units of J/s/m3. Includes background/contributions
357
+ from overlapping lines. Convention of positive => radiative gain,
358
+ negative => radiative loss.
359
+
360
+ Parameters
361
+ ----------
362
+ ctx : Context
363
+ A context with the full depth-dependent data (i.e. ctx.depthData.fill
364
+ = True set before the most recent formal solution).
365
+ loss : np.ndarray
366
+ The radiative gains/losses for each wavelength and depth computed by
367
+ `compute_radiative_losses`.
368
+ lines : AtomicLine or list of AtomicLine
369
+ The lines for which to compute losses.
370
+ extendGridNm : float, optional
371
+ Set this to a positive value to add an additional point at each end
372
+ of the integration range to include a wider continuum/far-wing
373
+ contribution. Units: nm, default: 0.0.
374
+
375
+ Returns
376
+ -------
377
+ linesLosses : array or list of array
378
+ The radiative gain/losses per line at each depth.
379
+ '''
380
+ from .atomic_model import AtomicLine
381
+
382
+ if isinstance(lines, AtomicLine):
383
+ lines = [lines]
384
+
385
+ spect = ctx.kwargs['spect']
386
+
387
+ lineLosses = []
388
+ for line in lines:
389
+ transId = line.transId
390
+ grid = spect.transWavelengths[transId]
391
+ blueIdx = spect.blueIdx[transId]
392
+ blue = ctx.spect.wavelength[blueIdx]
393
+ redIdx = blueIdx + grid.shape[0]
394
+ red = ctx.spect.wavelength[redIdx-1]
395
+
396
+ if extendGridNm != 0.0:
397
+ wav = np.concatenate(((blue-extendGridNm,),
398
+ ctx.spect.wavelength[blueIdx:redIdx],
399
+ (red+extendGridNm,)))
400
+ else:
401
+ wav = ctx.spect.wavelength[blueIdx:redIdx]
402
+
403
+ # NOTE(cmo): There's a sneaky transpose going on here for the integration
404
+ lineLoss = np.zeros((loss.shape[1], wav.shape[0]))
405
+ for k in range(loss.shape[1]):
406
+ lineLoss[k, :] = weno4(wav, ctx.spect.wavelength, loss[:, k])
407
+ lineLosses.append(trapezoid(lineLoss,
408
+ (wav << units.nm).to(units.Hz,
409
+ equivalencies=units.spectral()).value)
410
+ )
411
+ return lineLosses[0] if len(lineLosses) == 1 else lineLosses
412
+
413
+
414
+ def compute_contribution_fn(ctx, mu : int=-1, outgoing : bool=True) -> np.ndarray:
415
+ '''
416
+ Computes the contribution function for all wavelengths in the simulation,
417
+ for a chosen angular index.
418
+
419
+ Parameters
420
+ ----------
421
+ ctx : Context
422
+ A context with the full depth-dependent data (i.e. ctx.depthData.fill
423
+ = True set before the most recent formal solution).
424
+ mu : Optional[int]
425
+ The angular index to use (corresponding to the order of the angular
426
+ quadratures in atmosphere), default: -1.
427
+ outgoing : Optional[bool]
428
+ Whether to compute the contribution for outgoing or incoming
429
+ radiation (wrt to the atmosphere). Default: outgoing==True, i.e. to
430
+ observer.
431
+
432
+ Returns
433
+ -------
434
+ cfn : np.ndarray
435
+ The contribution function in terms of depth and wavelength.
436
+ '''
437
+ upDown = 1 if outgoing else 0
438
+ tau = np.zeros_like(ctx.depthData.chi[:, mu, upDown, :])
439
+ chi = ctx.depthData.chi
440
+ atmos = ctx.kwargs['atmos']
441
+
442
+ # NOTE(cmo): Compute tau for all wavelengths
443
+ tau[:, 0] = 1e-20
444
+ for k in range(1, tau.shape[1]):
445
+ tau[:, k] = tau[:, k-1] + 0.5 * (chi[:, mu, upDown, k] + chi[:, mu, upDown, k-1]) \
446
+ * (atmos.height[k-1] - atmos.height[k])
447
+
448
+ # NOTE(cmo): Source function.
449
+ Sfn = ((ctx.depthData.eta
450
+ + (ctx.background.sca * ctx.spect.J)[:, None, None, :])
451
+ / chi)
452
+
453
+ # NOTE(cmo): Contribution function for all wavelengths.
454
+ cfn = ctx.depthData.chi[:, mu, upDown, :] / atmos.muz[mu] \
455
+ * np.exp(-tau / atmos.muz[mu]) * Sfn[:, mu, upDown, :]
456
+
457
+ return cfn
458
+
459
+
460
+ def compute_wavelength_edges(ctx) -> np.ndarray:
461
+ '''
462
+ Compute the edges of the wavelength bins associated with the wavelength
463
+ array used in a simulation, typically used in conjunction with a plot
464
+ using pcolormesh.
465
+
466
+ Parameters
467
+ ----------
468
+ ctx : Context
469
+ The context from which to construct the wavelength edges.
470
+
471
+ Returns
472
+ -------
473
+ wlEdges : np.ndarray
474
+ The edges of the wavelength bins.
475
+ '''
476
+ wav = ctx.spect.wavelength
477
+ wlEdges = np.concatenate(((wav[0] - 0.5 * (wav[1] - wav[0]),),
478
+ 0.5 * (wav[1:] + wav[:-1]),
479
+ (wav[-1] + 0.5 * (wav[-1] - wav[-2]),)
480
+ ))
481
+ return wlEdges
482
+
483
+
484
+ def compute_height_edges(ctx) -> np.ndarray:
485
+ '''
486
+ Compute the edges of the height bins associated with the stratified
487
+ altitude array used in a simulation, typically used in conjunction with a
488
+ plot using pcolormesh.
489
+
490
+ Parameters
491
+ ----------
492
+ ctx : Context
493
+ The context from which to construct the height edges.
494
+
495
+ Returns
496
+ -------
497
+ heightEdges : np.ndarray
498
+ The edges of the height bins.
499
+ '''
500
+ atmos = ctx.kwargs['atmos']
501
+ heightEdges = np.concatenate(((atmos.height[0] + 0.5 * (atmos.height[0] - atmos.height[1]),),
502
+ 0.5 * (atmos.height[1:] + atmos.height[:-1]),
503
+ (atmos.height[-1] - 0.5 * (atmos.height[-2] - atmos.height[-1]),)))
504
+ 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.15.0'
32
+ __version_tuple__ = version_tuple = (0, 15, 0)
33
+
34
+ __commit_id__ = commit_id = 'gd4bacfad7'