PythonicDISORT 0.6.0__tar.gz → 0.6.2__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-0.6.0/src/PythonicDISORT.egg-info → PythonicDISORT-0.6.2}/PKG-INFO +10 -10
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/README.md +10 -10
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/pyproject.toml +1 -1
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT/_assemble_solution_functions.py +6 -6
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT/_diagonalize.py +19 -19
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT/_solve_for_coefs.py +23 -23
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT/pydisort.py +109 -166
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT/subroutines.py +17 -17
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2/src/PythonicDISORT.egg-info}/PKG-INFO +10 -10
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/LICENSE.md +0 -0
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/setup.cfg +0 -0
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT/__init__.py +0 -0
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT.egg-info/SOURCES.txt +0 -0
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT.egg-info/dependency_links.txt +0 -0
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT.egg-info/requires.txt +0 -0
- {PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/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: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
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
|
|
@@ -32,25 +32,25 @@ In addition, upon the request of a user, we added a subroutine to calculate acti
|
|
|
32
32
|
|
|
33
33
|
You may contact me, Dion, through dh3065@columbia.edu.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Our **GitHub repository:** https://github.com/LDEO-CREW/Pythonic-DISORT also includes our F2PY-wrapped Stamnes DISORT (version 4.0.99) in the `disort4.0.99_f2py` directory.
|
|
36
36
|
The original was downloaded from http://www.rtatmocn.com/disort/. The wrapper is inspired by https://github.com/kconnour/pyRT_DISORT.
|
|
37
37
|
|
|
38
38
|
# Documentation
|
|
39
39
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
40
40
|
|
|
41
|
-
Also see the accompanying Jupyter Notebook
|
|
42
|
-
of our GitHub repository
|
|
41
|
+
Also see the accompanying Jupyter Notebook `Pythonic-DISORT.ipynb` in the `docs` directory
|
|
42
|
+
of our GitHub repository.
|
|
43
43
|
The Jupyter Notebook provides comprehensive documentation, suggested inputs, explanations,
|
|
44
44
|
mathematical derivations and verification tests.
|
|
45
45
|
We highly recommend reading the non-optional parts of sections 1 and 2 before use.
|
|
46
46
|
|
|
47
47
|
## PyTest and examples of how to use PythonicDISORT
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
Not only do we have verification tests in the Jupyter Notebook, we also used PyTest to recreate most of the test problems in Stamnes' `disotest.f90`;
|
|
50
|
+
`disotest.f90` is included in the `disort4.0.99_f2py` directory of our GitHub repository. In these tests, the solutions from PythonicDISORT are compared
|
|
51
51
|
against solutions from our F2PY-wrapped Stamnes' DISORT (version 4.0.99). With PyTest installed, execute the console command `pytest`
|
|
52
52
|
in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks, one for each test,
|
|
53
|
-
to show how the
|
|
53
|
+
to show how the tests were implemented. These notebooks double up as examples of how to use PythonicDISORT.
|
|
54
54
|
|
|
55
55
|
# Installation
|
|
56
56
|
|
|
@@ -74,16 +74,16 @@ must be set up to run the last section (section 6).
|
|
|
74
74
|
|
|
75
75
|
## Compatibility
|
|
76
76
|
|
|
77
|
-
The PythonicDISORT package should be system agnostic given its minimal dependencies and pure Python code.
|
|
77
|
+
The PythonicDISORT package *should* be system agnostic given its minimal dependencies and pure Python code.
|
|
78
78
|
We do not guarantee that our Jupyter Notebook and F2PY-wrapped Stamnes' DISORT will work on other systems though.
|
|
79
79
|
The latter will almost certainly need user edits to work and in any case it requires FORTRAN compilers which
|
|
80
|
-
are not included in
|
|
80
|
+
are not included in our GitHub repository. Everything in the repository was built and tested on Windows 11 but
|
|
81
81
|
not yet tested on other systems.
|
|
82
82
|
|
|
83
83
|
# Acknowledgements
|
|
84
84
|
|
|
85
85
|
I acknowledge funding from NSF through the Learning the Earth with Artificial intelligence and Physics (LEAP)
|
|
86
|
-
Science and Technology Center (STC) (Award #2019625).
|
|
86
|
+
Science and Technology Center (STC) (Award #2019625) under which this package was initially created.
|
|
87
87
|
|
|
88
88
|
# References
|
|
89
89
|
1) S. Chandrasekhar. 1960. *Radiative Transfer.*
|
|
@@ -11,25 +11,25 @@ In addition, upon the request of a user, we added a subroutine to calculate acti
|
|
|
11
11
|
|
|
12
12
|
You may contact me, Dion, through dh3065@columbia.edu.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Our **GitHub repository:** https://github.com/LDEO-CREW/Pythonic-DISORT also includes our F2PY-wrapped Stamnes DISORT (version 4.0.99) in the `disort4.0.99_f2py` directory.
|
|
15
15
|
The original was downloaded from http://www.rtatmocn.com/disort/. The wrapper is inspired by https://github.com/kconnour/pyRT_DISORT.
|
|
16
16
|
|
|
17
17
|
# Documentation
|
|
18
18
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
19
19
|
|
|
20
|
-
Also see the accompanying Jupyter Notebook
|
|
21
|
-
of our GitHub repository
|
|
20
|
+
Also see the accompanying Jupyter Notebook `Pythonic-DISORT.ipynb` in the `docs` directory
|
|
21
|
+
of our GitHub repository.
|
|
22
22
|
The Jupyter Notebook provides comprehensive documentation, suggested inputs, explanations,
|
|
23
23
|
mathematical derivations and verification tests.
|
|
24
24
|
We highly recommend reading the non-optional parts of sections 1 and 2 before use.
|
|
25
25
|
|
|
26
26
|
## PyTest and examples of how to use PythonicDISORT
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
Not only do we have verification tests in the Jupyter Notebook, we also used PyTest to recreate most of the test problems in Stamnes' `disotest.f90`;
|
|
29
|
+
`disotest.f90` is included in the `disort4.0.99_f2py` directory of our GitHub repository. In these tests, the solutions from PythonicDISORT are compared
|
|
30
30
|
against solutions from our F2PY-wrapped Stamnes' DISORT (version 4.0.99). With PyTest installed, execute the console command `pytest`
|
|
31
31
|
in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks, one for each test,
|
|
32
|
-
to show how the
|
|
32
|
+
to show how the tests were implemented. These notebooks double up as examples of how to use PythonicDISORT.
|
|
33
33
|
|
|
34
34
|
# Installation
|
|
35
35
|
|
|
@@ -53,16 +53,16 @@ must be set up to run the last section (section 6).
|
|
|
53
53
|
|
|
54
54
|
## Compatibility
|
|
55
55
|
|
|
56
|
-
The PythonicDISORT package should be system agnostic given its minimal dependencies and pure Python code.
|
|
56
|
+
The PythonicDISORT package *should* be system agnostic given its minimal dependencies and pure Python code.
|
|
57
57
|
We do not guarantee that our Jupyter Notebook and F2PY-wrapped Stamnes' DISORT will work on other systems though.
|
|
58
58
|
The latter will almost certainly need user edits to work and in any case it requires FORTRAN compilers which
|
|
59
|
-
are not included in
|
|
59
|
+
are not included in our GitHub repository. Everything in the repository was built and tested on Windows 11 but
|
|
60
60
|
not yet tested on other systems.
|
|
61
61
|
|
|
62
62
|
# Acknowledgements
|
|
63
63
|
|
|
64
64
|
I acknowledge funding from NSF through the Learning the Earth with Artificial intelligence and Physics (LEAP)
|
|
65
|
-
Science and Technology Center (STC) (Award #2019625).
|
|
65
|
+
Science and Technology Center (STC) (Award #2019625) under which this package was initially created.
|
|
66
66
|
|
|
67
67
|
# References
|
|
68
68
|
1) S. Chandrasekhar. 1960. *Radiative Transfer.*
|
|
@@ -87,4 +87,4 @@ Science and Technology Center (STC) (Award #2019625).
|
|
|
87
87
|
|
|
88
88
|
11) T. Nakajima and M. Tanaka. 1988. *Algorithms for radiative intensity calculations in moderately thick atmospheres using a truncation approximation.* https://www.sciencedirect.com/science/article/pii/0022407388900313.
|
|
89
89
|
|
|
90
|
-
12) K. Connour and A. Stcherbinine. 2022. *pyRT_DISORT.* https://github.com/kconnour/pyRT_DISORT.
|
|
90
|
+
12) K. Connour and A. Stcherbinine. 2022. *pyRT_DISORT.* https://github.com/kconnour/pyRT_DISORT.
|
{PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT/_assemble_solution_functions.py
RENAMED
|
@@ -11,7 +11,7 @@ def _assemble_solution_functions(
|
|
|
11
11
|
scaled_tau_arr_with_0,
|
|
12
12
|
mu_arr_pos, mu_arr,
|
|
13
13
|
M_inv, W,
|
|
14
|
-
N, NQuad, NLeg,
|
|
14
|
+
N, NQuad, NLeg, NFourier,
|
|
15
15
|
NLayers, NBDRF,
|
|
16
16
|
atmos_is_multilayered,
|
|
17
17
|
weighted_scaled_Leg_coeffs,
|
|
@@ -46,7 +46,7 @@ def _assemble_solution_functions(
|
|
|
46
46
|
# Compute all the necessary quantities
|
|
47
47
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
48
48
|
outputs = _diagonalize(
|
|
49
|
-
|
|
49
|
+
NFourier,
|
|
50
50
|
scaled_omega_arr,
|
|
51
51
|
mu_arr_pos, mu_arr,
|
|
52
52
|
M_inv, W,
|
|
@@ -74,7 +74,7 @@ def _assemble_solution_functions(
|
|
|
74
74
|
B_collect = None
|
|
75
75
|
|
|
76
76
|
GC_collect = _solve_for_coefs(
|
|
77
|
-
|
|
77
|
+
NFourier,
|
|
78
78
|
G_collect,
|
|
79
79
|
K_collect,
|
|
80
80
|
B_collect,
|
|
@@ -154,14 +154,14 @@ def _assemble_solution_functions(
|
|
|
154
154
|
)
|
|
155
155
|
|
|
156
156
|
if _is_compatible_with_autograd:
|
|
157
|
-
um = um + np.concatenate((_mathscr_v_contribution.T, np.zeros((
|
|
157
|
+
um = um + np.concatenate((_mathscr_v_contribution.T, np.zeros((NFourier - 1, len(tau), NQuad))))
|
|
158
158
|
else:
|
|
159
159
|
um[0, :, :] += _mathscr_v_contribution.T
|
|
160
160
|
|
|
161
161
|
intensities = np.einsum(
|
|
162
162
|
"mti, mp -> itp",
|
|
163
163
|
um,
|
|
164
|
-
np.cos(np.arange(
|
|
164
|
+
np.cos(np.arange(NFourier)[:, None] * (phi0 - phi)[None, :]),
|
|
165
165
|
optimize=True,
|
|
166
166
|
)
|
|
167
167
|
|
|
@@ -183,7 +183,7 @@ def _assemble_solution_functions(
|
|
|
183
183
|
)
|
|
184
184
|
intensities_abs = np.abs(intensities)
|
|
185
185
|
Fourier_error = np.max(
|
|
186
|
-
np.abs(ulast[:, :, None] * np.cos((
|
|
186
|
+
np.abs(ulast[:, :, None] * np.cos((NFourier - 1) * (phi0 - phi))[None, None, :]),
|
|
187
187
|
intensities_abs,
|
|
188
188
|
out=np.zeros_like(intensities_abs),
|
|
189
189
|
where=intensities_abs > 1e-8,
|
|
@@ -4,7 +4,7 @@ from math import pi
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def _diagonalize(
|
|
7
|
-
|
|
7
|
+
NFourier,
|
|
8
8
|
scaled_omega_arr,
|
|
9
9
|
mu_arr_pos, mu_arr,
|
|
10
10
|
M_inv, W,
|
|
@@ -29,24 +29,24 @@ def _diagonalize(
|
|
|
29
29
|
ells_all = np.arange(NLeg)
|
|
30
30
|
ind = 0
|
|
31
31
|
|
|
32
|
-
G_collect = np.empty((
|
|
33
|
-
K_collect = np.empty((
|
|
34
|
-
alpha_arr = np.empty((
|
|
35
|
-
beta_arr = np.empty((
|
|
36
|
-
X_tilde_arr = np.empty((
|
|
32
|
+
G_collect = np.empty((NFourier * NLayers, NQuad, NQuad))
|
|
33
|
+
K_collect = np.empty((NFourier * NLayers, NQuad))
|
|
34
|
+
alpha_arr = np.empty((NFourier * NLayers, N, N))
|
|
35
|
+
beta_arr = np.empty((NFourier * NLayers, N, N))
|
|
36
|
+
X_tilde_arr = np.empty((NFourier * NLayers, NQuad))
|
|
37
37
|
no_shortcut_indices = []
|
|
38
38
|
no_shortcut_indices_0 = []
|
|
39
39
|
|
|
40
40
|
if there_is_beam_source:
|
|
41
|
-
B_collect = np.zeros((
|
|
41
|
+
B_collect = np.zeros((NFourier * NLayers, NQuad))
|
|
42
42
|
if there_is_iso_source:
|
|
43
43
|
G_inv_collect_0 = np.empty((NLayers, NQuad, NQuad))
|
|
44
44
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
45
45
|
|
|
46
|
-
# Loop over
|
|
46
|
+
# Loop over NFourier Fourier modes
|
|
47
47
|
# These can easily be parallelized, but the speed-up is unlikely to be worth the overhead
|
|
48
48
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
49
|
-
for m in range(
|
|
49
|
+
for m in range(NFourier):
|
|
50
50
|
# Setup
|
|
51
51
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
52
52
|
m_equals_0 = (m == 0)
|
|
@@ -169,26 +169,26 @@ def _diagonalize(
|
|
|
169
169
|
if there_is_beam_source:
|
|
170
170
|
if there_is_iso_source:
|
|
171
171
|
return (
|
|
172
|
-
G_collect.reshape((
|
|
173
|
-
K_collect.reshape((
|
|
174
|
-
B_collect.reshape((
|
|
172
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
173
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
174
|
+
B_collect.reshape((NFourier, NLayers, NQuad)),
|
|
175
175
|
G_inv_collect_0,
|
|
176
176
|
)
|
|
177
177
|
else:
|
|
178
178
|
return (
|
|
179
|
-
G_collect.reshape((
|
|
180
|
-
K_collect.reshape((
|
|
181
|
-
B_collect.reshape((
|
|
179
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
180
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
181
|
+
B_collect.reshape((NFourier, NLayers, NQuad)),
|
|
182
182
|
)
|
|
183
183
|
else:
|
|
184
184
|
if there_is_iso_source:
|
|
185
185
|
return (
|
|
186
|
-
G_collect.reshape((
|
|
187
|
-
K_collect.reshape((
|
|
186
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
187
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
188
188
|
G_inv_collect_0,
|
|
189
189
|
)
|
|
190
190
|
else:
|
|
191
191
|
return (
|
|
192
|
-
G_collect.reshape((
|
|
193
|
-
K_collect.reshape((
|
|
192
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
193
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
194
194
|
)
|
|
@@ -7,7 +7,7 @@ from math import pi
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def _solve_for_coefs(
|
|
10
|
-
|
|
10
|
+
NFourier,
|
|
11
11
|
G_collect,
|
|
12
12
|
K_collect,
|
|
13
13
|
B_collect,
|
|
@@ -36,11 +36,11 @@ def _solve_for_coefs(
|
|
|
36
36
|
"""
|
|
37
37
|
################################## Solve for coefficients of homogeneous solution ##########################################
|
|
38
38
|
|
|
39
|
-
GC_collect = np.empty((
|
|
39
|
+
GC_collect = np.empty((NFourier, NLayers, NQuad, NQuad))
|
|
40
40
|
use_sparse_framework = NLayers >= use_sparse_NLayers
|
|
41
41
|
|
|
42
42
|
# The following loops can easily be parallelized, but the speed-up is unlikely to be worth the overhead
|
|
43
|
-
for m in range(
|
|
43
|
+
for m in range(NFourier):
|
|
44
44
|
m_equals_0 = (m == 0)
|
|
45
45
|
BDRF_bool = m < NBDRF
|
|
46
46
|
|
|
@@ -89,7 +89,7 @@ def _solve_for_coefs(
|
|
|
89
89
|
K_collect_m[0:1, :],
|
|
90
90
|
G_inv_collect_0[0:1, :, :],
|
|
91
91
|
mu_arr,
|
|
92
|
-
).
|
|
92
|
+
).ravel()
|
|
93
93
|
|
|
94
94
|
_mathscr_v_contribution_middle = np.array([])
|
|
95
95
|
if atmos_is_multilayered:
|
|
@@ -115,7 +115,7 @@ def _solve_for_coefs(
|
|
|
115
115
|
G_inv_collect_0[1:, :, :],
|
|
116
116
|
mu_arr,
|
|
117
117
|
)
|
|
118
|
-
).
|
|
118
|
+
).ravel()
|
|
119
119
|
|
|
120
120
|
_mathscr_v_contribution_bottom = _mathscr_v(
|
|
121
121
|
tau_arr[-1:], np.array([0]),
|
|
@@ -124,7 +124,7 @@ def _solve_for_coefs(
|
|
|
124
124
|
G_collect_m[-1:, N:, :],
|
|
125
125
|
K_collect_m[-1:, :],
|
|
126
126
|
G_inv_collect_0[-1:, :, :],
|
|
127
|
-
mu_arr).
|
|
127
|
+
mu_arr).ravel()
|
|
128
128
|
if NBDRF > 0:
|
|
129
129
|
_mathscr_v_contribution_bottom = (
|
|
130
130
|
R + np.eye(N)
|
|
@@ -154,7 +154,7 @@ def _solve_for_coefs(
|
|
|
154
154
|
* np.exp(-mu0 * scaled_tau_arr_with_0[l + 1])
|
|
155
155
|
for l in range(NLayers - 1)
|
|
156
156
|
]
|
|
157
|
-
).
|
|
157
|
+
).ravel()
|
|
158
158
|
|
|
159
159
|
RHS = (
|
|
160
160
|
np.concatenate(
|
|
@@ -203,37 +203,37 @@ def _solve_for_coefs(
|
|
|
203
203
|
block_row_ind = np.arange(block_row_len).reshape(NQuad, 2 * NQuad)
|
|
204
204
|
block_row_sort = np.empty(block_row_len, dtype='int')
|
|
205
205
|
|
|
206
|
-
block_row_sort[:N_squared] = block_row_ind[:N, :N].
|
|
207
|
-
block_row_sort[N_squared : 2 * N_squared] = block_row_ind[N : 2 * N, :N].
|
|
208
|
-
block_row_sort[2 * N_squared : 4 * N_squared] = block_row_ind[:2 * N, N : 2 * N].
|
|
209
|
-
block_row_sort[4 * N_squared : 6 * N_squared] = block_row_ind[:2 * N, 2 * N : 3 * N].
|
|
210
|
-
block_row_sort[6 * N_squared : 7 * N_squared] = block_row_ind[:N, 3 * N : 4 * N].
|
|
211
|
-
block_row_sort[7 * N_squared : 8 * N_squared] = block_row_ind[N : 2 * N, 3 * N : 4 * N].
|
|
206
|
+
block_row_sort[:N_squared] = block_row_ind[:N, :N].ravel()
|
|
207
|
+
block_row_sort[N_squared : 2 * N_squared] = block_row_ind[N : 2 * N, :N].ravel()
|
|
208
|
+
block_row_sort[2 * N_squared : 4 * N_squared] = block_row_ind[:2 * N, N : 2 * N].ravel()
|
|
209
|
+
block_row_sort[4 * N_squared : 6 * N_squared] = block_row_ind[:2 * N, 2 * N : 3 * N].ravel()
|
|
210
|
+
block_row_sort[6 * N_squared : 7 * N_squared] = block_row_ind[:N, 3 * N : 4 * N].ravel()
|
|
211
|
+
block_row_sort[7 * N_squared : 8 * N_squared] = block_row_ind[N : 2 * N, 3 * N : 4 * N].ravel()
|
|
212
212
|
|
|
213
213
|
# BCs for the entire atmosphere
|
|
214
214
|
row_indices[:N_squared], col_indices[:N_squared] = _nd_slice_to_indexes(np.s_[:N, :N])
|
|
215
|
-
block_rows[:N_squared] = G_0_nn.
|
|
215
|
+
block_rows[:N_squared] = G_0_nn.ravel()
|
|
216
216
|
(
|
|
217
217
|
row_indices[N_squared : 2 * N_squared],
|
|
218
218
|
col_indices[N_squared : 2 * N_squared],
|
|
219
219
|
) = _nd_slice_to_indexes(np.s_[:N, N:NQuad])
|
|
220
220
|
block_rows[N_squared : 2 * N_squared] = (
|
|
221
221
|
G_0_np * np.exp(K_collect_m[0, :N] * scaled_tau_arr_with_0[1])[None, :]
|
|
222
|
-
).
|
|
222
|
+
).ravel()
|
|
223
223
|
(
|
|
224
224
|
row_indices[2 * N_squared : 3 * N_squared],
|
|
225
225
|
col_indices[2 * N_squared : 3 * N_squared],
|
|
226
226
|
) = _nd_slice_to_indexes(np.s_[dim - N : dim, dim - NQuad : dim - N])
|
|
227
227
|
block_rows[2 * N_squared : 3 * N_squared] = (
|
|
228
228
|
(G_L_pn - BDRF_LHS_contribution_neg) * E_Lm1L[None, :]
|
|
229
|
-
).
|
|
229
|
+
).ravel()
|
|
230
230
|
(
|
|
231
231
|
row_indices[3 * N_squared : 4 * N_squared],
|
|
232
232
|
col_indices[3 * N_squared : 4 * N_squared],
|
|
233
233
|
) = _nd_slice_to_indexes(np.s_[dim - N : dim, dim - N : dim])
|
|
234
234
|
block_rows[3 * N_squared : 4 * N_squared] = (
|
|
235
235
|
G_L_pp - BDRF_LHS_contribution_pos
|
|
236
|
-
).
|
|
236
|
+
).ravel()
|
|
237
237
|
|
|
238
238
|
# Interlayer / continuity BCs
|
|
239
239
|
for l in range(NLayers - 1):
|
|
@@ -263,12 +263,12 @@ def _solve_for_coefs(
|
|
|
263
263
|
] = col_indices_l[block_row_sort]
|
|
264
264
|
|
|
265
265
|
starting_ind = 4 * N_squared + l * block_row_len
|
|
266
|
-
block_rows[starting_ind : N_squared + starting_ind] = (G_l_pn * E_lm1l[None, :]).
|
|
267
|
-
block_rows[starting_ind + N_squared : 2 * N_squared + starting_ind] = (G_l_nn * E_lm1l[None, :]).
|
|
268
|
-
block_rows[starting_ind + 2 * N_squared : 4 * N_squared + starting_ind] = G_l_ap.
|
|
269
|
-
block_rows[starting_ind + 4 * N_squared : 6 * N_squared + starting_ind] = -G_lp1_an.
|
|
270
|
-
block_rows[starting_ind + 6 * N_squared : 7 * N_squared + starting_ind] = -(G_lp1_pp * E_llp1[None, :]).
|
|
271
|
-
block_rows[starting_ind + 7 * N_squared : 8 * N_squared + starting_ind] = -(G_lp1_np * E_llp1[None, :]).
|
|
266
|
+
block_rows[starting_ind : N_squared + starting_ind] = (G_l_pn * E_lm1l[None, :]).ravel()
|
|
267
|
+
block_rows[starting_ind + N_squared : 2 * N_squared + starting_ind] = (G_l_nn * E_lm1l[None, :]).ravel()
|
|
268
|
+
block_rows[starting_ind + 2 * N_squared : 4 * N_squared + starting_ind] = G_l_ap.ravel()
|
|
269
|
+
block_rows[starting_ind + 4 * N_squared : 6 * N_squared + starting_ind] = -G_lp1_an.ravel()
|
|
270
|
+
block_rows[starting_ind + 6 * N_squared : 7 * N_squared + starting_ind] = -(G_lp1_pp * E_llp1[None, :]).ravel()
|
|
271
|
+
block_rows[starting_ind + 7 * N_squared : 8 * N_squared + starting_ind] = -(G_lp1_np * E_llp1[None, :]).ravel()
|
|
272
272
|
|
|
273
273
|
LHS = sc.sparse.coo_matrix(
|
|
274
274
|
(
|
|
@@ -14,7 +14,7 @@ def pydisort(
|
|
|
14
14
|
Leg_coeffs_all,
|
|
15
15
|
mu0, I0, phi0,
|
|
16
16
|
NLeg=None,
|
|
17
|
-
|
|
17
|
+
NFourier=None,
|
|
18
18
|
b_pos=0,
|
|
19
19
|
b_neg=0,
|
|
20
20
|
only_flux=False,
|
|
@@ -42,9 +42,10 @@ def pydisort(
|
|
|
42
42
|
omega_arr : array
|
|
43
43
|
Single-scattering albedo of each atmospheric layer.
|
|
44
44
|
NQuad : int
|
|
45
|
-
Number of mu quadrature nodes.
|
|
45
|
+
Number of `mu` quadrature nodes.
|
|
46
46
|
Leg_coeffs_all : ndarray
|
|
47
|
-
All available unweighted phase function Legendre coefficients.
|
|
47
|
+
All available unweighted phase function Legendre coefficients.
|
|
48
|
+
Each coefficient should be between 0 and 1 inclusive.
|
|
48
49
|
mu0 : float
|
|
49
50
|
Cosine of polar angle of the incident beam.
|
|
50
51
|
I0 : float
|
|
@@ -53,7 +54,7 @@ def pydisort(
|
|
|
53
54
|
Azimuthal angle of the incident beam.
|
|
54
55
|
NLeg : optional, int
|
|
55
56
|
Number of phase function Legendre coefficients.
|
|
56
|
-
|
|
57
|
+
NFourier : optional, int
|
|
57
58
|
Number of Fourier modes to use in the intensity function.
|
|
58
59
|
b_pos : optional, 2darray or float
|
|
59
60
|
Dirichlet boundary condition for the upward direction.
|
|
@@ -66,7 +67,7 @@ def pydisort(
|
|
|
66
67
|
NT_cor : optional, bool
|
|
67
68
|
Perform Nakajima-Tanaka intensity corrections?
|
|
68
69
|
BDRF_Fourier_modes : optional, list
|
|
69
|
-
BDRF Fourier modes, each a function with arguments mu, -
|
|
70
|
+
BDRF Fourier modes, each a function with arguments `mu, -mu_p` of type array
|
|
70
71
|
and which output has the same dimensions as the outer product of the two arrays.
|
|
71
72
|
s_poly_coeffs : optional, array
|
|
72
73
|
Polynomial coefficients of isotropic internal sources.
|
|
@@ -77,21 +78,22 @@ def pydisort(
|
|
|
77
78
|
Returns
|
|
78
79
|
-------
|
|
79
80
|
array
|
|
80
|
-
All mu (cosine of polar angle) quadrature nodes.
|
|
81
|
+
All `mu` (cosine of polar angle) quadrature nodes.
|
|
81
82
|
function
|
|
82
|
-
(Energetic) Flux function with argument tau (type: array) for positive (upward) mu values.
|
|
83
|
+
(Energetic) Flux function with argument `tau` (type: array) for positive (upward) `mu` values.
|
|
83
84
|
Returns the diffuse flux magnitudes (type: array).
|
|
84
85
|
function
|
|
85
|
-
(Energetic) Flux function with argument tau (type: array) for negative (downward) mu values.
|
|
86
|
+
(Energetic) Flux function with argument `tau` (type: array) for negative (downward) `mu` values.
|
|
86
87
|
Returns a tuple of the diffuse and direct flux magnitudes respectively (type: (array, array)).
|
|
87
88
|
function
|
|
88
|
-
Zeroth Fourier mode of the intensity with argument tau (type: array).
|
|
89
|
-
Returns an ndarray with axes corresponding to variation with mu and tau respectively.
|
|
89
|
+
Zeroth Fourier mode of the intensity with argument `tau` (type: array).
|
|
90
|
+
Returns an ndarray with axes corresponding to variation with `mu` and `tau` respectively.
|
|
90
91
|
This function is useful for calculating actinic fluxes and other quantities of interest,
|
|
91
|
-
but reclassification of delta-scaled flux and other corrections must be done manually
|
|
92
|
+
but reclassification of delta-scaled flux and other corrections must be done manually
|
|
93
|
+
(for actinic flux `generate_diff_act_flux_funcs` will automatically perform the reclassification).
|
|
92
94
|
function, optional
|
|
93
|
-
Intensity function with arguments (tau, phi, return_Fourier_error=False) of types (array, array, bool)
|
|
94
|
-
Returns an ndarray with axes corresponding to variation with mu, tau, phi respectively.
|
|
95
|
+
Intensity function with arguments `(tau, phi, return_Fourier_error=False)` of types `(array, array, bool)`.
|
|
96
|
+
Returns an ndarray with axes corresponding to variation with `mu, tau, phi` respectively.
|
|
95
97
|
The optional flag `return_Fourier_error` determines whether the function will also return
|
|
96
98
|
the Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
|
|
97
99
|
|
|
@@ -114,8 +116,8 @@ def pydisort(
|
|
|
114
116
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
115
117
|
if NLeg is None:
|
|
116
118
|
NLeg = NQuad
|
|
117
|
-
if
|
|
118
|
-
|
|
119
|
+
if NFourier is None:
|
|
120
|
+
NFourier = NQuad
|
|
119
121
|
if np.all(b_pos == 0):
|
|
120
122
|
b_pos = 0
|
|
121
123
|
if np.all(b_neg == 0):
|
|
@@ -147,7 +149,7 @@ def pydisort(
|
|
|
147
149
|
# The user must supply at least as many phase function Legendre coefficients as intended for use
|
|
148
150
|
assert NLeg > 0
|
|
149
151
|
assert np.all(Leg_coeffs_all[:, 0] == 1)
|
|
150
|
-
assert np.all(np.
|
|
152
|
+
assert np.all(0 <= Leg_coeffs_all) and np.all(Leg_coeffs_all <= 1)
|
|
151
153
|
assert np.all(np.array([f.__code__.co_argcount for f in BDRF_Fourier_modes]) == 2)
|
|
152
154
|
assert NLeg <= NLeg_all
|
|
153
155
|
# Ensure that the first dimension of the following inputs corresponds to the number of layers
|
|
@@ -157,13 +159,13 @@ def pydisort(
|
|
|
157
159
|
assert len(f_arr) == NLayers
|
|
158
160
|
if there_is_iso_source:
|
|
159
161
|
assert np.shape(s_poly_coeffs)[0] == NLayers
|
|
160
|
-
# Conditions on the number of quadrature angles (NQuad), Legendre coefficients (NLeg) and loops (
|
|
162
|
+
# Conditions on the number of quadrature angles (NQuad), Legendre coefficients (NLeg) and loops (NFourier)
|
|
161
163
|
assert NQuad >= 2
|
|
162
164
|
assert NQuad % 2 == 0
|
|
163
|
-
assert
|
|
164
|
-
assert
|
|
165
|
+
assert NFourier > 0
|
|
166
|
+
assert NFourier <= NLeg
|
|
165
167
|
# Not strictly necessary but there will be tremendous inaccuracies if this is violated
|
|
166
|
-
assert
|
|
168
|
+
assert NLeg <= NQuad
|
|
167
169
|
# We require principal angles and a downward incident beam
|
|
168
170
|
assert I0 >= 0
|
|
169
171
|
if there_is_beam_source:
|
|
@@ -173,11 +175,11 @@ def pydisort(
|
|
|
173
175
|
if len(np.atleast_1d(b_pos)) == 1:
|
|
174
176
|
b_pos_is_scalar = True
|
|
175
177
|
else:
|
|
176
|
-
assert np.shape(b_pos) == (N,
|
|
178
|
+
assert np.shape(b_pos) == (N, NFourier)
|
|
177
179
|
if len(np.atleast_1d(b_neg)) == 1:
|
|
178
180
|
b_neg_is_scalar = True
|
|
179
181
|
else:
|
|
180
|
-
assert np.shape(b_neg) == (N,
|
|
182
|
+
assert np.shape(b_neg) == (N, NFourier)
|
|
181
183
|
# The fractional scattering must be between 0 and 1
|
|
182
184
|
assert np.all(0 <= f_arr) and np.all(f_arr <= 1)
|
|
183
185
|
# The minimum threshold is the minimum numbers of layers: 1
|
|
@@ -238,7 +240,7 @@ def pydisort(
|
|
|
238
240
|
scaled_tau_arr_with_0,
|
|
239
241
|
mu_arr_pos, mu_arr,
|
|
240
242
|
M_inv, W,
|
|
241
|
-
N, NQuad, NLeg,
|
|
243
|
+
N, NQuad, NLeg, NFourier,
|
|
242
244
|
NLayers, NBDRF,
|
|
243
245
|
atmos_is_multilayered,
|
|
244
246
|
weighted_scaled_Leg_coeffs,
|
|
@@ -319,159 +321,100 @@ def pydisort(
|
|
|
319
321
|
)
|
|
320
322
|
|
|
321
323
|
## Contribution from other layers
|
|
322
|
-
# TODO: Despite being vectorized and
|
|
323
|
-
# and is the bottleneck for the TMS correction
|
|
324
|
+
# TODO: Despite being vectorized and despite many attempts at further optimization,
|
|
325
|
+
# this block of code is slower than desired and is the bottleneck for the TMS correction.
|
|
324
326
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
325
327
|
if atmos_is_multilayered:
|
|
326
328
|
layers_arr = np.arange(NLayers)[:, None]
|
|
327
|
-
pos_contribution_mask = (l[None, :] < layers_arr).
|
|
328
|
-
neg_contribution_mask = (l[None, :] > layers_arr).
|
|
329
|
-
|
|
329
|
+
pos_contribution_mask = (l[None, :] < layers_arr).ravel()
|
|
330
|
+
neg_contribution_mask = (l[None, :] > layers_arr).ravel()
|
|
331
|
+
|
|
330
332
|
layers_arr_repeat = np.repeat(layers_arr, Ntau)
|
|
331
333
|
scaled_tau_tile = np.tile(scaled_tau, NLayers)
|
|
332
334
|
scaled_tau_arr_with_0_repeat = np.repeat(scaled_tau_arr_with_0, Ntau)
|
|
333
|
-
|
|
334
|
-
if
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
)
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
)
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
]
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
|
|
335
|
+
|
|
336
|
+
if np.any(pos_contribution_mask):
|
|
337
|
+
contribution_from_each_other_layer_pos = np.zeros((N, NLayers * Ntau, Nphi))
|
|
338
|
+
scaled_tau_l_tile_pos = scaled_tau_tile[pos_contribution_mask]
|
|
339
|
+
mathscr_B_l_repeat_posmasked = mathscr_B[
|
|
340
|
+
:N, layers_arr_repeat[pos_contribution_mask], :
|
|
341
|
+
]
|
|
342
|
+
scaled_tau_arr_with_0_l_tile_posmasked = scaled_tau_arr_with_0_repeat[:-Ntau][
|
|
343
|
+
pos_contribution_mask
|
|
344
|
+
]
|
|
345
|
+
scaled_tau_arr_with_0_lp1_tile_posmasked = scaled_tau_arr_with_0_repeat[Ntau:][
|
|
346
|
+
pos_contribution_mask
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
contribution_from_each_other_layer_pos[:, pos_contribution_mask, :] = (
|
|
350
|
+
mathscr_B_l_repeat_posmasked
|
|
351
|
+
* (
|
|
352
|
+
np.exp(
|
|
353
|
+
(scaled_tau_l_tile_pos - scaled_tau_arr_with_0_l_tile_posmasked)
|
|
354
|
+
/ mu_arr_pos[:, None]
|
|
355
|
+
- scaled_tau_arr_with_0_l_tile_posmasked / mu0
|
|
356
|
+
)
|
|
357
|
+
- np.exp(
|
|
358
|
+
(scaled_tau_l_tile_pos - scaled_tau_arr_with_0_lp1_tile_posmasked)
|
|
359
|
+
/ mu_arr_pos[:, None]
|
|
360
|
+
- scaled_tau_arr_with_0_lp1_tile_posmasked / mu0
|
|
361
|
+
)
|
|
362
|
+
)[:, :, None]
|
|
363
|
+
)
|
|
364
|
+
contribution_from_other_layers_pos = np.sum(
|
|
365
|
+
contribution_from_each_other_layer_pos.reshape((N, NLayers, Ntau, Nphi)),
|
|
366
|
+
axis=1,
|
|
367
|
+
)
|
|
368
|
+
if np.any(neg_contribution_mask):
|
|
369
|
+
contribution_from_each_other_layer_neg = np.zeros((N, NLayers * Ntau, Nphi))
|
|
370
|
+
scaled_tau_l_tile_neg = scaled_tau_tile[neg_contribution_mask]
|
|
371
|
+
mathscr_B_l_repeat_negmasked = mathscr_B[
|
|
372
|
+
N:, layers_arr_repeat[neg_contribution_mask], :
|
|
373
|
+
]
|
|
374
|
+
scaled_tau_arr_with_0_l_tile_negmasked = scaled_tau_arr_with_0_repeat[:-Ntau][
|
|
375
|
+
neg_contribution_mask
|
|
376
|
+
]
|
|
377
|
+
scaled_tau_arr_with_0_lp1_tile_negmasked = scaled_tau_arr_with_0_repeat[Ntau:][
|
|
378
|
+
neg_contribution_mask
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
contribution_from_each_other_layer_neg[:, neg_contribution_mask, :] = (
|
|
382
|
+
mathscr_B_l_repeat_negmasked
|
|
383
|
+
* (
|
|
384
|
+
np.exp(
|
|
385
|
+
(scaled_tau_arr_with_0_lp1_tile_negmasked - scaled_tau_l_tile_neg)
|
|
386
|
+
/ mu_arr_pos[:, None]
|
|
387
|
+
- scaled_tau_arr_with_0_lp1_tile_negmasked / mu0
|
|
388
|
+
)
|
|
389
|
+
- np.exp(
|
|
390
|
+
(scaled_tau_arr_with_0_l_tile_negmasked - scaled_tau_l_tile_neg)
|
|
391
|
+
/ mu_arr_pos[:, None]
|
|
392
|
+
- scaled_tau_arr_with_0_l_tile_negmasked / mu0
|
|
393
|
+
)
|
|
394
|
+
)[:, :, None]
|
|
395
|
+
)
|
|
396
|
+
contribution_from_other_layers_neg = np.sum(
|
|
397
|
+
contribution_from_each_other_layer_neg.reshape((N, NLayers, Ntau, Nphi)),
|
|
398
|
+
axis=1,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
if _is_compatible_with_autograd:
|
|
402
|
+
solution = (
|
|
395
403
|
mathscr_B[:, l, :]
|
|
396
404
|
* np.vstack((TMS_correction_pos, TMS_correction_neg))[:, :, None]
|
|
397
|
-
+ np.concatenate(
|
|
398
|
-
(contribution_from_other_layers_pos, contribution_from_other_layers_neg), axis=0
|
|
399
|
-
)
|
|
400
405
|
)
|
|
401
|
-
|
|
406
|
+
solution[:N, :, :] += contribution_from_other_layers_pos
|
|
407
|
+
solution[N:, :, :] += contribution_from_other_layers_neg
|
|
408
|
+
return solution
|
|
409
|
+
|
|
402
410
|
else:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
411
|
+
return mathscr_B[:, l, :] * np.vstack((TMS_correction_pos, TMS_correction_neg))[
|
|
412
|
+
:, :, None
|
|
413
|
+
] + np.concatenate(
|
|
414
|
+
(contribution_from_other_layers_pos, contribution_from_other_layers_neg),
|
|
415
|
+
axis=0,
|
|
408
416
|
)
|
|
409
|
-
tau_ind_tile = np.tile(np.arange(Ntau), NLayers)
|
|
410
|
-
|
|
411
|
-
if np.any(pos_contribution_mask):
|
|
412
|
-
scaled_tau_l_tile_pos = scaled_tau_tile[pos_contribution_mask]
|
|
413
|
-
mathscr_B_l_repeat_posmasked = mathscr_B[:N, layers_arr_repeat[pos_contribution_mask], :]
|
|
414
|
-
scaled_tau_arr_with_0_l_tile_posmasked = scaled_tau_arr_with_0_repeat[:-Ntau][
|
|
415
|
-
pos_contribution_mask
|
|
416
|
-
]
|
|
417
|
-
scaled_tau_arr_with_0_lp1_tile_posmasked = scaled_tau_arr_with_0_repeat[Ntau:][
|
|
418
|
-
pos_contribution_mask
|
|
419
|
-
]
|
|
420
|
-
|
|
421
|
-
np.add.at(
|
|
422
|
-
solution_movedaxis[:, :N, :],
|
|
423
|
-
tau_ind_tile[pos_contribution_mask],
|
|
424
|
-
np.moveaxis(
|
|
425
|
-
mathscr_B_l_repeat_posmasked
|
|
426
|
-
* (
|
|
427
|
-
np.exp(
|
|
428
|
-
(scaled_tau_l_tile_pos - scaled_tau_arr_with_0_l_tile_posmasked)
|
|
429
|
-
/ mu_arr_pos[:, None]
|
|
430
|
-
- scaled_tau_arr_with_0_l_tile_posmasked / mu0
|
|
431
|
-
)
|
|
432
|
-
- np.exp(
|
|
433
|
-
(scaled_tau_l_tile_pos - scaled_tau_arr_with_0_lp1_tile_posmasked)
|
|
434
|
-
/ mu_arr_pos[:, None]
|
|
435
|
-
- scaled_tau_arr_with_0_lp1_tile_posmasked / mu0
|
|
436
|
-
)
|
|
437
|
-
)[:, :, None],
|
|
438
|
-
0,
|
|
439
|
-
1,
|
|
440
|
-
),
|
|
441
|
-
)
|
|
442
|
-
if np.any(neg_contribution_mask):
|
|
443
|
-
scaled_tau_l_tile_neg = scaled_tau_tile[neg_contribution_mask]
|
|
444
|
-
mathscr_B_l_repeat_negmasked = mathscr_B[N:, layers_arr_repeat[neg_contribution_mask], :]
|
|
445
|
-
scaled_tau_arr_with_0_l_tile_negmasked = scaled_tau_arr_with_0_repeat[:-Ntau][
|
|
446
|
-
neg_contribution_mask
|
|
447
|
-
]
|
|
448
|
-
scaled_tau_arr_with_0_lp1_tile_negmasked = scaled_tau_arr_with_0_repeat[Ntau:][
|
|
449
|
-
neg_contribution_mask
|
|
450
|
-
]
|
|
451
|
-
|
|
452
|
-
np.add.at(
|
|
453
|
-
solution_movedaxis[:, N:, :],
|
|
454
|
-
tau_ind_tile[neg_contribution_mask],
|
|
455
|
-
np.moveaxis(
|
|
456
|
-
mathscr_B_l_repeat_negmasked
|
|
457
|
-
* (
|
|
458
|
-
np.exp(
|
|
459
|
-
(scaled_tau_arr_with_0_lp1_tile_negmasked - scaled_tau_l_tile_neg)
|
|
460
|
-
/ mu_arr_pos[:, None]
|
|
461
|
-
- scaled_tau_arr_with_0_lp1_tile_negmasked / mu0
|
|
462
|
-
)
|
|
463
|
-
- np.exp(
|
|
464
|
-
(scaled_tau_arr_with_0_l_tile_negmasked - scaled_tau_l_tile_neg)
|
|
465
|
-
/ mu_arr_pos[:, None]
|
|
466
|
-
- scaled_tau_arr_with_0_l_tile_negmasked / mu0
|
|
467
|
-
)
|
|
468
|
-
)[:, :, None],
|
|
469
|
-
0,
|
|
470
|
-
1,
|
|
471
|
-
),
|
|
472
|
-
)
|
|
473
417
|
|
|
474
|
-
return np.moveaxis(solution_movedaxis, 0, 1)
|
|
475
418
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
476
419
|
|
|
477
420
|
else:
|
|
@@ -544,14 +487,14 @@ def pydisort(
|
|
|
544
487
|
|
|
545
488
|
else:
|
|
546
489
|
if only_flux:
|
|
547
|
-
|
|
490
|
+
NFourier = 1 # We only need to solve for the 0th Fourier mode to compute the flux
|
|
548
491
|
return (mu_arr,) + _assemble_solution_functions(
|
|
549
492
|
scaled_omega_arr,
|
|
550
493
|
tau_arr,
|
|
551
494
|
scaled_tau_arr_with_0,
|
|
552
495
|
mu_arr_pos, mu_arr,
|
|
553
496
|
M_inv, W,
|
|
554
|
-
N, NQuad, NLeg,
|
|
497
|
+
N, NQuad, NLeg, NFourier,
|
|
555
498
|
NLayers, NBDRF,
|
|
556
499
|
atmos_is_multilayered,
|
|
557
500
|
weighted_scaled_Leg_coeffs,
|
|
@@ -30,7 +30,7 @@ def transform_interval(arr, c, d, a=-1, b=1):
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def transform_weights(weights, c, d, a=-1, b=1):
|
|
33
|
-
"""Transforms quadrature weights from interval [a, b] to [c, d].
|
|
33
|
+
"""Transforms an array of quadrature weights from interval [a, b] to [c, d].
|
|
34
34
|
|
|
35
35
|
Parameters
|
|
36
36
|
----------
|
|
@@ -55,8 +55,8 @@ def transform_weights(weights, c, d, a=-1, b=1):
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def calculate_nu(mu, phi, mu_p, phi_p):
|
|
58
|
-
"""Calculates the cosine of the scattering angle nu
|
|
59
|
-
between incident angle (mu_p, phi_p) and scattering angle (mu, phi)
|
|
58
|
+
"""Calculates the cosine of the scattering angle `nu`
|
|
59
|
+
between incident angle `(mu_p, phi_p)` and scattering angle `(mu, phi)`.
|
|
60
60
|
|
|
61
61
|
Parameters
|
|
62
62
|
----------
|
|
@@ -72,7 +72,7 @@ def calculate_nu(mu, phi, mu_p, phi_p):
|
|
|
72
72
|
Returns
|
|
73
73
|
-------
|
|
74
74
|
ndarray
|
|
75
|
-
Cosine of scattering angles which axes capture variation with mu, phi, mu_p, phi_p respectively.
|
|
75
|
+
Cosine of scattering angles which axes capture variation with `mu, phi, mu_p, phi_p` respectively.
|
|
76
76
|
|
|
77
77
|
"""
|
|
78
78
|
mu, phi, mu_p, phi_p = np.atleast_1d(mu, phi, mu_p, phi_p)
|
|
@@ -110,7 +110,7 @@ def Gauss_Legendre_quad(N, c=0, d=1):
|
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
def Clenshaw_Curtis_quad(Nphi, c=0, d=(2 * pi)):
|
|
113
|
-
"""Generates
|
|
113
|
+
"""Generates Clenshaw-Curtis quadrature zero points and weights for integration from c to d.
|
|
114
114
|
|
|
115
115
|
Parameters
|
|
116
116
|
----------
|
|
@@ -146,9 +146,9 @@ def Clenshaw_Curtis_quad(Nphi, c=0, d=(2 * pi)):
|
|
|
146
146
|
|
|
147
147
|
|
|
148
148
|
def generate_FD_mat(Ntau, a, b):
|
|
149
|
-
"""Generates a sparse first derivative
|
|
150
|
-
on [a,b] with `Ntau` grid points.
|
|
151
|
-
|
|
149
|
+
"""Generates a sparse first derivative central difference (second-order accuracy)
|
|
150
|
+
matrix on [a,b] in `csr` format with `Ntau` grid points.
|
|
151
|
+
Second order forward or backward differences are used at the boundaries.
|
|
152
152
|
|
|
153
153
|
Parameters
|
|
154
154
|
----------
|
|
@@ -181,15 +181,15 @@ def generate_FD_mat(Ntau, a, b):
|
|
|
181
181
|
first_deriv[-1, -2] = -2 / h
|
|
182
182
|
first_deriv[-1, -3] = 1 / (2 * h)
|
|
183
183
|
|
|
184
|
-
return tau_arr, first_deriv.
|
|
184
|
+
return tau_arr, first_deriv.tocsr()
|
|
185
185
|
|
|
186
186
|
|
|
187
187
|
def atleast_2d_append(*arys):
|
|
188
188
|
"""View inputs as arrays with at least two dimensions.
|
|
189
|
-
Dimensions are added
|
|
189
|
+
Dimensions are added as necessary to the back of the shape tuple rather than to the front.
|
|
190
190
|
|
|
191
|
-
This is exactly NumPy's
|
|
192
|
-
View the documentation for
|
|
191
|
+
This is exactly NumPy's ``numpy.atleast_2d`` function but altered to add dimensions to the back of the shape tuple rather than to the front.
|
|
192
|
+
View the documentation for ``numpy.atleast_2d`` at https://numpy.org/doc/stable/reference/generated/numpy.atleast_2d.html.
|
|
193
193
|
|
|
194
194
|
Parameters
|
|
195
195
|
----------
|
|
@@ -223,23 +223,23 @@ def atleast_2d_append(*arys):
|
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
def generate_diff_act_flux_funcs(u0):
|
|
226
|
-
"""Generates
|
|
227
|
-
This a use case of the u0 function that is an output of pydisort
|
|
226
|
+
"""Generates the up and down diffuse actinic flux functions respectively.
|
|
227
|
+
This is a use case of the ``u0`` function that is an output of ``pydisort``.
|
|
228
228
|
The reclassification of delta-scaled actinic flux is automatically performed.
|
|
229
229
|
|
|
230
230
|
Parameters
|
|
231
231
|
----------
|
|
232
232
|
u0 : func
|
|
233
233
|
Zeroth Fourier mode of the intensity.
|
|
234
|
-
See the fourth "return" of the
|
|
234
|
+
See the fourth "return" of the ``pydisort`` function.
|
|
235
235
|
|
|
236
236
|
Returns
|
|
237
237
|
-------
|
|
238
238
|
function
|
|
239
|
-
Actinic flux function with argument tau (type: array) for positive (upward) mu values.
|
|
239
|
+
Actinic flux function with argument `tau` (type: array) for positive (upward) `mu` values.
|
|
240
240
|
Returns the diffuse flux magnitudes (type: array).
|
|
241
241
|
function
|
|
242
|
-
Actinic flux function with argument tau (type: array) for negative (downward) mu values.
|
|
242
|
+
Actinic flux function with argument `tau` (type: array) for negative (downward) `mu` values.
|
|
243
243
|
Returns the diffuse flux magnitudes (type: array).
|
|
244
244
|
|
|
245
245
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PythonicDISORT
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
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
|
|
@@ -32,25 +32,25 @@ In addition, upon the request of a user, we added a subroutine to calculate acti
|
|
|
32
32
|
|
|
33
33
|
You may contact me, Dion, through dh3065@columbia.edu.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Our **GitHub repository:** https://github.com/LDEO-CREW/Pythonic-DISORT also includes our F2PY-wrapped Stamnes DISORT (version 4.0.99) in the `disort4.0.99_f2py` directory.
|
|
36
36
|
The original was downloaded from http://www.rtatmocn.com/disort/. The wrapper is inspired by https://github.com/kconnour/pyRT_DISORT.
|
|
37
37
|
|
|
38
38
|
# Documentation
|
|
39
39
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
40
40
|
|
|
41
|
-
Also see the accompanying Jupyter Notebook
|
|
42
|
-
of our GitHub repository
|
|
41
|
+
Also see the accompanying Jupyter Notebook `Pythonic-DISORT.ipynb` in the `docs` directory
|
|
42
|
+
of our GitHub repository.
|
|
43
43
|
The Jupyter Notebook provides comprehensive documentation, suggested inputs, explanations,
|
|
44
44
|
mathematical derivations and verification tests.
|
|
45
45
|
We highly recommend reading the non-optional parts of sections 1 and 2 before use.
|
|
46
46
|
|
|
47
47
|
## PyTest and examples of how to use PythonicDISORT
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
Not only do we have verification tests in the Jupyter Notebook, we also used PyTest to recreate most of the test problems in Stamnes' `disotest.f90`;
|
|
50
|
+
`disotest.f90` is included in the `disort4.0.99_f2py` directory of our GitHub repository. In these tests, the solutions from PythonicDISORT are compared
|
|
51
51
|
against solutions from our F2PY-wrapped Stamnes' DISORT (version 4.0.99). With PyTest installed, execute the console command `pytest`
|
|
52
52
|
in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks, one for each test,
|
|
53
|
-
to show how the
|
|
53
|
+
to show how the tests were implemented. These notebooks double up as examples of how to use PythonicDISORT.
|
|
54
54
|
|
|
55
55
|
# Installation
|
|
56
56
|
|
|
@@ -74,16 +74,16 @@ must be set up to run the last section (section 6).
|
|
|
74
74
|
|
|
75
75
|
## Compatibility
|
|
76
76
|
|
|
77
|
-
The PythonicDISORT package should be system agnostic given its minimal dependencies and pure Python code.
|
|
77
|
+
The PythonicDISORT package *should* be system agnostic given its minimal dependencies and pure Python code.
|
|
78
78
|
We do not guarantee that our Jupyter Notebook and F2PY-wrapped Stamnes' DISORT will work on other systems though.
|
|
79
79
|
The latter will almost certainly need user edits to work and in any case it requires FORTRAN compilers which
|
|
80
|
-
are not included in
|
|
80
|
+
are not included in our GitHub repository. Everything in the repository was built and tested on Windows 11 but
|
|
81
81
|
not yet tested on other systems.
|
|
82
82
|
|
|
83
83
|
# Acknowledgements
|
|
84
84
|
|
|
85
85
|
I acknowledge funding from NSF through the Learning the Earth with Artificial intelligence and Physics (LEAP)
|
|
86
|
-
Science and Technology Center (STC) (Award #2019625).
|
|
86
|
+
Science and Technology Center (STC) (Award #2019625) under which this package was initially created.
|
|
87
87
|
|
|
88
88
|
# References
|
|
89
89
|
1) S. Chandrasekhar. 1960. *Radiative Transfer.*
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{PythonicDISORT-0.6.0 → PythonicDISORT-0.6.2}/src/PythonicDISORT.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|