PythonicDISORT 1.6__tar.gz → 1.8__tar.gz
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.
- {pythonicdisort-1.6/src/PythonicDISORT.egg-info → pythonicdisort-1.8}/PKG-INFO +15 -12
- {pythonicdisort-1.6 → pythonicdisort-1.8}/README.md +14 -11
- {pythonicdisort-1.6 → pythonicdisort-1.8}/pyproject.toml +1 -1
- {pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py +126 -125
- {pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT/_solve_for_coeffs.py +79 -78
- pythonicdisort-1.8/src/PythonicDISORT/_solve_for_gen_and_part_sols.py +362 -0
- pythonicdisort-1.8/src/PythonicDISORT/pydisort.py +1140 -0
- {pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT/subroutines.py +169 -50
- {pythonicdisort-1.6 → pythonicdisort-1.8/src/PythonicDISORT.egg-info}/PKG-INFO +15 -12
- pythonicdisort-1.6/src/PythonicDISORT/_solve_for_gen_and_part_sols.py +0 -243
- pythonicdisort-1.6/src/PythonicDISORT/pydisort.py +0 -681
- {pythonicdisort-1.6 → pythonicdisort-1.8}/LICENSE.md +0 -0
- {pythonicdisort-1.6 → pythonicdisort-1.8}/setup.cfg +0 -0
- {pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT/__init__.py +0 -0
- {pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT.egg-info/SOURCES.txt +0 -0
- {pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT.egg-info/dependency_links.txt +0 -0
- {pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT.egg-info/requires.txt +0 -0
- {pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PythonicDISORT
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8
|
|
4
4
|
Summary: Discrete Ordinates Solver for the (1D) Radiative Transfer Equation in a single or multi-layer atmosphere.
|
|
5
5
|
Author-email: Dion HO Jia Xu <dh3065@columbia.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/LDEO-CREW/Pythonic-DISORT
|
|
@@ -26,15 +26,15 @@ Requires-Dist: matplotlib>=3.6.0; extra == "notebook-dependencies"
|
|
|
26
26
|
# Introduction
|
|
27
27
|
The PythonicDISORT package is a Discrete Ordinates Solver for the (1D) Radiative Transfer Equation
|
|
28
28
|
in a plane-parallel, horizontally homogeneous atmosphere.
|
|
29
|
-
It is coded entirely in Python 3 and is
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8])
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
It is coded entirely in Python 3 and is not a wrapper.
|
|
30
|
+
PythonicDISORT has been optimized for speed and **as of v1.8 is one-third the speed of FORTRAN DISORT while being NumPy and Scipy only**.
|
|
31
|
+
PythonicDISORT is intended to be easier to install, use, and modify than FORTRAN-based Discrete Ordinates Solvers.
|
|
32
|
+
|
|
33
|
+
PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8]),
|
|
34
|
+
as of v1.8 the only **missing features** are $\delta$-M+ scaling (PythonicDISORT instead implements NT corrections),
|
|
35
|
+
the Chapman function for near-horizon solar angles, and pseudo-spherical corrections.
|
|
36
|
+
On the other hand, we added a subroutine to compute actinic fluxes to satisfy a user request,
|
|
37
|
+
added analytic integration with respect to optical depth, and improved the handling of singularities.
|
|
38
38
|
Further feature requests as well as feedback are welcome.
|
|
39
39
|
|
|
40
40
|
You may contact me, Dion, through dh3065@columbia.edu.
|
|
@@ -70,8 +70,11 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
70
70
|
* Test Problem 7: Absorption + Scattering + All Possible Sources, Lambertian and Hapke Surface Reflectivities (one layer)
|
|
71
71
|
* Test Problem 8: Absorbing / Isotropic-Scattering Medium (multiple layers)
|
|
72
72
|
* Test Problem 9: General Emitting / Absorbing / Scattering Medium (multiple layers)
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
|
|
74
|
+
(no corresponding Jupyter Notebook)
|
|
75
|
+
* Test Problem 11: Single-Layer vs. Multiple Layers
|
|
76
|
+
* Test Problem I: Antiderivative / integration functionality
|
|
77
|
+
* Test Problem Interp: Interpolation + NT Correction functionality
|
|
75
78
|
|
|
76
79
|
# Installation
|
|
77
80
|
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
# Introduction
|
|
5
5
|
The PythonicDISORT package is a Discrete Ordinates Solver for the (1D) Radiative Transfer Equation
|
|
6
6
|
in a plane-parallel, horizontally homogeneous atmosphere.
|
|
7
|
-
It is coded entirely in Python 3 and is
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8])
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
It is coded entirely in Python 3 and is not a wrapper.
|
|
8
|
+
PythonicDISORT has been optimized for speed and **as of v1.8 is one-third the speed of FORTRAN DISORT while being NumPy and Scipy only**.
|
|
9
|
+
PythonicDISORT is intended to be easier to install, use, and modify than FORTRAN-based Discrete Ordinates Solvers.
|
|
10
|
+
|
|
11
|
+
PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8]),
|
|
12
|
+
as of v1.8 the only **missing features** are $\delta$-M+ scaling (PythonicDISORT instead implements NT corrections),
|
|
13
|
+
the Chapman function for near-horizon solar angles, and pseudo-spherical corrections.
|
|
14
|
+
On the other hand, we added a subroutine to compute actinic fluxes to satisfy a user request,
|
|
15
|
+
added analytic integration with respect to optical depth, and improved the handling of singularities.
|
|
16
16
|
Further feature requests as well as feedback are welcome.
|
|
17
17
|
|
|
18
18
|
You may contact me, Dion, through dh3065@columbia.edu.
|
|
@@ -48,8 +48,11 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
48
48
|
* Test Problem 7: Absorption + Scattering + All Possible Sources, Lambertian and Hapke Surface Reflectivities (one layer)
|
|
49
49
|
* Test Problem 8: Absorbing / Isotropic-Scattering Medium (multiple layers)
|
|
50
50
|
* Test Problem 9: General Emitting / Absorbing / Scattering Medium (multiple layers)
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
|
|
52
|
+
(no corresponding Jupyter Notebook)
|
|
53
|
+
* Test Problem 11: Single-Layer vs. Multiple Layers
|
|
54
|
+
* Test Problem I: Antiderivative / integration functionality
|
|
55
|
+
* Test Problem Interp: Interpolation + NT Correction functionality
|
|
53
56
|
|
|
54
57
|
# Installation
|
|
55
58
|
|
{pythonicdisort-1.6 → pythonicdisort-1.8}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py
RENAMED
|
@@ -29,7 +29,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
29
29
|
only_flux, # Only compute fluxes?
|
|
30
30
|
use_banded_solver_NLayers, # Number of layers above or equal which to use `scipy.linalg.solve_banded`
|
|
31
31
|
autograd_compatible, # Should the output functions be compatible with autograd?
|
|
32
|
-
|
|
32
|
+
cache_asso_leg, # Assoc. Legendre table cache mode: None, "no_mu0" or "mu0"
|
|
33
|
+
):
|
|
33
34
|
"""Assembles the solution functions: intensity `u`, upward flux `flux_up`, downward flux `flux_down`
|
|
34
35
|
from the previously solved eigenpairs and coefficients from the boundary conditions.
|
|
35
36
|
Returns these solution functions.
|
|
@@ -78,7 +79,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
78
79
|
| `only_flux` | boolean |
|
|
79
80
|
| `use_banded_solver_NLayers` | scalar |
|
|
80
81
|
| `autograd_compatible` | boolean |
|
|
81
|
-
|
|
82
|
+
| `cache_asso_leg` | None, "no_mu0" or "mu0" |
|
|
83
|
+
|
|
82
84
|
Notable internal variables of _assemble_intensity_and_fluxes
|
|
83
85
|
| Variable | Type / Shape |
|
|
84
86
|
| ----------------- | -------------------------------------- |
|
|
@@ -117,6 +119,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
117
119
|
mu0, I0_div_4pi,
|
|
118
120
|
there_is_beam_source,
|
|
119
121
|
there_is_iso_source,
|
|
122
|
+
cache_asso_leg,
|
|
120
123
|
)
|
|
121
124
|
if there_is_beam_source and there_is_iso_source:
|
|
122
125
|
G_collect, K_collect, B_collect, G_inv_collect_0 = outputs
|
|
@@ -161,8 +164,66 @@ def _assemble_intensity_and_fluxes(
|
|
|
161
164
|
G_collect_0 = G_collect[0, :, :, :]
|
|
162
165
|
K_collect_0 = K_collect[0, :, :]
|
|
163
166
|
GC_collect_0 = GC_collect[0, :, :, :]
|
|
164
|
-
# --------------------------------------------------------------------------------------------------------------------------
|
|
165
|
-
|
|
167
|
+
# --------------------------------------------------------------------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
# Value-keyed cache for the most recent `tau` evaluation grid, shared across the output
|
|
170
|
+
# functions `u`, `u0`, `flux_up` and `flux_down` (evaluating several outputs on the same
|
|
171
|
+
# grid, e.g. `flux_up(tau); flux_down(tau)`, then recomputes nothing).
|
|
172
|
+
# Stores a copy of `tau` (compared by value like the cache in `subroutines.interpolate`)
|
|
173
|
+
# with the derived layer indices and delta-scaled `tau`, and, lazily, the exponentials of
|
|
174
|
+
# the homogeneous-solution exponents for the 0th mode ("exp0") and for all modes ("expm").
|
|
175
|
+
# The cache is bypassed when `autograd_compatible` as it would break gradient tracing.
|
|
176
|
+
# --------------------------------------------------------------------------------------------------------------------------
|
|
177
|
+
is_delta_scaled = np.any(scale_tau != 1)
|
|
178
|
+
_tau_cache = {"tau": None, "l": None, "scaled_tau": None, "exp0": None, "expm": None}
|
|
179
|
+
|
|
180
|
+
def _process_tau(tau):
|
|
181
|
+
"""Layer indices and delta-scaled `tau` for the evaluation grid `tau`, cached."""
|
|
182
|
+
if (not autograd_compatible and _tau_cache["tau"] is not None
|
|
183
|
+
and np.array_equal(tau, _tau_cache["tau"])):
|
|
184
|
+
return _tau_cache["l"], _tau_cache["scaled_tau"]
|
|
185
|
+
# Atmospheric layer indices: smallest l with tau <= tau_arr[l]
|
|
186
|
+
l = np.searchsorted(tau_arr, tau, side="left")
|
|
187
|
+
if is_delta_scaled:
|
|
188
|
+
scaled_tau = scaled_tau_arr_with_0[1:][l] - (tau_arr[l] - tau) * scale_tau[l]
|
|
189
|
+
else:
|
|
190
|
+
scaled_tau = tau
|
|
191
|
+
if not autograd_compatible:
|
|
192
|
+
_tau_cache.update(
|
|
193
|
+
tau=np.array(tau, copy=True), l=l, scaled_tau=scaled_tau, exp0=None, expm=None
|
|
194
|
+
)
|
|
195
|
+
return l, scaled_tau
|
|
196
|
+
|
|
197
|
+
def _exp_exponent(l, scaled_tau, all_modes):
|
|
198
|
+
"""exp of the homogeneous-solution exponents for the (cached) evaluation grid.
|
|
199
|
+
Returns shape (Ntau, NQuad) for the 0th mode, or (NFourier, Ntau, NQuad) for all modes."""
|
|
200
|
+
key = "expm" if all_modes else "exp0"
|
|
201
|
+
if not autograd_compatible and _tau_cache[key] is not None:
|
|
202
|
+
return _tau_cache[key]
|
|
203
|
+
scaled_tau_arr_l = scaled_tau_arr_with_0[1:][l]
|
|
204
|
+
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
205
|
+
if all_modes:
|
|
206
|
+
exponent = np.concatenate(
|
|
207
|
+
[
|
|
208
|
+
K_collect[:, l, :N] * (scaled_tau - scaled_tau_arr_lm1)[None, :, None],
|
|
209
|
+
K_collect[:, l, N:] * (scaled_tau - scaled_tau_arr_l)[None, :, None],
|
|
210
|
+
],
|
|
211
|
+
axis=-1,
|
|
212
|
+
)
|
|
213
|
+
else:
|
|
214
|
+
exponent = np.concatenate(
|
|
215
|
+
[
|
|
216
|
+
K_collect_0[l, :N] * (scaled_tau - scaled_tau_arr_lm1)[:, None],
|
|
217
|
+
K_collect_0[l, N:] * (scaled_tau - scaled_tau_arr_l)[:, None],
|
|
218
|
+
],
|
|
219
|
+
axis=-1,
|
|
220
|
+
)
|
|
221
|
+
result = np.exp(exponent)
|
|
222
|
+
if not autograd_compatible:
|
|
223
|
+
_tau_cache[key] = result
|
|
224
|
+
return result
|
|
225
|
+
# --------------------------------------------------------------------------------------------------------------------------
|
|
226
|
+
|
|
166
227
|
if not only_flux:
|
|
167
228
|
|
|
168
229
|
# Construct the intensity function (refer to section 3.7 of the Comprehensive Documentation)
|
|
@@ -181,27 +242,10 @@ def _assemble_intensity_and_fluxes(
|
|
|
181
242
|
if np.any(tau < 0) or np.any(tau > tau_arr[-1]):
|
|
182
243
|
raise ValueError("tau input outside the tau range specified for the atmosphere (check `tau_arr`).")
|
|
183
244
|
|
|
184
|
-
# Atmospheric layer indices
|
|
185
|
-
l =
|
|
186
|
-
|
|
187
|
-
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
245
|
+
# Atmospheric layer indices, delta-M scaling and exponentials (cached)
|
|
246
|
+
l, scaled_tau = _process_tau(tau)
|
|
247
|
+
exp_exponent = _exp_exponent(l, scaled_tau, all_modes=True)
|
|
188
248
|
|
|
189
|
-
# Delta-M scaling
|
|
190
|
-
if np.any(scale_tau != np.ones(NLayers)):
|
|
191
|
-
tau_dist_from_top = tau_arr[l] - tau
|
|
192
|
-
scaled_tau_dist_from_top = tau_dist_from_top * scale_tau[l]
|
|
193
|
-
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
194
|
-
else:
|
|
195
|
-
scaled_tau = tau
|
|
196
|
-
|
|
197
|
-
exponent = np.concatenate(
|
|
198
|
-
[
|
|
199
|
-
K_collect[:, l, :N] * (scaled_tau - scaled_tau_arr_lm1)[None, :, None],
|
|
200
|
-
K_collect[:, l, N:] * (scaled_tau - scaled_tau_arr_l)[None, :, None],
|
|
201
|
-
],
|
|
202
|
-
axis=-1,
|
|
203
|
-
)
|
|
204
|
-
|
|
205
249
|
direct_beam_contribution = 0
|
|
206
250
|
if is_antiderivative_wrt_tau:
|
|
207
251
|
if there_is_beam_source:
|
|
@@ -213,7 +257,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
213
257
|
np.einsum(
|
|
214
258
|
"mtij, mtj -> mit",
|
|
215
259
|
GC_collect[:, l, :, :],
|
|
216
|
-
|
|
260
|
+
exp_exponent / (scale_tau[None, :, None] * K_collect)[:, l, :],
|
|
217
261
|
optimize=True,
|
|
218
262
|
)
|
|
219
263
|
+ direct_beam_contribution
|
|
@@ -227,7 +271,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
227
271
|
|
|
228
272
|
um = (
|
|
229
273
|
np.einsum(
|
|
230
|
-
"mtij, mtj -> mit", GC_collect[:, l, :, :],
|
|
274
|
+
"mtij, mtj -> mit", GC_collect[:, l, :, :], exp_exponent, optimize=True
|
|
231
275
|
)
|
|
232
276
|
+ direct_beam_contribution
|
|
233
277
|
)
|
|
@@ -236,7 +280,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
236
280
|
if there_is_iso_source:
|
|
237
281
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
238
282
|
_mathscr_v_contribution = _mathscr_v(
|
|
239
|
-
scaled_tau, scale_tau,
|
|
283
|
+
scaled_tau, scale_tau[l_uniq], # `scale_tau` is only required to determine the antiderivative
|
|
284
|
+
l_inv,
|
|
240
285
|
Nscoeffs,
|
|
241
286
|
scaled_s_poly_coeffs[l_uniq],
|
|
242
287
|
G_collect_0[l_uniq],
|
|
@@ -253,49 +298,52 @@ def _assemble_intensity_and_fluxes(
|
|
|
253
298
|
else:
|
|
254
299
|
um[0, :, :] += _mathscr_v_contribution
|
|
255
300
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
301
|
+
# Contract over the Fourier modes directly: materializing the
|
|
302
|
+
# (NFourier, NQuad, Ntau, Nphi) product only to sum over its first axis
|
|
303
|
+
# is substantially slower and heavier for large NFourier * Nphi
|
|
304
|
+
u = np.einsum(
|
|
305
|
+
"mit, mp -> itp",
|
|
306
|
+
um,
|
|
307
|
+
np.cos(np.arange(NFourier)[:, None] * (phi0 - phi)[None, :]),
|
|
308
|
+
optimize=True,
|
|
260
309
|
)
|
|
261
310
|
|
|
262
311
|
outputs = (rescale_factor * np.squeeze(u),)
|
|
263
312
|
output_is_tuple = False
|
|
264
313
|
|
|
265
314
|
if return_Fourier_error:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
K_collect[-1, l, N:] * (scaled_tau - scaled_tau_arr_l)[:, None],
|
|
270
|
-
],
|
|
271
|
-
axis=-1,
|
|
272
|
-
)
|
|
273
|
-
|
|
315
|
+
# The last Fourier mode's exponentials are the last slice of the cached tensor
|
|
316
|
+
exp_exponent_last = exp_exponent[-1]
|
|
317
|
+
|
|
274
318
|
direct_beam_contribution = 0
|
|
275
319
|
if is_antiderivative_wrt_tau:
|
|
276
320
|
if there_is_beam_source:
|
|
321
|
+
# `B_collect[-1].T` has shape (NQuad, NLayers): the last Fourier mode's
|
|
322
|
+
# particular-solution coefficients (`B_collect.T[-1]` would wrongly
|
|
323
|
+
# index the last quadrature node)
|
|
277
324
|
direct_beam_contribution = (
|
|
278
|
-
B_collect
|
|
325
|
+
B_collect[-1].T / (-scale_tau / mu0)[None, :]
|
|
279
326
|
)[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
280
327
|
|
|
281
328
|
ulast = (
|
|
282
329
|
np.einsum(
|
|
283
330
|
"tij, tj -> it",
|
|
284
331
|
GC_collect[-1, l, :, :],
|
|
285
|
-
|
|
332
|
+
exp_exponent_last / (scale_tau[:, None] * K_collect[-1, :, :])[l, :],
|
|
286
333
|
optimize=True,
|
|
287
334
|
)
|
|
288
335
|
+ direct_beam_contribution
|
|
289
336
|
)
|
|
290
337
|
else:
|
|
291
338
|
if there_is_beam_source:
|
|
339
|
+
# See the shape note in the `is_antiderivative_wrt_tau` branch above
|
|
292
340
|
direct_beam_contribution = (
|
|
293
|
-
B_collect.T[
|
|
341
|
+
B_collect[-1].T[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
294
342
|
)
|
|
295
343
|
|
|
296
344
|
ulast = (
|
|
297
345
|
np.einsum(
|
|
298
|
-
"tij, tj -> it", GC_collect[-1, l, :, :],
|
|
346
|
+
"tij, tj -> it", GC_collect[-1, l, :, :], exp_exponent_last, optimize=True
|
|
299
347
|
)
|
|
300
348
|
+ direct_beam_contribution
|
|
301
349
|
)
|
|
@@ -338,41 +386,25 @@ def _assemble_intensity_and_fluxes(
|
|
|
338
386
|
if np.any(tau < 0) or np.any(tau > tau_arr[-1]):
|
|
339
387
|
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
340
388
|
|
|
341
|
-
# Atmospheric layer indices
|
|
342
|
-
l =
|
|
343
|
-
|
|
344
|
-
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
389
|
+
# Atmospheric layer indices, delta-M scaling and exponentials (cached)
|
|
390
|
+
l, scaled_tau = _process_tau(tau)
|
|
391
|
+
exp_exponent = _exp_exponent(l, scaled_tau, all_modes=False)
|
|
345
392
|
|
|
346
|
-
#
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
351
|
-
# The following complements the function `subroutines.generate_diff_act_flux_funcs`
|
|
352
|
-
# in performing the reclassification of delta-scaled actinic flux
|
|
353
|
-
if _return_act_dscale_for_reclass:
|
|
354
|
-
if is_antiderivative_wrt_tau:
|
|
355
|
-
act_dscale_reclassification = (
|
|
356
|
-
I0 * np.exp(-scaled_tau / mu0) / (-scale_tau[l] / mu0)
|
|
357
|
-
- I0 * np.exp(-tau / mu0) * -mu0
|
|
358
|
-
)
|
|
359
|
-
else:
|
|
360
|
-
act_dscale_reclassification = I0 * np.exp(-scaled_tau / mu0) - I0 * np.exp(-tau / mu0)
|
|
361
|
-
else:
|
|
362
|
-
scaled_tau = tau
|
|
363
|
-
if _return_act_dscale_for_reclass:
|
|
393
|
+
# The following complements the function `subroutines.generate_diff_act_flux_funcs`
|
|
394
|
+
# in performing the reclassification of delta-scaled actinic flux
|
|
395
|
+
if _return_act_dscale_for_reclass:
|
|
396
|
+
if not is_delta_scaled:
|
|
364
397
|
act_dscale_reclassification = 0
|
|
398
|
+
elif is_antiderivative_wrt_tau:
|
|
399
|
+
act_dscale_reclassification = (
|
|
400
|
+
I0 * np.exp(-scaled_tau / mu0) / (-scale_tau[l] / mu0)
|
|
401
|
+
- I0 * np.exp(-tau / mu0) * -mu0
|
|
402
|
+
)
|
|
403
|
+
else:
|
|
404
|
+
act_dscale_reclassification = I0 * np.exp(-scaled_tau / mu0) - I0 * np.exp(-tau / mu0)
|
|
365
405
|
|
|
366
|
-
exponent = np.concatenate(
|
|
367
|
-
[
|
|
368
|
-
K_collect_0[l, :N] * (scaled_tau - scaled_tau_arr_lm1)[:, None],
|
|
369
|
-
K_collect_0[l, N:] * (scaled_tau - scaled_tau_arr_l)[:, None],
|
|
370
|
-
],
|
|
371
|
-
axis=-1,
|
|
372
|
-
)
|
|
373
|
-
|
|
374
406
|
direct_beam_contribution = 0
|
|
375
|
-
if is_antiderivative_wrt_tau
|
|
407
|
+
if is_antiderivative_wrt_tau:
|
|
376
408
|
if there_is_beam_source:
|
|
377
409
|
direct_beam_contribution = (B_collect_0.T / (-scale_tau / mu0)[None, :])[
|
|
378
410
|
:, l
|
|
@@ -382,7 +414,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
382
414
|
np.einsum(
|
|
383
415
|
"tij, tj -> it",
|
|
384
416
|
GC_collect_0[l, :, :],
|
|
385
|
-
|
|
417
|
+
exp_exponent / (scale_tau[:, None] * K_collect_0)[l, :],
|
|
386
418
|
optimize=True,
|
|
387
419
|
)
|
|
388
420
|
+ direct_beam_contribution
|
|
@@ -395,7 +427,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
395
427
|
|
|
396
428
|
u0 = (
|
|
397
429
|
np.einsum(
|
|
398
|
-
"tij, tj -> it", GC_collect_0[l, :, :],
|
|
430
|
+
"tij, tj -> it", GC_collect_0[l, :, :], exp_exponent, optimize=True
|
|
399
431
|
)
|
|
400
432
|
+ direct_beam_contribution
|
|
401
433
|
)
|
|
@@ -404,7 +436,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
404
436
|
if there_is_iso_source:
|
|
405
437
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
406
438
|
_mathscr_v_contribution = _mathscr_v(
|
|
407
|
-
scaled_tau, scale_tau,
|
|
439
|
+
scaled_tau, scale_tau[l_uniq], # `scale_tau` is only required to determine the antiderivative
|
|
440
|
+
l_inv,
|
|
408
441
|
Nscoeffs,
|
|
409
442
|
scaled_s_poly_coeffs[l_uniq],
|
|
410
443
|
G_collect_0[l_uniq],
|
|
@@ -447,23 +480,15 @@ def _assemble_intensity_and_fluxes(
|
|
|
447
480
|
if np.any(tau < 0) or np.any(tau > tau_arr[-1]):
|
|
448
481
|
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
449
482
|
|
|
450
|
-
# Atmospheric layer indices
|
|
451
|
-
l =
|
|
452
|
-
|
|
453
|
-
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
454
|
-
|
|
455
|
-
# Delta-M scaling
|
|
456
|
-
if np.any(scale_tau != np.ones(NLayers)):
|
|
457
|
-
tau_dist_from_top = tau_arr[l] - tau
|
|
458
|
-
scaled_tau_dist_from_top = tau_dist_from_top * scale_tau[l]
|
|
459
|
-
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
460
|
-
else:
|
|
461
|
-
scaled_tau = tau
|
|
483
|
+
# Atmospheric layer indices, delta-M scaling and exponentials (cached)
|
|
484
|
+
l, scaled_tau = _process_tau(tau)
|
|
485
|
+
exp_exponent = _exp_exponent(l, scaled_tau, all_modes=False)
|
|
462
486
|
|
|
463
487
|
if there_is_iso_source:
|
|
464
488
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
465
489
|
_mathscr_v_contribution = _mathscr_v(
|
|
466
|
-
scaled_tau, scale_tau,
|
|
490
|
+
scaled_tau, scale_tau[l_uniq], # `scale_tau` is only required to determine the antiderivative
|
|
491
|
+
l_inv,
|
|
467
492
|
Nscoeffs,
|
|
468
493
|
scaled_s_poly_coeffs[l_uniq],
|
|
469
494
|
G_collect_0[l_uniq, :N, :],
|
|
@@ -475,14 +500,6 @@ def _assemble_intensity_and_fluxes(
|
|
|
475
500
|
else:
|
|
476
501
|
_mathscr_v_contribution = 0
|
|
477
502
|
|
|
478
|
-
exponent = np.concatenate(
|
|
479
|
-
[
|
|
480
|
-
K_collect_0[l, :N] * (scaled_tau - scaled_tau_arr_lm1)[:, None],
|
|
481
|
-
K_collect_0[l, N:] * (scaled_tau - scaled_tau_arr_l)[:, None],
|
|
482
|
-
],
|
|
483
|
-
axis=-1,
|
|
484
|
-
)
|
|
485
|
-
|
|
486
503
|
direct_beam_contribution = 0
|
|
487
504
|
if is_antiderivative_wrt_tau:
|
|
488
505
|
if there_is_beam_source:
|
|
@@ -494,7 +511,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
494
511
|
np.einsum(
|
|
495
512
|
"tij, tj -> it",
|
|
496
513
|
GC_pos[l, :, :],
|
|
497
|
-
|
|
514
|
+
exp_exponent / (scale_tau[:, None] * K_collect_0)[l, :],
|
|
498
515
|
)
|
|
499
516
|
+ direct_beam_contribution
|
|
500
517
|
+ _mathscr_v_contribution
|
|
@@ -504,7 +521,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
504
521
|
direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
505
522
|
|
|
506
523
|
u0_pos = (
|
|
507
|
-
np.einsum("tij, tj -> it", GC_pos[l, :, :],
|
|
524
|
+
np.einsum("tij, tj -> it", GC_pos[l, :, :], exp_exponent)
|
|
508
525
|
+ direct_beam_contribution
|
|
509
526
|
+ _mathscr_v_contribution
|
|
510
527
|
)
|
|
@@ -529,23 +546,15 @@ def _assemble_intensity_and_fluxes(
|
|
|
529
546
|
if np.any(tau < 0) or np.any(tau > tau_arr[-1]):
|
|
530
547
|
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
531
548
|
|
|
532
|
-
# Atmospheric layer indices
|
|
533
|
-
l =
|
|
534
|
-
|
|
535
|
-
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
536
|
-
|
|
537
|
-
# Delta-M scaling
|
|
538
|
-
if np.any(scale_tau != np.ones(NLayers)):
|
|
539
|
-
tau_dist_from_top = tau_arr[l] - tau
|
|
540
|
-
scaled_tau_dist_from_top = tau_dist_from_top * scale_tau[l]
|
|
541
|
-
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
542
|
-
else:
|
|
543
|
-
scaled_tau = tau
|
|
549
|
+
# Atmospheric layer indices, delta-M scaling and exponentials (cached)
|
|
550
|
+
l, scaled_tau = _process_tau(tau)
|
|
551
|
+
exp_exponent = _exp_exponent(l, scaled_tau, all_modes=False)
|
|
544
552
|
|
|
545
553
|
if there_is_iso_source:
|
|
546
554
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
547
555
|
_mathscr_v_contribution = _mathscr_v(
|
|
548
|
-
scaled_tau, scale_tau,
|
|
556
|
+
scaled_tau, scale_tau[l_uniq], # `scale_tau` is only required to determine the antiderivative
|
|
557
|
+
l_inv,
|
|
549
558
|
Nscoeffs,
|
|
550
559
|
scaled_s_poly_coeffs[l_uniq],
|
|
551
560
|
G_collect_0[l_uniq, N:, :],
|
|
@@ -557,25 +566,17 @@ def _assemble_intensity_and_fluxes(
|
|
|
557
566
|
else:
|
|
558
567
|
_mathscr_v_contribution = 0
|
|
559
568
|
|
|
560
|
-
exponent = np.concatenate(
|
|
561
|
-
[
|
|
562
|
-
K_collect_0[l, :N] * (scaled_tau - scaled_tau_arr_lm1)[:, None],
|
|
563
|
-
K_collect_0[l, N:] * (scaled_tau - scaled_tau_arr_l)[:, None],
|
|
564
|
-
],
|
|
565
|
-
axis=-1,
|
|
566
|
-
)
|
|
567
|
-
|
|
568
569
|
direct_beam_contribution = 0
|
|
569
570
|
direct_beam = 0
|
|
570
571
|
direct_beam_scaled = 0
|
|
571
572
|
if is_antiderivative_wrt_tau:
|
|
572
573
|
if there_is_beam_source:
|
|
573
|
-
direct_beam_contribution = (B_neg / (-scale_tau / mu0)[None,
|
|
574
|
+
direct_beam_contribution = (B_neg / (-scale_tau / mu0)[None, :])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
574
575
|
direct_beam = I0 * mu0 * np.exp(-tau / mu0) * -mu0
|
|
575
|
-
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau / mu0)
|
|
576
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau[l] / mu0)
|
|
576
577
|
|
|
577
578
|
u0_neg = (
|
|
578
|
-
np.einsum("tij, tj -> it", GC_neg[l, :, :],
|
|
579
|
+
np.einsum("tij, tj -> it", GC_neg[l, :, :], exp_exponent / (scale_tau[:, None] * K_collect_0)[l, :])
|
|
579
580
|
+ direct_beam_contribution
|
|
580
581
|
+ _mathscr_v_contribution
|
|
581
582
|
)
|
|
@@ -586,7 +587,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
586
587
|
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0)
|
|
587
588
|
|
|
588
589
|
u0_neg = (
|
|
589
|
-
np.einsum("tij, tj -> it", GC_neg[l, :, :],
|
|
590
|
+
np.einsum("tij, tj -> it", GC_neg[l, :, :], exp_exponent)
|
|
590
591
|
+ direct_beam_contribution
|
|
591
592
|
+ _mathscr_v_contribution
|
|
592
593
|
)
|