PythonicDISORT 1.4__tar.gz → 1.6__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.4/src/PythonicDISORT.egg-info → pythonicdisort-1.6}/PKG-INFO +8 -5
- {pythonicdisort-1.4 → pythonicdisort-1.6}/README.md +6 -4
- {pythonicdisort-1.4 → pythonicdisort-1.6}/pyproject.toml +2 -1
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py +5 -12
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT/_solve_for_coeffs.py +7 -2
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT/_solve_for_gen_and_part_sols.py +21 -5
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT/pydisort.py +118 -162
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT/subroutines.py +12 -10
- {pythonicdisort-1.4 → pythonicdisort-1.6/src/PythonicDISORT.egg-info}/PKG-INFO +8 -5
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT.egg-info/requires.txt +1 -0
- {pythonicdisort-1.4 → pythonicdisort-1.6}/LICENSE.md +0 -0
- {pythonicdisort-1.4 → pythonicdisort-1.6}/setup.cfg +0 -0
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT/__init__.py +0 -0
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT.egg-info/SOURCES.txt +0 -0
- {pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT.egg-info/dependency_links.txt +0 -0
- {pythonicdisort-1.4 → pythonicdisort-1.6}/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.6
|
|
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
|
|
@@ -18,6 +18,7 @@ Provides-Extra: notebook-dependencies
|
|
|
18
18
|
Requires-Dist: autograd>=1.5; extra == "notebook-dependencies"
|
|
19
19
|
Requires-Dist: jupyter>1.0.0; extra == "notebook-dependencies"
|
|
20
20
|
Requires-Dist: notebook>6.5.2; extra == "notebook-dependencies"
|
|
21
|
+
Requires-Dist: matplotlib>=3.6.0; extra == "notebook-dependencies"
|
|
21
22
|
|
|
22
23
|
[](https://github.com/LDEO-CREW/Pythonic-DISORT/actions/workflows/continuous_integration.yml)
|
|
23
24
|
[](https://doi.org/10.21105/joss.06442)
|
|
@@ -40,7 +41,7 @@ You may contact me, Dion, through dh3065@columbia.edu.
|
|
|
40
41
|
|
|
41
42
|
The **GitHub repository** is https://github.com/LDEO-CREW/Pythonic-DISORT.
|
|
42
43
|
|
|
43
|
-
Accompanying Journal of Open Source Software paper: https://joss.theoj.org/papers/10.21105/joss.06442.
|
|
44
|
+
Accompanying Journal of Open Source Software paper: https://joss.theoj.org/papers/10.21105/joss.06442. **Please cite this paper if you use PythonicDISORT**.
|
|
44
45
|
|
|
45
46
|
# Documentation
|
|
46
47
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
@@ -70,18 +71,19 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
70
71
|
* Test Problem 8: Absorbing / Isotropic-Scattering Medium (multiple layers)
|
|
71
72
|
* Test Problem 9: General Emitting / Absorbing / Scattering Medium (multiple layers)
|
|
72
73
|
* Test Problem 11: Single-Layer vs. Multiple Layers (no corresponding Jupyter Notebook)
|
|
74
|
+
* Test Problem I: Antiderivative / integration functionality (no corresponding Jupyter Notebook)
|
|
73
75
|
|
|
74
76
|
# Installation
|
|
75
77
|
|
|
76
78
|
* From PyPI: `pip install PythonicDISORT`
|
|
77
|
-
* From Conda-
|
|
79
|
+
* From Conda-Forge: `conda install -c conda-forge pythonicdisort` or `mamba install -c conda-forge pythonicdisort`
|
|
78
80
|
* By cloning repository: `pip install .` in the `Pythonic-DISORT` directory; `pip install -r all_optional_dependencies.txt` to install all optional dependencies (see *Requirements to run PythonicDISORT*)
|
|
79
81
|
|
|
80
82
|
## Requirements to run PythonicDISORT
|
|
81
83
|
* Python 3.8+
|
|
82
84
|
* `numpy >= 1.8.0`
|
|
83
85
|
* `scipy >= 1.8.0`
|
|
84
|
-
* (OPTIONAL) `autograd >= 1.5`
|
|
86
|
+
* (OPTIONAL) `autograd >= 1.5` (`autograd` can be used to compute gradients of the output functions)
|
|
85
87
|
* (OPTIONAL) `pytest >= 6.2.5` (Required to use the command `pytest`, see *PyTest and examples of how to use PythonicDISORT*)
|
|
86
88
|
|
|
87
89
|
## (OPTIONAL) Additional requirements to run the Jupyter Notebook
|
|
@@ -89,7 +91,8 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
89
91
|
* `notebook > 6.5.2`
|
|
90
92
|
* `matplotlib >= 3.6.0`
|
|
91
93
|
|
|
92
|
-
In addition, a F2PY-wrapped Stamnes' DISORT
|
|
94
|
+
In addition, a F2PY-wrapped Stamnes' DISORT or equivalent (https://github.com/kconnour/pyRT_DISORT may work)
|
|
95
|
+
is required to properly run the last section (section 6).
|
|
93
96
|
|
|
94
97
|
## Compatibility
|
|
95
98
|
|
|
@@ -19,7 +19,7 @@ You may contact me, Dion, through dh3065@columbia.edu.
|
|
|
19
19
|
|
|
20
20
|
The **GitHub repository** is https://github.com/LDEO-CREW/Pythonic-DISORT.
|
|
21
21
|
|
|
22
|
-
Accompanying Journal of Open Source Software paper: https://joss.theoj.org/papers/10.21105/joss.06442.
|
|
22
|
+
Accompanying Journal of Open Source Software paper: https://joss.theoj.org/papers/10.21105/joss.06442. **Please cite this paper if you use PythonicDISORT**.
|
|
23
23
|
|
|
24
24
|
# Documentation
|
|
25
25
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
@@ -49,18 +49,19 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
49
49
|
* Test Problem 8: Absorbing / Isotropic-Scattering Medium (multiple layers)
|
|
50
50
|
* Test Problem 9: General Emitting / Absorbing / Scattering Medium (multiple layers)
|
|
51
51
|
* Test Problem 11: Single-Layer vs. Multiple Layers (no corresponding Jupyter Notebook)
|
|
52
|
+
* Test Problem I: Antiderivative / integration functionality (no corresponding Jupyter Notebook)
|
|
52
53
|
|
|
53
54
|
# Installation
|
|
54
55
|
|
|
55
56
|
* From PyPI: `pip install PythonicDISORT`
|
|
56
|
-
* From Conda-
|
|
57
|
+
* From Conda-Forge: `conda install -c conda-forge pythonicdisort` or `mamba install -c conda-forge pythonicdisort`
|
|
57
58
|
* By cloning repository: `pip install .` in the `Pythonic-DISORT` directory; `pip install -r all_optional_dependencies.txt` to install all optional dependencies (see *Requirements to run PythonicDISORT*)
|
|
58
59
|
|
|
59
60
|
## Requirements to run PythonicDISORT
|
|
60
61
|
* Python 3.8+
|
|
61
62
|
* `numpy >= 1.8.0`
|
|
62
63
|
* `scipy >= 1.8.0`
|
|
63
|
-
* (OPTIONAL) `autograd >= 1.5`
|
|
64
|
+
* (OPTIONAL) `autograd >= 1.5` (`autograd` can be used to compute gradients of the output functions)
|
|
64
65
|
* (OPTIONAL) `pytest >= 6.2.5` (Required to use the command `pytest`, see *PyTest and examples of how to use PythonicDISORT*)
|
|
65
66
|
|
|
66
67
|
## (OPTIONAL) Additional requirements to run the Jupyter Notebook
|
|
@@ -68,7 +69,8 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
68
69
|
* `notebook > 6.5.2`
|
|
69
70
|
* `matplotlib >= 3.6.0`
|
|
70
71
|
|
|
71
|
-
In addition, a F2PY-wrapped Stamnes' DISORT
|
|
72
|
+
In addition, a F2PY-wrapped Stamnes' DISORT or equivalent (https://github.com/kconnour/pyRT_DISORT may work)
|
|
73
|
+
is required to properly run the last section (section 6).
|
|
72
74
|
|
|
73
75
|
## Compatibility
|
|
74
76
|
|
|
@@ -8,7 +8,7 @@ dependencies = [
|
|
|
8
8
|
"scipy>=1.8.0",
|
|
9
9
|
]
|
|
10
10
|
name = "PythonicDISORT"
|
|
11
|
-
version = "1.
|
|
11
|
+
version = "1.6"
|
|
12
12
|
authors = [
|
|
13
13
|
{ name="Dion HO Jia Xu", email="dh3065@columbia.edu" },
|
|
14
14
|
]
|
|
@@ -27,6 +27,7 @@ notebook_dependencies = [
|
|
|
27
27
|
"autograd>=1.5",
|
|
28
28
|
"jupyter>1.0.0",
|
|
29
29
|
"notebook>6.5.2",
|
|
30
|
+
"matplotlib >= 3.6.0",
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
[project.urls]
|
{pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py
RENAMED
|
@@ -167,7 +167,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
167
167
|
|
|
168
168
|
# Construct the intensity function (refer to section 3.7 of the Comprehensive Documentation)
|
|
169
169
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
170
|
-
def u(tau, phi, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False
|
|
170
|
+
def u(tau, phi, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False):
|
|
171
171
|
"""
|
|
172
172
|
Intensity function with arguments `(tau, phi)` of types `(array or float, array or float)`.
|
|
173
173
|
Returns an ndarray with axes corresponding to variation with `mu, tau, phi` respectively.
|
|
@@ -236,7 +236,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
236
236
|
if there_is_iso_source:
|
|
237
237
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
238
238
|
_mathscr_v_contribution = _mathscr_v(
|
|
239
|
-
scaled_tau, l_inv,
|
|
239
|
+
scaled_tau, scale_tau, l_inv,
|
|
240
240
|
Nscoeffs,
|
|
241
241
|
scaled_s_poly_coeffs[l_uniq],
|
|
242
242
|
G_collect_0[l_uniq],
|
|
@@ -318,13 +318,6 @@ def _assemble_intensity_and_fluxes(
|
|
|
318
318
|
if return_tau_arr:
|
|
319
319
|
outputs += (tau_arr,)
|
|
320
320
|
output_is_tuple = True
|
|
321
|
-
|
|
322
|
-
if _return_l: # This indicates to the function that NT corrections are activated
|
|
323
|
-
output_is_tuple = True
|
|
324
|
-
outputs += (l,)
|
|
325
|
-
if there_is_iso_source:
|
|
326
|
-
# The TMS correction will use these
|
|
327
|
-
outputs += (l_uniq, l_inv)
|
|
328
321
|
|
|
329
322
|
return outputs if output_is_tuple else outputs[0]
|
|
330
323
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
@@ -411,7 +404,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
411
404
|
if there_is_iso_source:
|
|
412
405
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
413
406
|
_mathscr_v_contribution = _mathscr_v(
|
|
414
|
-
scaled_tau, l_inv,
|
|
407
|
+
scaled_tau, scale_tau, l_inv,
|
|
415
408
|
Nscoeffs,
|
|
416
409
|
scaled_s_poly_coeffs[l_uniq],
|
|
417
410
|
G_collect_0[l_uniq],
|
|
@@ -470,7 +463,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
470
463
|
if there_is_iso_source:
|
|
471
464
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
472
465
|
_mathscr_v_contribution = _mathscr_v(
|
|
473
|
-
scaled_tau, l_inv,
|
|
466
|
+
scaled_tau, scale_tau, l_inv,
|
|
474
467
|
Nscoeffs,
|
|
475
468
|
scaled_s_poly_coeffs[l_uniq],
|
|
476
469
|
G_collect_0[l_uniq, :N, :],
|
|
@@ -552,7 +545,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
552
545
|
if there_is_iso_source:
|
|
553
546
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
554
547
|
_mathscr_v_contribution = _mathscr_v(
|
|
555
|
-
scaled_tau, l_inv,
|
|
548
|
+
scaled_tau, scale_tau, l_inv,
|
|
556
549
|
Nscoeffs,
|
|
557
550
|
scaled_s_poly_coeffs[l_uniq],
|
|
558
551
|
G_collect_0[l_uniq, N:, :],
|
|
@@ -168,7 +168,8 @@ def _solve_for_coeffs(
|
|
|
168
168
|
if m_equals_0 and there_is_iso_source:
|
|
169
169
|
# Top boundary
|
|
170
170
|
RHS[:N] -= _mathscr_v(
|
|
171
|
-
np.array([0]),
|
|
171
|
+
np.array([0]),
|
|
172
|
+
None,
|
|
172
173
|
np.array([0]),
|
|
173
174
|
Nscoeffs,
|
|
174
175
|
scaled_s_poly_coeffs[[0], :],
|
|
@@ -183,6 +184,7 @@ def _solve_for_coeffs(
|
|
|
183
184
|
RHS[N:-N] += (
|
|
184
185
|
_mathscr_v(
|
|
185
186
|
scaled_tau_arr_with_0[1:-1],
|
|
187
|
+
None,
|
|
186
188
|
indices,
|
|
187
189
|
Nscoeffs,
|
|
188
190
|
scaled_s_poly_coeffs[indicesp1],
|
|
@@ -192,6 +194,7 @@ def _solve_for_coeffs(
|
|
|
192
194
|
)
|
|
193
195
|
- _mathscr_v(
|
|
194
196
|
scaled_tau_arr_with_0[1:-1],
|
|
197
|
+
None,
|
|
195
198
|
indices,
|
|
196
199
|
Nscoeffs,
|
|
197
200
|
scaled_s_poly_coeffs[indices],
|
|
@@ -203,7 +206,8 @@ def _solve_for_coeffs(
|
|
|
203
206
|
|
|
204
207
|
# Bottom boundary
|
|
205
208
|
RHS[-N:] -= _mathscr_v(
|
|
206
|
-
scaled_tau_arr_with_0[[-1]],
|
|
209
|
+
scaled_tau_arr_with_0[[-1]],
|
|
210
|
+
None,
|
|
207
211
|
np.array([0]),
|
|
208
212
|
Nscoeffs,
|
|
209
213
|
scaled_s_poly_coeffs[[-1], :],
|
|
@@ -217,6 +221,7 @@ def _solve_for_coeffs(
|
|
|
217
221
|
R
|
|
218
222
|
@ _mathscr_v(
|
|
219
223
|
scaled_tau_arr_with_0[[-1]],
|
|
224
|
+
None,
|
|
220
225
|
np.array([0]),
|
|
221
226
|
Nscoeffs,
|
|
222
227
|
scaled_s_poly_coeffs[[-1], :],
|
{pythonicdisort-1.4 → pythonicdisort-1.6}/src/PythonicDISORT/_solve_for_gen_and_part_sols.py
RENAMED
|
@@ -198,7 +198,8 @@ def _solve_for_gen_and_part_sols(
|
|
|
198
198
|
G_collect[no_shortcut_indices, N:, :N] = GpG_m_GmG_div_2
|
|
199
199
|
|
|
200
200
|
K_collect[no_shortcut_indices, :] = K_arr
|
|
201
|
-
|
|
201
|
+
len_no_shortcut_indices_0 = len(no_shortcut_indices_0)
|
|
202
|
+
if len_no_shortcut_indices_0 > 0: # If there is no isotropic source this list will be empty
|
|
202
203
|
G_inv_collect_0[no_shortcut_indices_0, :, :] = np.linalg.inv(
|
|
203
204
|
G_collect[no_shortcut_indices_0, :, :]
|
|
204
205
|
)
|
|
@@ -209,10 +210,25 @@ def _solve_for_gen_and_part_sols(
|
|
|
209
210
|
|
|
210
211
|
# Solve for particular solution w.r.t. direct beam source (refer to section 3.6.1 of the Comprehensive Documentation)
|
|
211
212
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
if len_no_shortcut_indices_0 > 0: # In this case we have `G_inv_collect_0`, so use it
|
|
214
|
+
B_collect[no_shortcut_indices_0, :] = np.einsum(
|
|
215
|
+
"bij, bjk, bk -> bi",
|
|
216
|
+
# Note that the minus sign has been absorbed into `X_arr`
|
|
217
|
+
G_collect[no_shortcut_indices_0, :, :]
|
|
218
|
+
/ (1 / mu0 + K_collect[no_shortcut_indices_0, None, :]),
|
|
219
|
+
G_inv_collect_0[no_shortcut_indices_0, :, :],
|
|
220
|
+
X_arr[no_shortcut_indices_0, :],
|
|
221
|
+
optimize=True,
|
|
222
|
+
)
|
|
223
|
+
no_shortcut_indices = no_shortcut_indices[len_no_shortcut_indices_0:]
|
|
224
|
+
|
|
225
|
+
if len(no_shortcut_indices) > 0:
|
|
226
|
+
A_arr = A_arr[len_no_shortcut_indices_0:]
|
|
227
|
+
A_arr[:, :N, N:] = -A_arr[:, N:, :N]
|
|
228
|
+
A_arr[:, :N, :N] = -A_arr[:, N:, N:]
|
|
229
|
+
A_arr.reshape(-1, NQuad * NQuad)[:, ::NQuad + 1] += 1 / mu0
|
|
230
|
+
# Note that the minus sign has been absorbed into `X_arr`
|
|
231
|
+
B_collect[no_shortcut_indices, :] = np.linalg.solve(A_arr, X_arr[no_shortcut_indices, :, None]).squeeze(-1)
|
|
216
232
|
|
|
217
233
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
218
234
|
|
|
@@ -98,7 +98,7 @@ def pydisort(
|
|
|
98
98
|
Returns the diffuse flux magnitudes (same type and size as ``tau``).
|
|
99
99
|
Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
|
|
100
100
|
to switch to an antiderivative of this function with respect to ``tau``.
|
|
101
|
-
For example, ``Fp(tau_arr,
|
|
101
|
+
For example, ``Fp(tau_arr, True) - Fp(np.insert(tau_arr[:-1] + 1e-15, 0, 0), True)``
|
|
102
102
|
will produce an array of the tau-integral over each layer.
|
|
103
103
|
Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
|
|
104
104
|
Fm(tau) : function
|
|
@@ -406,19 +406,21 @@ def pydisort(
|
|
|
406
406
|
|
|
407
407
|
# TMS correction for the intensity (see section 3.7.2)
|
|
408
408
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
409
|
-
def TMS_correction(tau, phi, is_antiderivative_wrt_tau
|
|
409
|
+
def TMS_correction(tau, phi, is_antiderivative_wrt_tau):
|
|
410
410
|
Ntau = len(tau)
|
|
411
411
|
Nphi = len(phi)
|
|
412
412
|
# Atmospheric layer indices
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
scaled_tau_arr_l = scaled_tau_arr_with_0[1:][l]
|
|
413
|
+
l = np.argmax(tau[:, None] <= tau_arr[None, :], axis=1)
|
|
414
|
+
scaled_tau_arr_l = scaled_tau_arr_with_0[l + 1]
|
|
416
415
|
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
417
416
|
|
|
418
417
|
# Delta-M scaling
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
418
|
+
if np.any(scale_tau != np.ones(NLayers)):
|
|
419
|
+
tau_dist_from_top = tau_arr[l] - tau
|
|
420
|
+
scaled_tau_dist_from_top = tau_dist_from_top * scale_tau[l]
|
|
421
|
+
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
422
|
+
else:
|
|
423
|
+
scaled_tau = tau
|
|
422
424
|
|
|
423
425
|
# mathscr_B
|
|
424
426
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
@@ -429,45 +431,44 @@ def pydisort(
|
|
|
429
431
|
[
|
|
430
432
|
f(nu)[:, None, :]
|
|
431
433
|
# Iterates over the 0th axis
|
|
432
|
-
for f in map(Legendre, weighted_Leg_coeffs_all
|
|
434
|
+
for f in map(Legendre, weighted_Leg_coeffs_all)
|
|
433
435
|
],
|
|
434
436
|
axis=1,
|
|
435
|
-
)
|
|
437
|
+
)
|
|
436
438
|
p_trun = np.concatenate(
|
|
437
439
|
[
|
|
438
440
|
f(nu)[:, None, :]
|
|
439
441
|
# Iterates over the 0th axis
|
|
440
|
-
for f in map(Legendre, weighted_scaled_Leg_coeffs
|
|
442
|
+
for f in map(Legendre, weighted_scaled_Leg_coeffs)
|
|
441
443
|
],
|
|
442
444
|
axis=1,
|
|
443
|
-
)
|
|
444
|
-
|
|
445
|
+
)
|
|
445
446
|
mathscr_B = (
|
|
446
|
-
(scaled_omega_arr
|
|
447
|
+
(scaled_omega_arr * I0)[None, :, None]
|
|
448
|
+
/ (4 * np.pi)
|
|
447
449
|
* (mu0 / (mu0 + mu_arr))[:, None, None]
|
|
448
|
-
* (p_true / (1 - f_arr
|
|
449
|
-
)
|
|
450
|
+
* (p_true / (1 - f_arr[None, :, None]) - p_trun)
|
|
451
|
+
)
|
|
450
452
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
451
453
|
|
|
452
454
|
neg_scaled_tau_div_mu0 = -scaled_tau / mu0
|
|
453
455
|
if is_antiderivative_wrt_tau:
|
|
454
456
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
scale_tau_l_div_mu_arr_pos = scale_tau_l[None, :] / mu_arr_pos[:, None]
|
|
457
|
+
neg_scale_tau_div_mu0 = -scale_tau / mu0
|
|
458
|
+
scale_tau_div_mu_arr_pos = scale_tau[None, :] / mu_arr_pos[:, None]
|
|
458
459
|
|
|
459
460
|
TMS_correction_pos = (
|
|
460
|
-
np.exp(neg_scaled_tau_div_mu0) /
|
|
461
|
+
np.exp(neg_scaled_tau_div_mu0) / neg_scale_tau_div_mu0[l]
|
|
461
462
|
)[None, :] - np.exp(
|
|
462
463
|
(scaled_tau - scaled_tau_arr_l)[None, :] / mu_arr_pos[:, None]
|
|
463
464
|
- scaled_tau_arr_l[None, :] / mu0
|
|
464
|
-
) /
|
|
465
|
+
) / scale_tau_div_mu_arr_pos[:, l]
|
|
465
466
|
TMS_correction_neg = (
|
|
466
|
-
np.exp(neg_scaled_tau_div_mu0) /
|
|
467
|
+
np.exp(neg_scaled_tau_div_mu0) / neg_scale_tau_div_mu0[l]
|
|
467
468
|
)[None, :] + np.exp(
|
|
468
469
|
(scaled_tau_arr_lm1 - scaled_tau)[None, :] / mu_arr_pos[:, None]
|
|
469
470
|
- scaled_tau_arr_lm1[None, :] / mu0
|
|
470
|
-
) /
|
|
471
|
+
) / scale_tau_div_mu_arr_pos[:, l]
|
|
471
472
|
else:
|
|
472
473
|
TMS_correction_pos = np.exp(neg_scaled_tau_div_mu0)[None, :] - np.exp(
|
|
473
474
|
(scaled_tau - scaled_tau_arr_l)[None, :] / mu_arr_pos[:, None]
|
|
@@ -477,123 +478,111 @@ def pydisort(
|
|
|
477
478
|
(scaled_tau_arr_lm1 - scaled_tau)[None, :] / mu_arr_pos[:, None]
|
|
478
479
|
- scaled_tau_arr_lm1[None, :] / mu0
|
|
479
480
|
)
|
|
480
|
-
|
|
481
481
|
|
|
482
482
|
## Contribution from other layers
|
|
483
483
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
* np.concatenate((TMS_correction_pos, TMS_correction_neg), axis=0)[:, :, None]
|
|
487
|
-
)
|
|
484
|
+
Bpos = mathscr_B[:N, :, :] # (N, NLayers, Nphi)
|
|
485
|
+
Bneg = mathscr_B[N:, :, :] # (N, NLayers, Nphi)
|
|
488
486
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
487
|
+
sol_pos = Bpos[:, l, :] * TMS_correction_pos[:, :, None] # (N, Ntau, Nphi)
|
|
488
|
+
sol_neg = Bneg[:, l, :] * TMS_correction_neg[:, :, None] # (N, Ntau, Nphi)
|
|
489
|
+
|
|
490
|
+
if not is_atmos_multilayered:
|
|
491
|
+
return np.concatenate((sol_pos, sol_neg), axis=0)
|
|
494
492
|
|
|
495
|
-
|
|
496
|
-
|
|
493
|
+
# ---------------- other-layer contributions ----------------
|
|
494
|
+
any_pos = (l.min() < NLayers - 1) # exists tau with layers below
|
|
495
|
+
any_neg = (l.max() > 0) # exists tau with layers above
|
|
497
496
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
497
|
+
# Layer boundaries/thickness in scaled-tau coordinates
|
|
498
|
+
tau_front = scaled_tau_arr_with_0[:-1] # (NLayers,) = tau_r
|
|
499
|
+
tau_back = scaled_tau_arr_with_0[1:] # (NLayers,) = tau_{r+1}
|
|
501
500
|
|
|
501
|
+
# decay[n,r] = exp(-scaled_thickness_arr[r]/mu[n]) (argument <= 0; underflow is safe)
|
|
502
|
+
decay = np.exp(-scaled_thickness_arr[None, :] * M_inv[:, None]) # (N, NLayers)
|
|
502
503
|
|
|
503
|
-
|
|
504
|
-
|
|
504
|
+
exp_taufront_mu0_inv = np.exp(-tau_front / mu0) # (NLayers,)
|
|
505
|
+
exp_tauback_mu0_inv = np.exp(-tau_back / mu0) # (NLayers,)
|
|
505
506
|
|
|
506
|
-
|
|
507
|
-
|
|
507
|
+
if is_antiderivative_wrt_tau:
|
|
508
|
+
# (N, NLayers): multiply by this instead of dividing by (scale_tau/mu)
|
|
509
|
+
mu_div_scale_layers = mu_arr_pos[:, None] / scale_tau[None, :]
|
|
508
510
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
511
|
+
# ===== POS: sum over r > l =====
|
|
512
|
+
if any_pos:
|
|
513
|
+
# fac_pos = 1 - exp(-scaled_thickness_arr*(1/mu + 1/mu0)) = -expm1(-scaled_thickness_arr*(...))
|
|
514
|
+
delta_pos = scaled_thickness_arr[None, :] * (M_inv + 1 / mu0)[:, None] # (N, NLayers) >= 0
|
|
515
|
+
fac_pos = -np.expm1(-delta_pos) # (N, NLayers) in [0,1)
|
|
512
516
|
|
|
513
517
|
if is_antiderivative_wrt_tau:
|
|
514
|
-
# (N, NLayers)
|
|
515
|
-
integration_factor = mu_arr_pos[:, None] / scale_tau
|
|
516
|
-
|
|
517
|
-
# ====================================================================
|
|
518
|
-
# POS: pre-accumulate "contribution from below" sums Rpos[N, l, Nphi]
|
|
519
|
-
# ====================================================================
|
|
520
|
-
if any_pos:
|
|
521
|
-
thickness_pos = scaled_thickness_arr[None, :] * (M_inv + mu0_inv)[:, None] # (N, NLayers); always > 0
|
|
522
|
-
# em1_pos = 1 - exp(-scaled_thickness_arr*(1/mu + 1/mu0)) = -expm1(-thickness_pos)
|
|
523
|
-
em1_pos = -np.expm1(-thickness_pos) # (N, NLayers)
|
|
524
|
-
if is_antiderivative_wrt_tau:
|
|
525
|
-
em1_pos = integration_factor * em1_pos
|
|
526
|
-
|
|
527
|
-
layer_term_pos = em1_pos * exp_taufront_mu0_inv[None, :] # (N, NLayers)
|
|
528
|
-
|
|
529
|
-
# Reverse-scan via cumsum:
|
|
530
|
-
# Rpos[l] = (1/decay_prod[l+1]) * sum_{r=l+1..} (layer_term_pos[r] * decay_prod[r])
|
|
531
|
-
weighted_layer_term_pos = layer_term_pos * decay_prod[:, :NLayers] # (N, NLayers)
|
|
532
|
-
cumsum_pos = np.cumsum(weighted_layer_term_pos[:, ::-1], axis=1)[:, ::-1] # (N, NLayers)
|
|
533
|
-
|
|
534
|
-
Rpos_rest = cumsum_pos[:, 1:] / decay_prod[:, 1 : NLayers] # (N, NLayers - 1)
|
|
535
|
-
Rpos = np.concatenate([Rpos_rest, np.zeros((N, 1))], axis=1) # (N, NLayers)
|
|
536
|
-
|
|
537
|
-
# Apply to all `tau`
|
|
538
|
-
# expfac_pos = exp(M_inv*tau) * exp(-M_inv*scaled_tauwith0_back[l])
|
|
539
|
-
expfac_pos = np.exp(M_inv[:, None] * (scaled_tau - scaled_tauwith0_back[l])[None, :]) # (N, Ntau)
|
|
540
|
-
tmp = np.take(Rpos, l, axis=1) # (N, Ntau)
|
|
541
|
-
tmp = (tmp * expfac_pos)[:, :, None] * Bpos[:, l_inv, :]
|
|
542
|
-
|
|
543
|
-
if autograd_compatible:
|
|
544
|
-
solution = np.concatenate((solution[:N, :, :] + tmp, solution[N:, :, :]), axis=0)
|
|
545
|
-
else:
|
|
546
|
-
solution[:N, :, :] += tmp
|
|
547
|
-
|
|
548
|
-
# ====================================================================
|
|
549
|
-
# NEG: pre-accumulate "contribution from above" sums Rneg[N, l, Nphi]
|
|
550
|
-
# ====================================================================
|
|
551
|
-
if any_neg:
|
|
552
|
-
decay = np.exp(log_decay) # (N, NLayers)
|
|
553
|
-
|
|
554
|
-
# Ensure no positive exponents
|
|
555
|
-
thickness_neg = scaled_thickness_arr[None, :] * (M_inv - mu0_inv)[:, None] # (N, NLayers)
|
|
556
|
-
mask = (thickness_neg >= 0)
|
|
518
|
+
fac_pos = fac_pos * mu_div_scale_layers # (N, NLayers)
|
|
557
519
|
|
|
558
|
-
|
|
559
|
-
|
|
520
|
+
# coef_pos[n,r] multiplies exp((tau - tau_front[r])/mu - tau_front[r]/mu0) after re-referencing
|
|
521
|
+
# includes exp(-tau_front[r]/mu0)
|
|
522
|
+
coef_pos = fac_pos * exp_taufront_mu0_inv[None, :] # (N, NLayers)
|
|
560
523
|
|
|
561
|
-
|
|
562
|
-
|
|
524
|
+
# Reverse scan to build Acc_l (referenced at tau_back[l]) INCLUDING Bpos:
|
|
525
|
+
# Acc_{r-1} = coef_pos[:,r]*Bpos[:,r,:] + decay[:,r]*Acc_r
|
|
526
|
+
acc0 = np.zeros((N, Nphi))
|
|
527
|
+
acc = acc0
|
|
528
|
+
cols_rev = [acc0] # Acc_{NLayers-1} = 0
|
|
563
529
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
layer_term_neg = layer_term_posthick * mask + layer_term_negthick * (~mask) # (N, NLayers)
|
|
530
|
+
for r in range(NLayers - 1, 0, -1): # r = NLayers-1 ... 1
|
|
531
|
+
term = coef_pos[:, r][:, None] * Bpos[:, r, :] # (N, Nphi)
|
|
532
|
+
acc = term + decay[:, r][:, None] * acc
|
|
533
|
+
cols_rev.append(acc)
|
|
569
534
|
|
|
570
|
-
|
|
571
|
-
layer_term_neg = -integration_factor * layer_term_neg
|
|
535
|
+
Acc_pos = np.stack(cols_rev[::-1], axis=1) # (N, NLayers, Nphi)
|
|
572
536
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
cumsum_neg = np.cumsum(weighted_layer_term_neg, axis=1) # (N, NLayers)
|
|
577
|
-
|
|
578
|
-
Rneg_rest = decay_prod[:, 1 : NLayers] * cumsum_neg[:, :-1] # (N, NLayers - 1)
|
|
579
|
-
Rneg = np.concatenate([np.zeros((N, 1)), Rneg_rest], axis=1) # (N, NLayers)
|
|
537
|
+
# Apply to tau: multiply by exp((scaled_tau - tau_back[l]) / mu) (argument <= 0 in exact arithmetic)
|
|
538
|
+
expfac_pos = np.exp(M_inv[:, None] * (scaled_tau - tau_back[l])[None, :]) # (N, Ntau)
|
|
539
|
+
contrib_pos = Acc_pos[:, l, :] * expfac_pos[:, :, None] # (N, Ntau, Nphi)
|
|
580
540
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
541
|
+
sol_pos = sol_pos + contrib_pos
|
|
542
|
+
|
|
543
|
+
# ===== NEG: sum over r < l =====
|
|
544
|
+
if any_neg:
|
|
545
|
+
# We need term_base[n,r] = exp(-tau_back[r]/mu0) - exp(-scaled_thickness_arr[r]/mu)*exp(-tau_front[r]/mu0)
|
|
546
|
+
# computed stably via expm1 using only non-positive inputs.
|
|
547
|
+
delta_neg = scaled_thickness_arr[None, :] * (M_inv - 1 / mu0)[:, None] # (N, NLayers), can be +/-.
|
|
548
|
+
abs_delta = np.abs(delta_neg)
|
|
549
|
+
em1 = np.expm1(-abs_delta) # (N, NLayers) in [-1,0], argument <= 0
|
|
550
|
+
|
|
551
|
+
exp_x1 = exp_tauback_mu0_inv[None, :] # (1, NLayers)
|
|
552
|
+
exp_x0 = decay * exp_taufront_mu0_inv[None, :] # (N, NLayers)
|
|
553
|
+
|
|
554
|
+
# if delta>=0: term = exp_x1*(1-exp(-delta)) = exp_x1*(-expm1(-delta)) = exp_x1*(-em1)
|
|
555
|
+
# if delta<0 : term = exp_x0*(exp(delta)-1) = exp_x0*expm1(delta) = exp_x0*( em1) (since delta<0 -> em1=expm1(delta))
|
|
556
|
+
term_base = np.where(delta_neg >= 0.0, (-em1) * exp_x1, em1 * exp_x0) # (N, NLayers)
|
|
557
|
+
|
|
558
|
+
if is_antiderivative_wrt_tau:
|
|
559
|
+
# original divides by (-scale_tau/mu) == multiply by (-(mu/scale_tau))
|
|
560
|
+
coef_neg = term_base * -mu_div_scale_layers # (N, NLayers)
|
|
561
|
+
else:
|
|
562
|
+
coef_neg = term_base # (N, NLayers)
|
|
563
|
+
|
|
564
|
+
# Forward scan to build Acc_l (referenced at tau_front[l]) INCLUDING Bneg:
|
|
565
|
+
# Acc_{r+1} = coef_neg[:,r]*Bneg[:,r,:] + decay[:,r]*Acc_r
|
|
566
|
+
acc0 = np.zeros((N, Nphi))
|
|
567
|
+
acc = acc0
|
|
568
|
+
cols = [acc0] # Acc_0 = 0
|
|
569
|
+
|
|
570
|
+
for r in range(0, NLayers - 1): # r = 0 ... NLayers-2
|
|
571
|
+
term = coef_neg[:, r][:, None] * Bneg[:, r, :] # (N, Nphi)
|
|
572
|
+
acc = term + decay[:, r][:, None] * acc
|
|
573
|
+
cols.append(acc)
|
|
574
|
+
|
|
575
|
+
Acc_neg = np.stack(cols, axis=1) # (N, NLayers, Nphi)
|
|
576
|
+
|
|
577
|
+
# Apply to tau: multiply by exp((tau_front[l] - scaled_tau) / mu) (argument <= 0 in exact arithmetic)
|
|
578
|
+
expfac_neg = np.exp(M_inv[:, None] * (tau_front[l] - scaled_tau)[None, :]) # (N, Ntau)
|
|
579
|
+
contrib_neg = Acc_neg[:, l, :] * expfac_neg[:, :, None] # (N, Ntau, Nphi)
|
|
590
580
|
|
|
591
|
-
|
|
581
|
+
sol_neg = sol_neg + contrib_neg
|
|
592
582
|
|
|
583
|
+
return np.concatenate((sol_pos, sol_neg), axis=0)
|
|
584
|
+
|
|
593
585
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
594
|
-
|
|
595
|
-
else:
|
|
596
|
-
return solution
|
|
597
586
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
598
587
|
|
|
599
588
|
# IMS correction for the intensity (see section 3.7.2)
|
|
@@ -628,7 +617,8 @@ def pydisort(
|
|
|
628
617
|
) / (mu_arr_pos * scaled_mu0 * x)[:, None]
|
|
629
618
|
|
|
630
619
|
return (
|
|
631
|
-
|
|
620
|
+
I0
|
|
621
|
+
/ (4 * pi)
|
|
632
622
|
* (omega_avg * f_avg) ** 2
|
|
633
623
|
/ (1 - omega_avg * f_avg)
|
|
634
624
|
* Legendre(
|
|
@@ -640,59 +630,25 @@ def pydisort(
|
|
|
640
630
|
|
|
641
631
|
# The corrected intensity
|
|
642
632
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
643
|
-
def u_corrected(
|
|
644
|
-
tau,
|
|
645
|
-
phi,
|
|
646
|
-
is_antiderivative_wrt_tau=False,
|
|
647
|
-
return_Fourier_error=False,
|
|
648
|
-
return_tau_arr=False,
|
|
649
|
-
):
|
|
633
|
+
def u_corrected(tau, phi, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False):
|
|
650
634
|
tau = np.atleast_1d(tau)
|
|
651
635
|
phi = np.atleast_1d(phi)
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
tau,
|
|
655
|
-
phi,
|
|
656
|
-
is_antiderivative_wrt_tau,
|
|
657
|
-
return_Fourier_error,
|
|
658
|
-
return_tau_arr,
|
|
659
|
-
_return_l=True,
|
|
660
|
-
)
|
|
661
|
-
if there_is_iso_source:
|
|
662
|
-
NT_corrections = TMS_correction(
|
|
663
|
-
tau,
|
|
664
|
-
phi,
|
|
665
|
-
is_antiderivative_wrt_tau,
|
|
666
|
-
u_star_outputs[-3],
|
|
667
|
-
u_star_outputs[-2],
|
|
668
|
-
u_star_outputs[-1],
|
|
669
|
-
)
|
|
670
|
-
u_star_outputs = u_star_outputs[:-3]
|
|
671
|
-
else:
|
|
672
|
-
NT_corrections = TMS_correction(
|
|
673
|
-
tau, phi, is_antiderivative_wrt_tau, u_star_outputs[-1], None, None
|
|
674
|
-
)
|
|
675
|
-
u_star_outputs = u_star_outputs[:-1]
|
|
676
|
-
|
|
636
|
+
NT_corrections = TMS_correction(tau, phi, is_antiderivative_wrt_tau)
|
|
637
|
+
|
|
677
638
|
if autograd_compatible:
|
|
678
|
-
NT_corrections = np.concatenate(
|
|
679
|
-
(
|
|
680
|
-
NT_corrections[:N, :, :],
|
|
681
|
-
NT_corrections[N:, :, :]
|
|
682
|
-
+ IMS_correction(tau, phi, is_antiderivative_wrt_tau),
|
|
683
|
-
),
|
|
684
|
-
axis=0,
|
|
639
|
+
NT_corrections = NT_corrections + np.concatenate(
|
|
640
|
+
[np.zeros((N, len(tau), len(phi))), IMS_correction(tau, phi, is_antiderivative_wrt_tau)], axis=0
|
|
685
641
|
)
|
|
686
642
|
else:
|
|
687
643
|
NT_corrections[N:, :, :] += IMS_correction(tau, phi, is_antiderivative_wrt_tau)
|
|
688
|
-
|
|
644
|
+
|
|
689
645
|
if return_Fourier_error or return_tau_arr:
|
|
646
|
+
u_star_outputs = u_star(tau, phi, is_antiderivative_wrt_tau, return_Fourier_error, return_tau_arr)
|
|
690
647
|
return (
|
|
691
648
|
u_star_outputs[0] + rescale_factor * np.squeeze(NT_corrections),
|
|
692
649
|
) + u_star_outputs[1:]
|
|
693
650
|
else:
|
|
694
|
-
return
|
|
695
|
-
|
|
651
|
+
return u_star(tau, phi, is_antiderivative_wrt_tau) + rescale_factor * np.squeeze(NT_corrections)
|
|
696
652
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
697
653
|
|
|
698
654
|
return mu_arr, flux_up, flux_down, u0, u_corrected
|
|
@@ -744,6 +744,7 @@ def to_diag_ordered_form(A, Nsuperdiags, Nsubdiags):
|
|
|
744
744
|
|
|
745
745
|
|
|
746
746
|
def _mathscr_v(tau, # Input optical depths
|
|
747
|
+
scale_tau, # Delta-scale factor for tau
|
|
747
748
|
l, # Layer index of each input optical depth
|
|
748
749
|
Nscoeffs, # Number of isotropic source polynomial coefficients
|
|
749
750
|
s_poly_coeffs, # Polynomial coefficients of isotropic source
|
|
@@ -763,6 +764,7 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
763
764
|
| Variable | Shape |
|
|
764
765
|
| ----------------------------- | ----------------------------------------- |
|
|
765
766
|
| ``tau`` | ``Ntau`` |
|
|
767
|
+
| ``scale_tau`` | `NLayers` |
|
|
766
768
|
| ``l`` | ``Ntau`` |
|
|
767
769
|
| ``Nscoeffs`` | scalar |
|
|
768
770
|
| ``s_poly_coeffs`` | ``NL x Nscoeffs`` |
|
|
@@ -784,13 +786,13 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
784
786
|
if Nscoeffs == 1:
|
|
785
787
|
s0 = s_poly_coeffs[:, 0:1]
|
|
786
788
|
c0w = s0 * K_inv * G_inv_mu_inv # (NL, 1) * (NL, NQuad) -> (NL, NQuad)
|
|
789
|
+
|
|
790
|
+
d0 = np.einsum("lij, lj -> il", G, c0w, optimize=True)
|
|
787
791
|
|
|
788
792
|
if is_antiderivative_wrt_tau:
|
|
789
|
-
p0 = tau
|
|
793
|
+
p0 = tau / scale_tau
|
|
790
794
|
else:
|
|
791
795
|
p0 = np.ones_like(tau)
|
|
792
|
-
|
|
793
|
-
d0 = np.einsum("lij, lj -> il", G, c0w, optimize=True)
|
|
794
796
|
|
|
795
797
|
return d0[:, l] * p0[None, :]
|
|
796
798
|
|
|
@@ -803,19 +805,19 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
803
805
|
|
|
804
806
|
c0w = s1 * K_inv * G_inv_mu_inv
|
|
805
807
|
c1w = (s0 * K_inv + s1 * K_inv2) * G_inv_mu_inv
|
|
808
|
+
|
|
809
|
+
d0 = np.einsum("lij, lj -> il", G, c0w, optimize=True) # (NQuad, NL)
|
|
810
|
+
d1 = np.einsum("lij, lj -> il", G, c1w, optimize=True) # (NQuad, NL)
|
|
806
811
|
|
|
807
812
|
if is_antiderivative_wrt_tau:
|
|
808
813
|
p0 = tau * tau / 2
|
|
809
814
|
p1 = tau
|
|
815
|
+
return (d0[:, l] * p0[None, :] + d1[:, l] * p1[None, :]) / scale_tau
|
|
810
816
|
else:
|
|
811
817
|
p0 = tau
|
|
812
818
|
p1 = np.ones_like(tau)
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
d1 = np.einsum("lij, lj -> il", G, c1w, optimize=True) # (NQuad, NL)
|
|
816
|
-
|
|
817
|
-
return d0[:, l] * p0[None, :] + d1[:, l] * p1[None, :]
|
|
818
|
-
|
|
819
|
+
return d0[:, l] * p0[None, :] + d1[:, l] * p1[None, :]
|
|
820
|
+
|
|
819
821
|
# --------------------- Nscoeffs > 2 ----------------------
|
|
820
822
|
else:
|
|
821
823
|
if Nscoeffs > 10:
|
|
@@ -853,7 +855,7 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
853
855
|
powers = np.arange(Nscoeffs - 1, -1, -1)[None, :]
|
|
854
856
|
if is_antiderivative_wrt_tau:
|
|
855
857
|
p = powers + 1
|
|
856
|
-
tau_poly = (tau[:, None] ** p) / p
|
|
858
|
+
tau_poly = (tau[:, None] ** p) / (p * scale_tau)
|
|
857
859
|
else:
|
|
858
860
|
tau_poly = tau[:, None] ** powers
|
|
859
861
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PythonicDISORT
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6
|
|
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
|
|
@@ -18,6 +18,7 @@ Provides-Extra: notebook-dependencies
|
|
|
18
18
|
Requires-Dist: autograd>=1.5; extra == "notebook-dependencies"
|
|
19
19
|
Requires-Dist: jupyter>1.0.0; extra == "notebook-dependencies"
|
|
20
20
|
Requires-Dist: notebook>6.5.2; extra == "notebook-dependencies"
|
|
21
|
+
Requires-Dist: matplotlib>=3.6.0; extra == "notebook-dependencies"
|
|
21
22
|
|
|
22
23
|
[](https://github.com/LDEO-CREW/Pythonic-DISORT/actions/workflows/continuous_integration.yml)
|
|
23
24
|
[](https://doi.org/10.21105/joss.06442)
|
|
@@ -40,7 +41,7 @@ You may contact me, Dion, through dh3065@columbia.edu.
|
|
|
40
41
|
|
|
41
42
|
The **GitHub repository** is https://github.com/LDEO-CREW/Pythonic-DISORT.
|
|
42
43
|
|
|
43
|
-
Accompanying Journal of Open Source Software paper: https://joss.theoj.org/papers/10.21105/joss.06442.
|
|
44
|
+
Accompanying Journal of Open Source Software paper: https://joss.theoj.org/papers/10.21105/joss.06442. **Please cite this paper if you use PythonicDISORT**.
|
|
44
45
|
|
|
45
46
|
# Documentation
|
|
46
47
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
@@ -70,18 +71,19 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
70
71
|
* Test Problem 8: Absorbing / Isotropic-Scattering Medium (multiple layers)
|
|
71
72
|
* Test Problem 9: General Emitting / Absorbing / Scattering Medium (multiple layers)
|
|
72
73
|
* Test Problem 11: Single-Layer vs. Multiple Layers (no corresponding Jupyter Notebook)
|
|
74
|
+
* Test Problem I: Antiderivative / integration functionality (no corresponding Jupyter Notebook)
|
|
73
75
|
|
|
74
76
|
# Installation
|
|
75
77
|
|
|
76
78
|
* From PyPI: `pip install PythonicDISORT`
|
|
77
|
-
* From Conda-
|
|
79
|
+
* From Conda-Forge: `conda install -c conda-forge pythonicdisort` or `mamba install -c conda-forge pythonicdisort`
|
|
78
80
|
* By cloning repository: `pip install .` in the `Pythonic-DISORT` directory; `pip install -r all_optional_dependencies.txt` to install all optional dependencies (see *Requirements to run PythonicDISORT*)
|
|
79
81
|
|
|
80
82
|
## Requirements to run PythonicDISORT
|
|
81
83
|
* Python 3.8+
|
|
82
84
|
* `numpy >= 1.8.0`
|
|
83
85
|
* `scipy >= 1.8.0`
|
|
84
|
-
* (OPTIONAL) `autograd >= 1.5`
|
|
86
|
+
* (OPTIONAL) `autograd >= 1.5` (`autograd` can be used to compute gradients of the output functions)
|
|
85
87
|
* (OPTIONAL) `pytest >= 6.2.5` (Required to use the command `pytest`, see *PyTest and examples of how to use PythonicDISORT*)
|
|
86
88
|
|
|
87
89
|
## (OPTIONAL) Additional requirements to run the Jupyter Notebook
|
|
@@ -89,7 +91,8 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
89
91
|
* `notebook > 6.5.2`
|
|
90
92
|
* `matplotlib >= 3.6.0`
|
|
91
93
|
|
|
92
|
-
In addition, a F2PY-wrapped Stamnes' DISORT
|
|
94
|
+
In addition, a F2PY-wrapped Stamnes' DISORT or equivalent (https://github.com/kconnour/pyRT_DISORT may work)
|
|
95
|
+
is required to properly run the last section (section 6).
|
|
93
96
|
|
|
94
97
|
## Compatibility
|
|
95
98
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|