PythonicDISORT 1.5__tar.gz → 1.7__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.5/src/PythonicDISORT.egg-info → pythonicdisort-1.7}/PKG-INFO +3 -3
- {pythonicdisort-1.5 → pythonicdisort-1.7}/README.md +2 -2
- {pythonicdisort-1.5 → pythonicdisort-1.7}/pyproject.toml +1 -1
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py +19 -22
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT/_solve_for_coeffs.py +62 -69
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT/_solve_for_gen_and_part_sols.py +41 -61
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT/pydisort.py +155 -180
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT/subroutines.py +35 -20
- {pythonicdisort-1.5 → pythonicdisort-1.7/src/PythonicDISORT.egg-info}/PKG-INFO +3 -3
- {pythonicdisort-1.5 → pythonicdisort-1.7}/LICENSE.md +0 -0
- {pythonicdisort-1.5 → pythonicdisort-1.7}/setup.cfg +0 -0
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT/__init__.py +0 -0
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT.egg-info/SOURCES.txt +0 -0
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT.egg-info/dependency_links.txt +0 -0
- {pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT.egg-info/requires.txt +0 -0
- {pythonicdisort-1.5 → pythonicdisort-1.7}/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.7
|
|
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
|
|
@@ -41,7 +41,7 @@ You may contact me, Dion, through dh3065@columbia.edu.
|
|
|
41
41
|
|
|
42
42
|
The **GitHub repository** is https://github.com/LDEO-CREW/Pythonic-DISORT.
|
|
43
43
|
|
|
44
|
-
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**.
|
|
45
45
|
|
|
46
46
|
# Documentation
|
|
47
47
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
@@ -76,7 +76,7 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
76
76
|
# Installation
|
|
77
77
|
|
|
78
78
|
* From PyPI: `pip install PythonicDISORT`
|
|
79
|
-
* From Conda-
|
|
79
|
+
* From Conda-Forge: `conda install -c conda-forge pythonicdisort` or `mamba install -c conda-forge pythonicdisort`
|
|
80
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*)
|
|
81
81
|
|
|
82
82
|
## Requirements to run PythonicDISORT
|
|
@@ -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/
|
|
@@ -54,7 +54,7 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
54
54
|
# Installation
|
|
55
55
|
|
|
56
56
|
* From PyPI: `pip install PythonicDISORT`
|
|
57
|
-
* From Conda-
|
|
57
|
+
* From Conda-Forge: `conda install -c conda-forge pythonicdisort` or `mamba install -c conda-forge pythonicdisort`
|
|
58
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*)
|
|
59
59
|
|
|
60
60
|
## Requirements to run PythonicDISORT
|
{pythonicdisort-1.5 → pythonicdisort-1.7}/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.
|
|
@@ -182,12 +182,12 @@ def _assemble_intensity_and_fluxes(
|
|
|
182
182
|
raise ValueError("tau input outside the tau range specified for the atmosphere (check `tau_arr`).")
|
|
183
183
|
|
|
184
184
|
# Atmospheric layer indices
|
|
185
|
-
l = np.
|
|
185
|
+
l = np.searchsorted(tau_arr, tau, side="left")
|
|
186
186
|
scaled_tau_arr_l = scaled_tau_arr_with_0[1:][l]
|
|
187
187
|
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
188
188
|
|
|
189
189
|
# Delta-M scaling
|
|
190
|
-
if np.any(scale_tau !=
|
|
190
|
+
if np.any(scale_tau != 1):
|
|
191
191
|
tau_dist_from_top = tau_arr[l] - tau
|
|
192
192
|
scaled_tau_dist_from_top = tau_dist_from_top * scale_tau[l]
|
|
193
193
|
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
@@ -236,7 +236,8 @@ 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, scale_tau,
|
|
239
|
+
scaled_tau, scale_tau[l_uniq], # `scale_tau` is only required to determine the antiderivative
|
|
240
|
+
l_inv,
|
|
240
241
|
Nscoeffs,
|
|
241
242
|
scaled_s_poly_coeffs[l_uniq],
|
|
242
243
|
G_collect_0[l_uniq],
|
|
@@ -318,13 +319,6 @@ def _assemble_intensity_and_fluxes(
|
|
|
318
319
|
if return_tau_arr:
|
|
319
320
|
outputs += (tau_arr,)
|
|
320
321
|
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
322
|
|
|
329
323
|
return outputs if output_is_tuple else outputs[0]
|
|
330
324
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
@@ -346,12 +340,12 @@ def _assemble_intensity_and_fluxes(
|
|
|
346
340
|
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
347
341
|
|
|
348
342
|
# Atmospheric layer indices
|
|
349
|
-
l = np.
|
|
343
|
+
l = np.searchsorted(tau_arr, tau, side="left")
|
|
350
344
|
scaled_tau_arr_l = scaled_tau_arr_with_0[1:][l]
|
|
351
345
|
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
352
346
|
|
|
353
347
|
# Delta-M scaling
|
|
354
|
-
if np.any(scale_tau !=
|
|
348
|
+
if np.any(scale_tau != 1):
|
|
355
349
|
tau_dist_from_top = tau_arr[l] - tau
|
|
356
350
|
scaled_tau_dist_from_top = tau_dist_from_top * scale_tau[l]
|
|
357
351
|
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
@@ -411,7 +405,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
411
405
|
if there_is_iso_source:
|
|
412
406
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
413
407
|
_mathscr_v_contribution = _mathscr_v(
|
|
414
|
-
scaled_tau, scale_tau,
|
|
408
|
+
scaled_tau, scale_tau[l_uniq], # `scale_tau` is only required to determine the antiderivative
|
|
409
|
+
l_inv,
|
|
415
410
|
Nscoeffs,
|
|
416
411
|
scaled_s_poly_coeffs[l_uniq],
|
|
417
412
|
G_collect_0[l_uniq],
|
|
@@ -455,12 +450,12 @@ def _assemble_intensity_and_fluxes(
|
|
|
455
450
|
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
456
451
|
|
|
457
452
|
# Atmospheric layer indices
|
|
458
|
-
l = np.
|
|
453
|
+
l = np.searchsorted(tau_arr, tau, side="left")
|
|
459
454
|
scaled_tau_arr_l = scaled_tau_arr_with_0[1:][l]
|
|
460
455
|
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
461
456
|
|
|
462
457
|
# Delta-M scaling
|
|
463
|
-
if np.any(scale_tau !=
|
|
458
|
+
if np.any(scale_tau != 1):
|
|
464
459
|
tau_dist_from_top = tau_arr[l] - tau
|
|
465
460
|
scaled_tau_dist_from_top = tau_dist_from_top * scale_tau[l]
|
|
466
461
|
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
@@ -470,7 +465,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
470
465
|
if there_is_iso_source:
|
|
471
466
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
472
467
|
_mathscr_v_contribution = _mathscr_v(
|
|
473
|
-
scaled_tau, scale_tau,
|
|
468
|
+
scaled_tau, scale_tau[l_uniq], # `scale_tau` is only required to determine the antiderivative
|
|
469
|
+
l_inv,
|
|
474
470
|
Nscoeffs,
|
|
475
471
|
scaled_s_poly_coeffs[l_uniq],
|
|
476
472
|
G_collect_0[l_uniq, :N, :],
|
|
@@ -537,12 +533,12 @@ def _assemble_intensity_and_fluxes(
|
|
|
537
533
|
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
538
534
|
|
|
539
535
|
# Atmospheric layer indices
|
|
540
|
-
l = np.
|
|
536
|
+
l = np.searchsorted(tau_arr, tau, side="left")
|
|
541
537
|
scaled_tau_arr_l = scaled_tau_arr_with_0[1:][l]
|
|
542
538
|
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
543
539
|
|
|
544
540
|
# Delta-M scaling
|
|
545
|
-
if np.any(scale_tau !=
|
|
541
|
+
if np.any(scale_tau != 1):
|
|
546
542
|
tau_dist_from_top = tau_arr[l] - tau
|
|
547
543
|
scaled_tau_dist_from_top = tau_dist_from_top * scale_tau[l]
|
|
548
544
|
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
@@ -552,7 +548,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
552
548
|
if there_is_iso_source:
|
|
553
549
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
554
550
|
_mathscr_v_contribution = _mathscr_v(
|
|
555
|
-
scaled_tau, scale_tau,
|
|
551
|
+
scaled_tau, scale_tau[l_uniq], # `scale_tau` is only required to determine the antiderivative
|
|
552
|
+
l_inv,
|
|
556
553
|
Nscoeffs,
|
|
557
554
|
scaled_s_poly_coeffs[l_uniq],
|
|
558
555
|
G_collect_0[l_uniq, N:, :],
|
|
@@ -577,9 +574,9 @@ def _assemble_intensity_and_fluxes(
|
|
|
577
574
|
direct_beam_scaled = 0
|
|
578
575
|
if is_antiderivative_wrt_tau:
|
|
579
576
|
if there_is_beam_source:
|
|
580
|
-
direct_beam_contribution = (B_neg / (-scale_tau / mu0)[None,
|
|
577
|
+
direct_beam_contribution = (B_neg / (-scale_tau / mu0)[None, :])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
581
578
|
direct_beam = I0 * mu0 * np.exp(-tau / mu0) * -mu0
|
|
582
|
-
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau / mu0)
|
|
579
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau[l] / mu0)
|
|
583
580
|
|
|
584
581
|
u0_neg = (
|
|
585
582
|
np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :])
|
|
@@ -79,7 +79,8 @@ def _solve_for_coeffs(
|
|
|
79
79
|
use_banded_solver = (NLayers >= use_banded_solver_NLayers)
|
|
80
80
|
dim = NLayers * NQuad
|
|
81
81
|
C_collect = np.empty((NFourier, dim))
|
|
82
|
-
|
|
82
|
+
thickness = np.diff(scaled_tau_arr_with_0) # (NLayers,)
|
|
83
|
+
|
|
83
84
|
RHS = np.empty(dim)
|
|
84
85
|
i_bot = dim - N # Equals `j_bot_right`
|
|
85
86
|
j_bot_left = dim - NQuad
|
|
@@ -89,23 +90,47 @@ def _solve_for_coeffs(
|
|
|
89
90
|
LHS_dof = np.zeros((6 * N - 1, dim), order="F") # Diagonal ordered form
|
|
90
91
|
s0, s1 = LHS_dof.strides
|
|
91
92
|
col_stride = s1 - s0 # Stride when moving +1 col and -1 row
|
|
92
|
-
|
|
93
|
+
|
|
93
94
|
def _view_slanted(i0, j0, nrows):
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
"""Single slanted view into `LHS_dof` anchored at conceptual LHS coords (i0, j0).
|
|
96
|
+
Returns a 2D view of shape (nrows, N). Used for top/bottom boundary-condition rows.
|
|
97
|
+
|
|
98
|
+
Uses `numpy.lib.stride_tricks.as_strided` (zero-copy but unsafe), which is why
|
|
99
|
+
this helper is not exposed in `PythonicDISORT.subroutines`.
|
|
98
100
|
See https://numpy.org/devdocs/reference/generated/numpy.lib.stride_tricks.as_strided.html.
|
|
99
|
-
|
|
101
|
+
|
|
100
102
|
Assume the following exist in the outer scope:
|
|
101
|
-
`LHS_dof`, `Nsupsubdiags`, `s0`, `col_stride
|
|
103
|
+
`LHS_dof`, `Nsupsubdiags`, `N`, `s0`, `col_stride`.
|
|
102
104
|
"""
|
|
103
105
|
r0 = Nsupsubdiags + i0 - j0
|
|
104
106
|
base = LHS_dof[r0 : r0 + nrows, j0 : j0 + N]
|
|
105
107
|
return as_strided(base, shape=(nrows, N), strides=(s0, col_stride))
|
|
106
|
-
|
|
108
|
+
|
|
109
|
+
def _view_slanted_batched(i0, j0, nrows):
|
|
110
|
+
"""Batched (NLayers-1, nrows, N) slanted view across interlayer continuity rows.
|
|
111
|
+
Anchored at conceptual LHS coords (i0, j0) for layer 0; layer l advances both
|
|
112
|
+
i and j by `NQuad` along the diagonal.
|
|
113
|
+
|
|
114
|
+
Same `(i0, j0)` convention as `_view_slanted` (slant arithmetic done internally).
|
|
115
|
+
Assume the following exist in the outer scope:
|
|
116
|
+
`LHS_dof`, `Nsupsubdiags`, `N`, `NQuad`, `NLayers`, `s0`, `s1`, `col_stride`.
|
|
117
|
+
"""
|
|
118
|
+
r0 = Nsupsubdiags + i0 - j0 # matches `_view_slanted`'s internal slant
|
|
119
|
+
base = LHS_dof[r0 : r0 + nrows, j0 : j0 + N + (NLayers - 2) * NQuad]
|
|
120
|
+
return as_strided(base, shape=(NLayers - 1, nrows, N),
|
|
121
|
+
strides=(NQuad * s1, s0, col_stride))
|
|
122
|
+
|
|
107
123
|
else:
|
|
108
124
|
LHS = np.zeros((dim, dim), order="F")
|
|
125
|
+
sR, sC = LHS.strides
|
|
126
|
+
def _dense_slab(r0, c0, nrows, ncols):
|
|
127
|
+
"""Batched (NLayers-1, nrows, ncols) view across interlayer blocks of `LHS`.
|
|
128
|
+
Anchored at LHS coords (r0, c0) for layer 0; layer l advances both row and column
|
|
129
|
+
anchors by `NQuad` along the diagonal. Each slab has shape (nrows, ncols)."""
|
|
130
|
+
base = LHS[r0 : r0 + nrows + (NLayers - 2) * NQuad,
|
|
131
|
+
c0 : c0 + ncols + (NLayers - 2) * NQuad]
|
|
132
|
+
return as_strided(base, shape=(NLayers - 1, nrows, ncols),
|
|
133
|
+
strides=(NQuad * (sR + sC), sR, sC))
|
|
109
134
|
|
|
110
135
|
# The following loops can easily be parallelized, but the speed-up is unlikely to be worth the overhead
|
|
111
136
|
for m in range(NFourier):
|
|
@@ -262,7 +287,7 @@ def _solve_for_coeffs(
|
|
|
262
287
|
G_0_nn = G_0[N:, :N]
|
|
263
288
|
G_0_np = G_0[N:, N:]
|
|
264
289
|
E_01 = np.exp(K_collect_m[0, :N] * scaled_tau_arr_with_0[1])[None, :]
|
|
265
|
-
|
|
290
|
+
|
|
266
291
|
G_L = G_collect_m[-1]
|
|
267
292
|
G_L_pn = G_L[:N, :N]
|
|
268
293
|
G_L_nn = G_L[N:, :N]
|
|
@@ -271,6 +296,9 @@ def _solve_for_coeffs(
|
|
|
271
296
|
E_Lm1L = np.exp(
|
|
272
297
|
K_collect_m[-1, :N] * (scaled_tau_arr_with_0[-1] - scaled_tau_arr_with_0[-2])
|
|
273
298
|
)[None, :]
|
|
299
|
+
# Per-layer thickness exponentials, computed once per Fourier mode and reused below.
|
|
300
|
+
# If looping over `l in range(NLayers)`, the line below would be `E_pos_full[l] = exp(-thickness[l] * K_pos[l])`
|
|
301
|
+
E_pos_full = np.exp(-thickness[:, None] * K_collect_m[:, N:]) # (NLayers, N)
|
|
274
302
|
|
|
275
303
|
################################## Use `sc.linalg.solve_banded` ##################################
|
|
276
304
|
if use_banded_solver:
|
|
@@ -292,35 +320,18 @@ def _solve_for_coeffs(
|
|
|
292
320
|
_view_slanted(i_bot, j_bot_left, N)[:] = G_L_pn * E_Lm1L
|
|
293
321
|
_view_slanted(i_bot, i_bot, N)[:] = G_L_pp
|
|
294
322
|
|
|
295
|
-
# ---------------- Interlayer / continuity BCs ----------------
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
G_l_ap = G_l[:, N:] # (2N,N)
|
|
301
|
-
|
|
302
|
-
G_lp1 = G_collect_m[l + 1]
|
|
303
|
-
G_lp1_an = G_lp1[:, :N] # (2N,N)
|
|
304
|
-
G_lp1_pp = G_lp1[:N, N:] # (N,N)
|
|
305
|
-
G_lp1_np = G_lp1[N:, N:] # (N,N)
|
|
306
|
-
|
|
307
|
-
tau_lm1 = scaled_tau_arr_with_0[l]
|
|
308
|
-
tau_l = scaled_tau_arr_with_0[l + 1]
|
|
309
|
-
tau_lp1 = scaled_tau_arr_with_0[l + 2]
|
|
310
|
-
|
|
311
|
-
E_lm1l = np.exp(K_collect_m[l, N:] * (tau_lm1 - tau_l))[None, :] # (N,)
|
|
312
|
-
E_llp1 = np.exp(K_collect_m[l + 1, N:] * (tau_l - tau_lp1))[None, :] # (N,)
|
|
323
|
+
# ---------------- Interlayer / continuity BCs (batched across layers) ----------------
|
|
324
|
+
E_lm1l = E_pos_full[:-1, None, :] # (NLayers-1, 1, N)
|
|
325
|
+
E_llp1 = E_pos_full[1:, None, :] # (NLayers-1, 1, N)
|
|
326
|
+
G_lo = G_collect_m[:-1] # (NLayers-1, NQuad, NQuad)
|
|
327
|
+
G_hi = G_collect_m[1:] # (NLayers-1, NQuad, NQuad)
|
|
313
328
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
_view_slanted(start_row, start_col + NQuad, NQuad)[:] = -G_lp1_an
|
|
322
|
-
_view_slanted(start_row, start_col + 3 * N, N)[:] = -G_lp1_pp * E_llp1
|
|
323
|
-
_view_slanted(start_row + N, start_col + 3 * N, N)[:] = -G_lp1_np * E_llp1
|
|
329
|
+
_view_slanted_batched(N, 0, N)[:] = G_lo[:, :N, :N] * E_lm1l
|
|
330
|
+
_view_slanted_batched(2 * N, 0, N)[:] = G_lo[:, N:, :N] * E_lm1l
|
|
331
|
+
_view_slanted_batched(N, N, NQuad)[:] = G_lo[:, :, N:]
|
|
332
|
+
_view_slanted_batched(N, NQuad, NQuad)[:] = -G_hi[:, :, :N]
|
|
333
|
+
_view_slanted_batched(N, 3 * N, N)[:] = -G_hi[:, :N, N:] * E_llp1
|
|
334
|
+
_view_slanted_batched(2 * N, 3 * N, N)[:] = -G_hi[:, N:, N:] * E_llp1
|
|
324
335
|
|
|
325
336
|
# Solve the system
|
|
326
337
|
C_m = sc.linalg.solve_banded(
|
|
@@ -329,7 +340,7 @@ def _solve_for_coeffs(
|
|
|
329
340
|
RHS,
|
|
330
341
|
False,
|
|
331
342
|
True,
|
|
332
|
-
|
|
343
|
+
True,
|
|
333
344
|
)
|
|
334
345
|
|
|
335
346
|
##################################################################################################
|
|
@@ -348,36 +359,18 @@ def _solve_for_coeffs(
|
|
|
348
359
|
LHS[-N:, -NQuad : -N] = G_L_pn * E_Lm1L
|
|
349
360
|
LHS[-N:, -N:] = G_L_pp
|
|
350
361
|
|
|
351
|
-
# ---------------- Interlayer / continuity BCs ----------------
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
|
|
364
|
-
scaled_tau_arr_l = scaled_tau_arr_with_0[l + 1]
|
|
365
|
-
scaled_tau_arr_lp1 = scaled_tau_arr_with_0[l + 2]
|
|
366
|
-
|
|
367
|
-
# Postive eigenvalues
|
|
368
|
-
E_lm1l = np.exp(K_collect_m[l, N:] * (scaled_tau_arr_lm1 - scaled_tau_arr_l))
|
|
369
|
-
E_llp1 = np.exp(K_collect_m[l + 1, N:] * (scaled_tau_arr_l - scaled_tau_arr_lp1))
|
|
370
|
-
|
|
371
|
-
start_row = N + l * NQuad
|
|
372
|
-
start_col = l * NQuad
|
|
373
|
-
|
|
374
|
-
LHS[start_row : N + start_row, start_col : N + start_col] = G_l_pn * E_lm1l
|
|
375
|
-
LHS[N + start_row : NQuad + start_row, start_col : N + start_col] = G_l_nn * E_lm1l
|
|
376
|
-
LHS[start_row : NQuad + start_row, N + start_col : NQuad + start_col] = G_l_ap
|
|
377
|
-
|
|
378
|
-
LHS[start_row : NQuad + start_row, NQuad + start_col : 3 * N + start_col] = -G_lp1_an
|
|
379
|
-
LHS[start_row : N + start_row, 3 * N + start_col : 4 * N + start_col] = -G_lp1_pp * E_llp1
|
|
380
|
-
LHS[N + start_row : NQuad + start_row, 3 * N + start_col : 4 * N + start_col] = -G_lp1_np * E_llp1
|
|
362
|
+
# ---------------- Interlayer / continuity BCs (batched across layers) ----------------
|
|
363
|
+
E_lm1l = E_pos_full[:-1, None, :] # (NLayers-1, 1, N)
|
|
364
|
+
E_llp1 = E_pos_full[1:, None, :] # (NLayers-1, 1, N)
|
|
365
|
+
G_lo = G_collect_m[:-1]
|
|
366
|
+
G_hi = G_collect_m[1:]
|
|
367
|
+
|
|
368
|
+
_dense_slab(N, 0, N, N)[:] = G_lo[:, :N, :N] * E_lm1l
|
|
369
|
+
_dense_slab(2 * N, 0, N, N)[:] = G_lo[:, N:, :N] * E_lm1l
|
|
370
|
+
_dense_slab(N, N, NQuad, N)[:] = G_lo[:, :, N:]
|
|
371
|
+
_dense_slab(N, NQuad, NQuad, N)[:] = -G_hi[:, :, :N]
|
|
372
|
+
_dense_slab(N, 3 * N, N, N)[:] = -G_hi[:, :N, N:] * E_llp1
|
|
373
|
+
_dense_slab(2 * N, 3 * N, N, N)[:] = -G_hi[:, N:, N:] * E_llp1
|
|
381
374
|
|
|
382
375
|
# Solve the system
|
|
383
376
|
C_m = np.linalg.solve(LHS, RHS)
|
{pythonicdisort-1.5 → pythonicdisort-1.7}/src/PythonicDISORT/_solve_for_gen_and_part_sols.py
RENAMED
|
@@ -69,13 +69,14 @@ def _solve_for_gen_and_part_sols(
|
|
|
69
69
|
G_trivial.ravel()[N : N + N * stride : stride] = 1
|
|
70
70
|
G_trivial.ravel()[NQuad * N : NQuad * N + N * stride : stride] = 1
|
|
71
71
|
|
|
72
|
+
# Flat (NFourier * NLayers, ...) storage so a single integer index list can address
|
|
73
|
+
# every (m, l) pair across Fourier modes for the cross-mode batched `eig`.
|
|
72
74
|
G_collect = np.empty((NFourier * NLayers, NQuad, NQuad))
|
|
73
75
|
K_collect = np.empty((NFourier * NLayers, NQuad))
|
|
74
76
|
A_arr = np.empty((NFourier * NLayers, NQuad, NQuad))
|
|
75
77
|
no_shortcut_indices = []
|
|
76
78
|
no_shortcut_indices_0 = []
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
|
|
79
80
|
if there_is_beam_source:
|
|
80
81
|
mu_arr_pos_mu0 = np.append(mu_arr_pos, -mu0)
|
|
81
82
|
|
|
@@ -109,66 +110,45 @@ def _solve_for_gen_and_part_sols(
|
|
|
109
110
|
fac_asso_leg_term_posT = poch[None, :] * asso_leg_term_pos.T
|
|
110
111
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
111
112
|
|
|
112
|
-
#
|
|
113
|
-
# --------------------------------------------------------------------------------------------------------------------------
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
# Vectorized over NLayers atmospheric layers
|
|
114
|
+
# --------------------------------------------------------------------------------------------------------------------------
|
|
115
|
+
omega_times_Leg = (scaled_omega_arr[:, None] / 2) * weighted_scaled_Leg_coeffs[:, m:] # (NLayers, NLeg-m)
|
|
116
|
+
no_shortcut = np.any(np.abs(omega_times_Leg) > 1e-8, axis=1) # (NLayers,)
|
|
117
|
+
ns_ind = np.flatnonzero(no_shortcut)
|
|
118
|
+
sh_ind = np.flatnonzero(~no_shortcut)
|
|
119
|
+
ind_base = m * NLayers
|
|
120
|
+
|
|
121
|
+
# Shortcut layers: trivial diagonalization (no multiple scattering)
|
|
122
|
+
if sh_ind.size:
|
|
123
|
+
G_collect[ind_base + sh_ind] = G_trivial
|
|
124
|
+
K_collect[ind_base + sh_ind, :N] = -M_inv
|
|
125
|
+
K_collect[ind_base + sh_ind, N:] = M_inv
|
|
126
|
+
if m_equals_0 and there_is_iso_source:
|
|
127
|
+
G_inv_collect_0[sh_ind] = G_trivial
|
|
128
|
+
|
|
129
|
+
# Non-shortcut layers: full computation, batched over NLayers
|
|
130
|
+
if ns_ind.size:
|
|
131
|
+
D_temp = omega_times_Leg[ns_ind][:, None, :] * fac_asso_leg_term_posT[None, :, :] # (Nns, N, NLeg-m)
|
|
132
|
+
D_pos = D_temp @ asso_leg_term_pos # (Nns, N, N)
|
|
133
|
+
D_neg = D_temp @ asso_leg_term_neg # (Nns, N, N)
|
|
134
|
+
DW = D_pos * W[None, None, :]
|
|
135
|
+
DW.reshape(-1, N * N)[:, ::N + 1] -= 1 # batched diag-sub
|
|
136
|
+
inds = ind_base + ns_ind
|
|
137
|
+
A_arr[inds, N:, N:] = M_inv[None, :, None] * DW # alpha
|
|
138
|
+
A_arr[inds, N:, :N] = M_inv[None, :, None] * D_neg * W[None, None, :] # beta
|
|
139
|
+
no_shortcut_indices.extend(inds.tolist())
|
|
140
|
+
if m_equals_0 and there_is_iso_source:
|
|
141
|
+
no_shortcut_indices_0.extend(ns_ind.tolist())
|
|
142
|
+
if there_is_beam_source:
|
|
143
|
+
X_temp = (
|
|
144
|
+
scalar_fac_asso_leg_term_mu0
|
|
145
|
+
* scaled_omega_arr[ns_ind, None]
|
|
146
|
+
* weighted_scaled_Leg_coeffs[ns_ind, m:]
|
|
147
|
+
)
|
|
148
|
+
# Signs flipped due to the extra minus sign
|
|
149
|
+
X_arr[inds, :N] = M_inv * (X_temp @ asso_leg_term_pos)
|
|
150
|
+
X_arr[inds, N:] = -M_inv * (X_temp @ asso_leg_term_neg)
|
|
129
151
|
|
|
130
|
-
# Assemble the coefficient matrix and additional terms
|
|
131
|
-
# --------------------------------------------------------------------------------------------------------------------------
|
|
132
|
-
DW = D_pos * W[None, :]
|
|
133
|
-
DW.ravel()[::N + 1] -= 1
|
|
134
|
-
alpha = M_inv[:, None] * DW
|
|
135
|
-
beta = M_inv[:, None] * D_neg * W[None, :]
|
|
136
|
-
|
|
137
|
-
# --------------------------------------------------------------------------------------------------------------------------
|
|
138
|
-
|
|
139
|
-
# Setup to solve for particular solution w.r.t. direct beam source (see section 3.6.1 of the Comprehensive Documentation)
|
|
140
|
-
# --------------------------------------------------------------------------------------------------------------------------
|
|
141
|
-
if there_is_beam_source:
|
|
142
|
-
# Generate X
|
|
143
|
-
X_temp = (
|
|
144
|
-
scalar_fac_asso_leg_term_mu0
|
|
145
|
-
* scaled_omega_l
|
|
146
|
-
* weighted_scaled_Leg_coeffs_l
|
|
147
|
-
)
|
|
148
|
-
X_pos = X_temp @ asso_leg_term_pos
|
|
149
|
-
X_neg = X_temp @ asso_leg_term_neg
|
|
150
|
-
# Signs flipped due to the extra minus sign
|
|
151
|
-
X_arr[ind, :N] = M_inv * X_pos
|
|
152
|
-
X_arr[ind, N:] = -M_inv * X_neg
|
|
153
|
-
|
|
154
|
-
# --------------------------------------------------------------------------------------------------------------------------
|
|
155
|
-
|
|
156
|
-
A_arr[ind, N:, :N] = beta
|
|
157
|
-
A_arr[ind, N:, N:] = alpha
|
|
158
|
-
no_shortcut_indices.append(ind)
|
|
159
|
-
if m_equals_0 and there_is_iso_source: # Keep the list empty if there is no isotropic source
|
|
160
|
-
no_shortcut_indices_0.append(l)
|
|
161
|
-
|
|
162
|
-
else:
|
|
163
|
-
# This is a shortcut to the diagonalization results
|
|
164
|
-
G_collect[ind, :, :] = G_trivial
|
|
165
|
-
K_collect[ind, :N] = -M_inv
|
|
166
|
-
K_collect[ind, N:] = M_inv
|
|
167
|
-
if m_equals_0 and there_is_iso_source:
|
|
168
|
-
G_inv_collect_0[l, :, :] = G_trivial
|
|
169
|
-
|
|
170
|
-
ind += 1
|
|
171
|
-
|
|
172
152
|
if len(no_shortcut_indices) > 0:
|
|
173
153
|
A_arr = A_arr[no_shortcut_indices, :, :]
|
|
174
154
|
alpha_arr = A_arr[:, N:, N:]
|
|
@@ -6,7 +6,7 @@ import warnings
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import scipy as sc
|
|
8
8
|
from math import pi
|
|
9
|
-
from numpy.polynomial.legendre import Legendre
|
|
9
|
+
from numpy.polynomial.legendre import Legendre, legval
|
|
10
10
|
from scipy import integrate
|
|
11
11
|
|
|
12
12
|
|
|
@@ -406,68 +406,59 @@ 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
|
-
# Atmospheric layer indices
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
scaled_tau_arr_l = scaled_tau_arr_with_0[1:][l]
|
|
412
|
+
# Atmospheric layer indices: smallest l with tau <= tau_arr[l].
|
|
413
|
+
l = np.searchsorted(tau_arr, tau, side="left")
|
|
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 != 1):
|
|
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
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
425
427
|
nu = subroutines.atleast_2d_append(
|
|
426
428
|
subroutines.calculate_nu(mu_arr, phi, -mu0, phi0)
|
|
427
429
|
)
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
) # (NQuad, NLayers_uniq, Nphi)
|
|
436
|
-
p_trun = np.concatenate(
|
|
437
|
-
[
|
|
438
|
-
f(nu)[:, None, :]
|
|
439
|
-
# Iterates over the 0th axis
|
|
440
|
-
for f in map(Legendre, weighted_scaled_Leg_coeffs[l_uniq])
|
|
441
|
-
],
|
|
442
|
-
axis=1,
|
|
443
|
-
) # (NQuad, NLayers_uniq, Nphi)
|
|
444
|
-
|
|
430
|
+
# Vectorized Legendre evaluation: legval broadcasts the trailing-axis
|
|
431
|
+
# coefficient set, returning (NLayers, NQuad, Nphi). Move the layer axis
|
|
432
|
+
# to the middle to match the original (NQuad, NLayers, Nphi) layout.
|
|
433
|
+
# Always uses base numpy's legval (not autograd's): this matches the
|
|
434
|
+
# original `Legendre(...)(nu)` path, which was also pure numpy.
|
|
435
|
+
p_true = np.moveaxis(legval(nu, weighted_Leg_coeffs_all.T, tensor=True), 0, 1)
|
|
436
|
+
p_trun = np.moveaxis(legval(nu, weighted_scaled_Leg_coeffs.T, tensor=True), 0, 1)
|
|
445
437
|
mathscr_B = (
|
|
446
|
-
(scaled_omega_arr
|
|
438
|
+
(scaled_omega_arr * I0_div_4pi)[None, :, None]
|
|
447
439
|
* (mu0 / (mu0 + mu_arr))[:, None, None]
|
|
448
|
-
* (p_true / (1 - f_arr
|
|
449
|
-
)
|
|
440
|
+
* (p_true / (1 - f_arr[None, :, None]) - p_trun)
|
|
441
|
+
)
|
|
450
442
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
451
443
|
|
|
452
444
|
neg_scaled_tau_div_mu0 = -scaled_tau / mu0
|
|
453
445
|
if is_antiderivative_wrt_tau:
|
|
454
446
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
scale_tau_l_div_mu_arr_pos = scale_tau_l[None, :] / mu_arr_pos[:, None]
|
|
447
|
+
neg_scale_tau_div_mu0 = -scale_tau / mu0
|
|
448
|
+
scale_tau_div_mu_arr_pos = scale_tau[None, :] / mu_arr_pos[:, None]
|
|
458
449
|
|
|
459
450
|
TMS_correction_pos = (
|
|
460
|
-
np.exp(neg_scaled_tau_div_mu0) /
|
|
451
|
+
np.exp(neg_scaled_tau_div_mu0) / neg_scale_tau_div_mu0[l]
|
|
461
452
|
)[None, :] - np.exp(
|
|
462
453
|
(scaled_tau - scaled_tau_arr_l)[None, :] / mu_arr_pos[:, None]
|
|
463
454
|
- scaled_tau_arr_l[None, :] / mu0
|
|
464
|
-
) /
|
|
455
|
+
) / scale_tau_div_mu_arr_pos[:, l]
|
|
465
456
|
TMS_correction_neg = (
|
|
466
|
-
np.exp(neg_scaled_tau_div_mu0) /
|
|
457
|
+
np.exp(neg_scaled_tau_div_mu0) / neg_scale_tau_div_mu0[l]
|
|
467
458
|
)[None, :] + np.exp(
|
|
468
459
|
(scaled_tau_arr_lm1 - scaled_tau)[None, :] / mu_arr_pos[:, None]
|
|
469
460
|
- scaled_tau_arr_lm1[None, :] / mu0
|
|
470
|
-
) /
|
|
461
|
+
) / scale_tau_div_mu_arr_pos[:, l]
|
|
471
462
|
else:
|
|
472
463
|
TMS_correction_pos = np.exp(neg_scaled_tau_div_mu0)[None, :] - np.exp(
|
|
473
464
|
(scaled_tau - scaled_tau_arr_l)[None, :] / mu_arr_pos[:, None]
|
|
@@ -477,123 +468,140 @@ def pydisort(
|
|
|
477
468
|
(scaled_tau_arr_lm1 - scaled_tau)[None, :] / mu_arr_pos[:, None]
|
|
478
469
|
- scaled_tau_arr_lm1[None, :] / mu0
|
|
479
470
|
)
|
|
480
|
-
|
|
481
471
|
|
|
482
472
|
## Contribution from other layers
|
|
483
473
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
* np.concatenate((TMS_correction_pos, TMS_correction_neg), axis=0)[:, :, None]
|
|
487
|
-
)
|
|
474
|
+
Bpos = mathscr_B[:N, :, :] # (N, NLayers, Nphi)
|
|
475
|
+
Bneg = mathscr_B[N:, :, :] # (N, NLayers, Nphi)
|
|
488
476
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
scaled_tauwith0_front = scaled_tau_arr_with_0[:-1] # (NLayers,)
|
|
496
|
-
scaled_tauwith0_back = scaled_tau_arr_with_0[1:] # (NLayers,); i.e. `scaled_tau_arr`
|
|
477
|
+
sol_pos = Bpos[:, l, :] * TMS_correction_pos[:, :, None] # (N, Ntau, Nphi)
|
|
478
|
+
sol_neg = Bneg[:, l, :] * TMS_correction_neg[:, :, None] # (N, Ntau, Nphi)
|
|
479
|
+
|
|
480
|
+
if not is_atmos_multilayered:
|
|
481
|
+
return np.concatenate((sol_pos, sol_neg), axis=0)
|
|
497
482
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
483
|
+
# ---------------- other-layer contributions ----------------
|
|
484
|
+
any_pos = (l.min() < NLayers - 1) # exists tau with layers below
|
|
485
|
+
any_neg = (l.max() > 0) # exists tau with layers above
|
|
501
486
|
|
|
487
|
+
# Layer boundaries/thickness in scaled-tau coordinates
|
|
488
|
+
exp_scaled_tau_arr_with_0 = prepend(np.exp(-scaled_tau_arr_with_0[1:] / mu0), NLayers, 1)
|
|
502
489
|
|
|
503
|
-
|
|
504
|
-
|
|
490
|
+
# decay[n,r] = exp(-scaled_thickness_arr[r]/mu[n])
|
|
491
|
+
delta = scaled_thickness_arr[None, :] * M_inv[:, None]
|
|
492
|
+
decay = np.exp(-delta) # (N, NLayers)
|
|
505
493
|
|
|
506
|
-
|
|
507
|
-
|
|
494
|
+
# tau_front = scaled_tau_arr_with_0[:-1]
|
|
495
|
+
exp_taufront_mu0_inv = exp_scaled_tau_arr_with_0[:-1] # (NLayers,)
|
|
496
|
+
# tau_back = scaled_tau_arr_with_0[1:]
|
|
497
|
+
exp_tauback_mu0_inv = exp_scaled_tau_arr_with_0[1:] # (NLayers,)
|
|
508
498
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
499
|
+
if is_antiderivative_wrt_tau:
|
|
500
|
+
# (N, NLayers): multiply by this instead of dividing by (scale_tau/mu)
|
|
501
|
+
mu_div_scale_layers = mu_arr_pos[:, None] / scale_tau[None, :]
|
|
502
|
+
|
|
503
|
+
# ===== POS: sum over r > l =====
|
|
504
|
+
if any_pos:
|
|
505
|
+
l_min = int(l.min()) # only Acc_pos at layers >= l_min is ever indexed
|
|
506
|
+
|
|
507
|
+
# fac_pos = 1 - exp(-scaled_thickness_arr*(1/mu + 1/mu0)) = -expm1(-scaled_thickness_arr*(...))
|
|
508
|
+
delta_pos = delta + (scaled_thickness_arr / mu0)[None, :] # (N, NLayers) >= 0
|
|
509
|
+
fac_pos = -np.expm1(-delta_pos) # (N, NLayers) in [0,1)
|
|
510
|
+
|
|
511
|
+
# NOTE: antiderivative target-layer factor `mu/scale_tau[l]` is applied
|
|
512
|
+
# OUTSIDE the accumulator at line 544, not per-source-layer inside it.
|
|
513
|
+
|
|
514
|
+
# coef_pos[n,r] multiplies exp((tau - tau_front[r])/mu - tau_front[r]/mu0) after re-referencing
|
|
515
|
+
# includes exp(-tau_front[r]/mu0)
|
|
516
|
+
coef_pos = fac_pos * exp_taufront_mu0_inv[None, :] # (N, NLayers)
|
|
517
|
+
|
|
518
|
+
# Reverse scan to build Acc_l (referenced at tau_back[l]) INCLUDING Bpos:
|
|
519
|
+
# Acc_{r-1} = coef_pos[:,r]*Bpos[:,r,:] + decay[:,r]*Acc_r
|
|
520
|
+
# Stored array covers layers [l_min..NLayers-1]; layer r maps to index r-l_min.
|
|
521
|
+
Npos = NLayers - l_min
|
|
522
|
+
|
|
523
|
+
if autograd_compatible:
|
|
524
|
+
acc0 = np.zeros((N, Nphi))
|
|
525
|
+
acc = acc0
|
|
526
|
+
cols_rev = [acc0] # Acc_{NLayers-1} = 0
|
|
527
|
+
for r in range(NLayers - 1, l_min, -1):
|
|
528
|
+
term = coef_pos[:, r][:, None] * Bpos[:, r, :] # (N, Nphi)
|
|
529
|
+
acc = term + decay[:, r][:, None] * acc
|
|
530
|
+
cols_rev.append(acc)
|
|
531
|
+
Acc_pos = np.stack(cols_rev[::-1], axis=1) # (N, Npos, Nphi)
|
|
532
|
+
else:
|
|
533
|
+
Acc_pos = np.empty((N, Npos, Nphi))
|
|
534
|
+
Acc_pos[:, Npos - 1, :] = 0.0 # Acc_{NLayers-1}
|
|
535
|
+
for r in range(NLayers - 1, l_min, -1):
|
|
536
|
+
i_dest = r - 1 - l_min
|
|
537
|
+
i_source = r - l_min
|
|
538
|
+
np.multiply(coef_pos[:, r:r+1], Bpos[:, r, :],
|
|
539
|
+
out=Acc_pos[:, i_dest, :])
|
|
540
|
+
Acc_pos[:, i_dest, :] += decay[:, r:r+1] * Acc_pos[:, i_source, :]
|
|
541
|
+
|
|
542
|
+
# Apply to tau: multiply by exp((scaled_tau - tau_back[l]) / mu) (argument <= 0 in exact arithmetic)
|
|
543
|
+
expfac_pos = np.exp(M_inv[:, None] * (scaled_tau - scaled_tau_arr_with_0[1:][l])[None, :]) # (N, Ntau)
|
|
544
|
+
contrib_pos = Acc_pos[:, l - l_min, :] * expfac_pos[:, :, None] # (N, Ntau, Nphi)
|
|
512
545
|
|
|
513
546
|
if is_antiderivative_wrt_tau:
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
547
|
+
contrib_pos = contrib_pos * mu_div_scale_layers[:, l, None]
|
|
548
|
+
|
|
549
|
+
sol_pos = sol_pos + contrib_pos
|
|
550
|
+
|
|
551
|
+
# ===== NEG: sum over r < l =====
|
|
552
|
+
if any_neg:
|
|
553
|
+
l_max = int(l.max()) # only Acc_neg at layers <= l_max is ever indexed
|
|
554
|
+
|
|
555
|
+
# We need term_base[n,r] = exp(-tau_back[r]/mu0) - exp(-scaled_thickness_arr[r]/mu)*exp(-tau_front[r]/mu0)
|
|
556
|
+
# computed stably via expm1 using only non-positive inputs.
|
|
557
|
+
delta_neg = delta - (scaled_thickness_arr / mu0)[None, :] # (N, NLayers), can be +/-.
|
|
558
|
+
em1 = np.expm1(-np.abs(delta_neg)) # (N, NLayers) in [-1,0], argument <= 0
|
|
559
|
+
|
|
560
|
+
exp_x1 = exp_tauback_mu0_inv[None, :] # (1, NLayers)
|
|
561
|
+
exp_x0 = decay * exp_taufront_mu0_inv[None, :] # (N, NLayers)
|
|
562
|
+
|
|
563
|
+
# if delta>=0: term = exp_x1*(1-exp(-delta)) = exp_x1*(-expm1(-delta)) = exp_x1*(-em1)
|
|
564
|
+
# if delta<0 : term = exp_x0*(exp(delta)-1) = exp_x0*expm1(delta) = exp_x0*( em1) (since delta<0 -> em1=expm1(delta))
|
|
565
|
+
term_base = np.where(delta_neg >= 0.0, -em1 * exp_x1, em1 * exp_x0) # (N, NLayers)
|
|
566
|
+
|
|
567
|
+
coef_neg = term_base # (N, NLayers)
|
|
568
|
+
# NOTE: antiderivative target-layer factor `-mu/scale_tau[l]` is applied
|
|
569
|
+
# OUTSIDE the accumulator at line 600, not per-source-layer inside it.
|
|
570
|
+
|
|
571
|
+
# Forward scan to build Acc_l (referenced at tau_front[l]) INCLUDING Bneg:
|
|
572
|
+
# Acc_{r+1} = coef_neg[:,r]*Bneg[:,r,:] + decay[:,r]*Acc_r
|
|
573
|
+
# Stored array covers layers [0..l_max]; layer index unchanged.
|
|
574
|
+
Nneg = l_max + 1
|
|
575
|
+
|
|
576
|
+
if autograd_compatible:
|
|
577
|
+
acc0 = np.zeros((N, Nphi))
|
|
578
|
+
acc = acc0
|
|
579
|
+
cols = [acc0] # Acc_0 = 0
|
|
580
|
+
for r in range(0, l_max):
|
|
581
|
+
term = coef_neg[:, r][:, None] * Bneg[:, r, :] # (N, Nphi)
|
|
582
|
+
acc = term + decay[:, r][:, None] * acc
|
|
583
|
+
cols.append(acc)
|
|
584
|
+
Acc_neg = np.stack(cols, axis=1) # (N, Nneg, Nphi)
|
|
585
|
+
else:
|
|
586
|
+
Acc_neg = np.empty((N, Nneg, Nphi))
|
|
587
|
+
Acc_neg[:, 0, :] = 0.0 # Acc_0
|
|
588
|
+
for r in range(0, l_max):
|
|
589
|
+
np.multiply(coef_neg[:, r:r+1], Bneg[:, r, :],
|
|
590
|
+
out=Acc_neg[:, r + 1, :])
|
|
591
|
+
Acc_neg[:, r + 1, :] += decay[:, r:r+1] * Acc_neg[:, r, :]
|
|
592
|
+
|
|
593
|
+
# Apply to tau: multiply by exp((tau_front[l] - scaled_tau) / mu) (argument <= 0 in exact arithmetic)
|
|
594
|
+
expfac_neg = np.exp(M_inv[:, None] * (scaled_tau_arr_with_0[l] - scaled_tau)[None, :]) # (N, Ntau)
|
|
595
|
+
contrib_neg = Acc_neg[:, l, :] * expfac_neg[:, :, None] # (N, Ntau, Nphi)
|
|
560
596
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
# thickness_neg>=0: exp_x1 * (1 - exp(-a)) = exp_x1 * (-expm1(-a)) = exp_x1 * (-em1_neg)
|
|
565
|
-
# thickness_neg<0 : exp_x0 * (exp(-a) - 1) = exp_x0 * expm1(-a) = exp_x0 * ( em1_neg)
|
|
566
|
-
layer_term_negthick = em1_neg * exp_x0 # (N, NLayers)
|
|
567
|
-
layer_term_posthick = -em1_neg * exp_x1 # (N, NLayers)
|
|
568
|
-
layer_term_neg = layer_term_posthick * mask + layer_term_negthick * (~mask) # (N, NLayers)
|
|
569
|
-
|
|
570
|
-
if is_antiderivative_wrt_tau:
|
|
571
|
-
layer_term_neg = -integration_factor * layer_term_neg
|
|
597
|
+
if is_antiderivative_wrt_tau:
|
|
598
|
+
contrib_neg = -contrib_neg * mu_div_scale_layers[:, l, None]
|
|
572
599
|
|
|
573
|
-
|
|
574
|
-
# Rneg[l] = decay_prod[l] * sum_{r<l} layer_term_neg[r] / decay_prod[r+1]
|
|
575
|
-
weighted_layer_term_neg = layer_term_neg / decay_prod[:, 1:] # (N, NLayers)
|
|
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)
|
|
600
|
+
sol_neg = sol_neg + contrib_neg
|
|
580
601
|
|
|
581
|
-
|
|
582
|
-
expfac_neg = np.exp(M_inv[:, None] * (scaled_tauwith0_front[l] - scaled_tau)[None, :]) # (N, Ntau)
|
|
583
|
-
tmp = np.take(Rneg, l, axis=1) # (N, Ntau)
|
|
584
|
-
tmp = (tmp * expfac_neg)[:, :, None] * Bneg[:, l_inv, :]
|
|
602
|
+
return np.concatenate((sol_pos, sol_neg), axis=0)
|
|
585
603
|
|
|
586
|
-
if autograd_compatible:
|
|
587
|
-
solution = np.concatenate((solution[:N, :, :], solution[N:, :, :] + tmp), axis=0)
|
|
588
|
-
else:
|
|
589
|
-
solution[N:, :, :] += tmp
|
|
590
|
-
|
|
591
|
-
return solution
|
|
592
|
-
|
|
593
604
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
594
|
-
|
|
595
|
-
else:
|
|
596
|
-
return solution
|
|
597
605
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
598
606
|
|
|
599
607
|
# IMS correction for the intensity (see section 3.7.2)
|
|
@@ -628,7 +636,8 @@ def pydisort(
|
|
|
628
636
|
) / (mu_arr_pos * scaled_mu0 * x)[:, None]
|
|
629
637
|
|
|
630
638
|
return (
|
|
631
|
-
|
|
639
|
+
I0
|
|
640
|
+
/ (4 * pi)
|
|
632
641
|
* (omega_avg * f_avg) ** 2
|
|
633
642
|
/ (1 - omega_avg * f_avg)
|
|
634
643
|
* Legendre(
|
|
@@ -640,59 +649,25 @@ def pydisort(
|
|
|
640
649
|
|
|
641
650
|
# The corrected intensity
|
|
642
651
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
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
|
-
):
|
|
652
|
+
def u_corrected(tau, phi, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False):
|
|
650
653
|
tau = np.atleast_1d(tau)
|
|
651
654
|
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
|
-
|
|
655
|
+
NT_corrections = TMS_correction(tau, phi, is_antiderivative_wrt_tau)
|
|
656
|
+
|
|
677
657
|
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,
|
|
658
|
+
NT_corrections = NT_corrections + np.concatenate(
|
|
659
|
+
[np.zeros((N, len(tau), len(phi))), IMS_correction(tau, phi, is_antiderivative_wrt_tau)], axis=0
|
|
685
660
|
)
|
|
686
661
|
else:
|
|
687
662
|
NT_corrections[N:, :, :] += IMS_correction(tau, phi, is_antiderivative_wrt_tau)
|
|
688
|
-
|
|
663
|
+
|
|
689
664
|
if return_Fourier_error or return_tau_arr:
|
|
665
|
+
u_star_outputs = u_star(tau, phi, is_antiderivative_wrt_tau, return_Fourier_error, return_tau_arr)
|
|
690
666
|
return (
|
|
691
667
|
u_star_outputs[0] + rescale_factor * np.squeeze(NT_corrections),
|
|
692
668
|
) + u_star_outputs[1:]
|
|
693
669
|
else:
|
|
694
|
-
return
|
|
695
|
-
|
|
670
|
+
return u_star(tau, phi, is_antiderivative_wrt_tau) + rescale_factor * np.squeeze(NT_corrections)
|
|
696
671
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
697
672
|
|
|
698
673
|
return mu_arr, flux_up, flux_down, u0, u_corrected
|
|
@@ -641,60 +641,76 @@ def interpolate(u):
|
|
|
641
641
|
u_pos_interpol = sc.interpolate.BarycentricInterpolator(mu_arr_pos)
|
|
642
642
|
u_neg_interpol = sc.interpolate.BarycentricInterpolator(-mu_arr_pos)
|
|
643
643
|
|
|
644
|
+
# Value-keyed cache for the most recent (tau, phi, is_antideriv) -> u_cache.
|
|
645
|
+
# Hits when the user loops over `mu` with the same (tau, phi); otherwise re-evaluates `u`.
|
|
646
|
+
# Cache stores copies of `tau`/`phi` so in-place mutation of the user's arrays
|
|
647
|
+
# cannot return stale values (compared via `np.array_equal`).
|
|
648
|
+
# Only used on the simple-return path (skipped when Fourier_error / tau_arr are requested).
|
|
649
|
+
_cache = [None, None, None, None] # [tau_copy, phi_copy, is_antideriv, u_cache]
|
|
650
|
+
|
|
644
651
|
argcount = u.__code__.co_argcount
|
|
645
652
|
if argcount == 5: # Function is `u` instead of `u0`
|
|
646
653
|
def u_interpol(mu, tau, phi, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False):
|
|
647
654
|
if not np.all(np.abs(mu) <= 1):
|
|
648
655
|
raise ValueError("mu values must be between -1 and 1.")
|
|
649
|
-
|
|
656
|
+
|
|
650
657
|
mu = np.atleast_1d(mu)
|
|
651
658
|
|
|
652
659
|
if return_Fourier_error or return_tau_arr:
|
|
653
660
|
u_outputs = u(tau, phi, is_antiderivative_wrt_tau, return_Fourier_error, return_tau_arr)
|
|
654
661
|
u_cache = u_outputs[0]
|
|
662
|
+
elif (_cache[3] is not None and is_antiderivative_wrt_tau == _cache[2]
|
|
663
|
+
and np.array_equal(tau, _cache[0]) and np.array_equal(phi, _cache[1])):
|
|
664
|
+
u_cache = _cache[3]
|
|
655
665
|
else:
|
|
656
666
|
u_cache = u(tau, phi, is_antiderivative_wrt_tau)
|
|
657
|
-
|
|
667
|
+
_cache[:] = [np.array(tau, copy=True), np.array(phi, copy=True),
|
|
668
|
+
is_antiderivative_wrt_tau, u_cache]
|
|
669
|
+
|
|
658
670
|
results = np.empty((len(mu),) + np.shape(u_cache)[1:])
|
|
659
671
|
mask_pos = mu > 0
|
|
660
672
|
mask_else = ~mask_pos
|
|
661
|
-
|
|
673
|
+
|
|
662
674
|
if np.any(mask_pos):
|
|
663
675
|
u_pos_interpol.set_yi(u_cache[:N])
|
|
664
676
|
results[mask_pos] = u_pos_interpol(mu[mask_pos])
|
|
665
677
|
if np.any(mask_else):
|
|
666
678
|
u_neg_interpol.set_yi(u_cache[N:])
|
|
667
679
|
results[mask_else] = u_neg_interpol(mu[mask_else])
|
|
668
|
-
|
|
680
|
+
|
|
669
681
|
if return_Fourier_error or return_tau_arr:
|
|
670
682
|
return (np.squeeze(results)[()],) + u_outputs[1:]
|
|
671
683
|
else:
|
|
672
684
|
return np.squeeze(results)[()]
|
|
673
|
-
|
|
685
|
+
|
|
674
686
|
elif argcount == 4: # Function is u0 instead of u
|
|
675
687
|
def u_interpol(mu, tau, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False):
|
|
676
688
|
if not np.all(np.abs(mu) <= 1):
|
|
677
689
|
raise ValueError("mu values must be between -1 and 1.")
|
|
678
|
-
|
|
690
|
+
|
|
679
691
|
mu = np.atleast_1d(mu)
|
|
680
692
|
|
|
681
693
|
if return_Fourier_error or return_tau_arr:
|
|
682
694
|
u_outputs = u(tau, is_antiderivative_wrt_tau, return_Fourier_error, return_tau_arr)
|
|
683
695
|
u_cache = u_outputs[0]
|
|
696
|
+
elif (_cache[3] is not None and is_antiderivative_wrt_tau == _cache[2]
|
|
697
|
+
and np.array_equal(tau, _cache[0])):
|
|
698
|
+
u_cache = _cache[3]
|
|
684
699
|
else:
|
|
685
700
|
u_cache = u(tau, is_antiderivative_wrt_tau)
|
|
686
|
-
|
|
701
|
+
_cache[:] = [np.array(tau, copy=True), None, is_antiderivative_wrt_tau, u_cache]
|
|
702
|
+
|
|
687
703
|
results = np.empty((len(mu),) + np.shape(u_cache)[1:])
|
|
688
704
|
mask_pos = mu > 0
|
|
689
705
|
mask_else = ~mask_pos
|
|
690
|
-
|
|
706
|
+
|
|
691
707
|
if np.any(mask_pos):
|
|
692
708
|
u_pos_interpol.set_yi(u_cache[:N])
|
|
693
709
|
results[mask_pos] = u_pos_interpol(mu[mask_pos])
|
|
694
710
|
if np.any(mask_else):
|
|
695
711
|
u_neg_interpol.set_yi(u_cache[N:])
|
|
696
712
|
results[mask_else] = u_neg_interpol(mu[mask_else])
|
|
697
|
-
|
|
713
|
+
|
|
698
714
|
if return_Fourier_error or return_tau_arr:
|
|
699
715
|
return (np.squeeze(results)[()],) + u_outputs[1:]
|
|
700
716
|
else:
|
|
@@ -764,7 +780,7 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
764
780
|
| Variable | Shape |
|
|
765
781
|
| ----------------------------- | ----------------------------------------- |
|
|
766
782
|
| ``tau`` | ``Ntau`` |
|
|
767
|
-
| ``scale_tau`` |
|
|
783
|
+
| ``scale_tau`` | ``NL`` |
|
|
768
784
|
| ``l`` | ``Ntau`` |
|
|
769
785
|
| ``Nscoeffs`` | scalar |
|
|
770
786
|
| ``s_poly_coeffs`` | ``NL x Nscoeffs`` |
|
|
@@ -790,11 +806,10 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
790
806
|
d0 = np.einsum("lij, lj -> il", G, c0w, optimize=True)
|
|
791
807
|
|
|
792
808
|
if is_antiderivative_wrt_tau:
|
|
793
|
-
p0 = tau
|
|
809
|
+
p0 = tau
|
|
810
|
+
return (d0 / scale_tau[None, :])[:, l] * p0[None, :]
|
|
794
811
|
else:
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
return d0[:, l] * p0[None, :]
|
|
812
|
+
return d0[:, l]
|
|
798
813
|
|
|
799
814
|
# --------------------- Nscoeffs == 2 ---------------------
|
|
800
815
|
elif Nscoeffs == 2:
|
|
@@ -812,11 +827,11 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
812
827
|
if is_antiderivative_wrt_tau:
|
|
813
828
|
p0 = tau * tau / 2
|
|
814
829
|
p1 = tau
|
|
815
|
-
return (d0[:, l] * p0[None, :] + d1[:, l] * p1[None, :]
|
|
830
|
+
return (d0 / scale_tau[None, :])[:, l] * p0[None, :] + (d1 / scale_tau[None, :])[:, l] * p1[None, :]
|
|
816
831
|
else:
|
|
817
832
|
p0 = tau
|
|
818
|
-
p1 = np.ones_like(tau)
|
|
819
|
-
return d0[:, l] * p0[None, :] + d1[:, l]
|
|
833
|
+
#p1 = np.ones_like(tau)
|
|
834
|
+
return d0[:, l] * p0[None, :] + d1[:, l]
|
|
820
835
|
|
|
821
836
|
# --------------------- Nscoeffs > 2 ----------------------
|
|
822
837
|
else:
|
|
@@ -855,11 +870,11 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
855
870
|
powers = np.arange(Nscoeffs - 1, -1, -1)[None, :]
|
|
856
871
|
if is_antiderivative_wrt_tau:
|
|
857
872
|
p = powers + 1
|
|
858
|
-
tau_poly = (tau[:, None] ** p) /
|
|
873
|
+
tau_poly = (tau[:, None] ** p) / p
|
|
874
|
+
return np.einsum("tqp, tp -> qt", (mathscr_b / scale_tau[:, None, None])[l, :, :], tau_poly, optimize=True)
|
|
859
875
|
else:
|
|
860
876
|
tau_poly = tau[:, None] ** powers
|
|
861
|
-
|
|
862
|
-
return np.einsum("tqp, tp -> qt", mathscr_b[l, :, :], tau_poly, optimize=True)
|
|
877
|
+
return np.einsum("tqp, tp -> qt", mathscr_b[l, :, :], tau_poly, optimize=True)
|
|
863
878
|
|
|
864
879
|
|
|
865
880
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PythonicDISORT
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7
|
|
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
|
|
@@ -41,7 +41,7 @@ You may contact me, Dion, through dh3065@columbia.edu.
|
|
|
41
41
|
|
|
42
42
|
The **GitHub repository** is https://github.com/LDEO-CREW/Pythonic-DISORT.
|
|
43
43
|
|
|
44
|
-
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**.
|
|
45
45
|
|
|
46
46
|
# Documentation
|
|
47
47
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
@@ -76,7 +76,7 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
76
76
|
# Installation
|
|
77
77
|
|
|
78
78
|
* From PyPI: `pip install PythonicDISORT`
|
|
79
|
-
* From Conda-
|
|
79
|
+
* From Conda-Forge: `conda install -c conda-forge pythonicdisort` or `mamba install -c conda-forge pythonicdisort`
|
|
80
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*)
|
|
81
81
|
|
|
82
82
|
## Requirements to run PythonicDISORT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|