PythonicDISORT 0.7.0__tar.gz → 0.8.0__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.7.0/src/PythonicDISORT.egg-info → pythonicdisort-0.8.0}/PKG-INFO +25 -25
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/README.md +24 -24
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/pyproject.toml +1 -1
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py +181 -53
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT/_solve_for_coeffs.py +3 -3
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT/_solve_for_gen_and_part_sols.py +16 -14
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT/pydisort.py +173 -96
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT/subroutines.py +40 -24
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0/src/PythonicDISORT.egg-info}/PKG-INFO +25 -25
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/LICENSE.md +0 -0
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/setup.cfg +0 -0
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT/__init__.py +0 -0
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT.egg-info/SOURCES.txt +0 -0
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT.egg-info/dependency_links.txt +0 -0
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT.egg-info/requires.txt +0 -0
- {pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/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.
|
|
3
|
+
Version: 0.8.0
|
|
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
|
|
@@ -20,42 +20,43 @@ Requires-Dist: jupyter>1.0.0; extra == "notebook-dependencies"
|
|
|
20
20
|
Requires-Dist: notebook>6.5.2; extra == "notebook-dependencies"
|
|
21
21
|
|
|
22
22
|
# Introduction
|
|
23
|
-
The PythonicDISORT package is a Discrete Ordinates Solver for the (1D) Radiative Transfer Equation
|
|
23
|
+
The PythonicDISORT package is a Discrete Ordinates Solver for the (1D) Radiative Transfer Equation
|
|
24
|
+
in a plane-parallel, horizontally homogeneous atmosphere.
|
|
24
25
|
It is coded entirely in Python 3 and is a reimplementation instead of a wrapper.
|
|
25
26
|
While PythonicDISORT has been optimized for speed, it will naturally be slower than similar FORTRAN algorithms.
|
|
26
27
|
On the other hand, PythonicDISORT should be easier to install, use, and modify than FORTRAN-based Discrete Ordinates Solvers.
|
|
27
28
|
|
|
28
|
-
PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8]) and has its main features:
|
|
29
|
-
delta-M scaling, Nakajima-Tanaka (NT) corrections, only flux option, isotropic internal
|
|
29
|
+
PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8]) and has its main features: multi-layer solver,
|
|
30
|
+
delta-M scaling, Nakajima-Tanaka (NT) corrections, only flux option, direct beam source, isotropic internal source (blackbody emission),
|
|
30
31
|
Dirichlet boundary conditions (diffuse flux boundary sources), Bi-Directional Reflectance Function (BDRF) for surface reflection, and more.
|
|
31
|
-
In addition,
|
|
32
|
+
In addition, subroutine to calculate actinic fluxes was added to satisfy a user request and further feature requests as well as feedback are welcome.
|
|
32
33
|
|
|
33
34
|
You may contact me, Dion, through dh3065@columbia.edu.
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
The original was downloaded from http://www.rtatmocn.com/disort/. The wrapper is inspired by https://github.com/kconnour/pyRT_DISORT.
|
|
36
|
+
The **GitHub repository** is https://github.com/LDEO-CREW/Pythonic-DISORT.
|
|
37
37
|
|
|
38
38
|
# Documentation
|
|
39
39
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
40
40
|
|
|
41
41
|
Also see the accompanying Jupyter Notebook `Pythonic-DISORT.ipynb` in the `docs` directory
|
|
42
42
|
of our GitHub repository.
|
|
43
|
-
|
|
43
|
+
This Jupyter Notebook provides comprehensive documentation, suggested inputs, explanations,
|
|
44
44
|
mathematical derivations and verification tests.
|
|
45
|
-
|
|
45
|
+
It is highly recommended that new users read the non-optional parts of sections 1 and 2.
|
|
46
46
|
|
|
47
47
|
## PyTest and examples of how to use PythonicDISORT
|
|
48
48
|
|
|
49
|
-
Not only
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
Not only are there verification tests in `Pythonic-DISORT.ipynb`,
|
|
50
|
+
most of the test problems in Stamnes' `disotest.f90` (download DISORT 4.0.99 from http://www.rtatmocn.com/disort/) have also been recreated.
|
|
51
|
+
In these tests, the solutions from PythonicDISORT are compared against solutions
|
|
52
|
+
from a F2PY-wrapped Stamnes' DISORT (version 4.0.99; wrapper inspired by https://github.com/kconnour/pyRT_DISORT). With PyTest installed, execute the console command `pytest`
|
|
53
|
+
in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks that correspond to each test
|
|
54
|
+
to show how it was implemented. These notebooks double up as examples of how to use PythonicDISORT.
|
|
54
55
|
|
|
55
56
|
# Installation
|
|
56
57
|
|
|
57
58
|
* From PyPI: `pip install PythonicDISORT`
|
|
58
|
-
* From Conda-forge: (TODO:
|
|
59
|
+
* From Conda-forge: (TODO: need to first publish on Conda-forge)
|
|
59
60
|
* 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*)
|
|
60
61
|
|
|
61
62
|
## Requirements to run PythonicDISORT
|
|
@@ -64,21 +65,18 @@ to show how the tests were implemented. These notebooks double up as examples of
|
|
|
64
65
|
* `scipy >= 1.8.0`
|
|
65
66
|
* (OPTIONAL) `pytest >= 6.2.5` (Required to use the command `pytest`, see *PyTest and examples of how to use PythonicDISORT*)
|
|
66
67
|
|
|
67
|
-
## Additional requirements to run the Jupyter Notebook
|
|
68
|
+
## (OPTIONAL) Additional requirements to run the Jupyter Notebook
|
|
68
69
|
* `autograd >= 1.5`
|
|
69
70
|
* `jupyter > 1.0.0`
|
|
70
71
|
* `notebook > 6.5.2`
|
|
72
|
+
* `matplotlib >= 3.6.0`
|
|
71
73
|
|
|
72
|
-
In addition,
|
|
73
|
-
must be set up to run the last section (section 6).
|
|
74
|
+
In addition, a F2PY-wrapped Stamnes' DISORT, or equivalent, is required to properly run the last section (section 6).
|
|
74
75
|
|
|
75
76
|
## Compatibility
|
|
76
77
|
|
|
77
|
-
The PythonicDISORT package
|
|
78
|
-
|
|
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 our GitHub repository. Everything in the repository was built and tested on Windows 11 but
|
|
81
|
-
not yet tested on other systems.
|
|
78
|
+
The PythonicDISORT package should be system agnostic given its minimal dependencies and pure Python code.
|
|
79
|
+
Everything in the repository was built and tested on Windows 11.
|
|
82
80
|
|
|
83
81
|
# Acknowledgements
|
|
84
82
|
|
|
@@ -106,6 +104,8 @@ Science and Technology Center (STC) (Award #2019625) under which this package wa
|
|
|
106
104
|
|
|
107
105
|
10) Trefethen, L. N.. 1996. *Finite difference and spectral methods for ordinary and partial differential equations.* https://people.maths.ox.ac.uk/trefethen/pdetext.html.
|
|
108
106
|
|
|
109
|
-
11)
|
|
107
|
+
11) Knut Stamnes. 1982. *On the computation of angular distributions of radiation in planetary atmospheres.* https://www.sciencedirect.com/science/article/pii/0022407382900966.
|
|
110
108
|
|
|
111
|
-
12)
|
|
109
|
+
12) 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.
|
|
110
|
+
|
|
111
|
+
13) Connour, Kyle and Wolff, Michael. 2020. *pyRT_DISORT: A pre-processing front-end to help make DISORT simulations easier in Python.* https://github.com/kconnour/pyRT_DISORT.
|
|
@@ -1,40 +1,41 @@
|
|
|
1
1
|
# Introduction
|
|
2
|
-
The PythonicDISORT package is a Discrete Ordinates Solver for the (1D) Radiative Transfer Equation
|
|
2
|
+
The PythonicDISORT package is a Discrete Ordinates Solver for the (1D) Radiative Transfer Equation
|
|
3
|
+
in a plane-parallel, horizontally homogeneous atmosphere.
|
|
3
4
|
It is coded entirely in Python 3 and is a reimplementation instead of a wrapper.
|
|
4
5
|
While PythonicDISORT has been optimized for speed, it will naturally be slower than similar FORTRAN algorithms.
|
|
5
6
|
On the other hand, PythonicDISORT should be easier to install, use, and modify than FORTRAN-based Discrete Ordinates Solvers.
|
|
6
7
|
|
|
7
|
-
PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8]) and has its main features:
|
|
8
|
-
delta-M scaling, Nakajima-Tanaka (NT) corrections, only flux option, isotropic internal
|
|
8
|
+
PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8]) and has its main features: multi-layer solver,
|
|
9
|
+
delta-M scaling, Nakajima-Tanaka (NT) corrections, only flux option, direct beam source, isotropic internal source (blackbody emission),
|
|
9
10
|
Dirichlet boundary conditions (diffuse flux boundary sources), Bi-Directional Reflectance Function (BDRF) for surface reflection, and more.
|
|
10
|
-
In addition,
|
|
11
|
+
In addition, subroutine to calculate actinic fluxes was added to satisfy a user request and further feature requests as well as feedback are welcome.
|
|
11
12
|
|
|
12
13
|
You may contact me, Dion, through dh3065@columbia.edu.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
The original was downloaded from http://www.rtatmocn.com/disort/. The wrapper is inspired by https://github.com/kconnour/pyRT_DISORT.
|
|
15
|
+
The **GitHub repository** is https://github.com/LDEO-CREW/Pythonic-DISORT.
|
|
16
16
|
|
|
17
17
|
# Documentation
|
|
18
18
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
19
19
|
|
|
20
20
|
Also see the accompanying Jupyter Notebook `Pythonic-DISORT.ipynb` in the `docs` directory
|
|
21
21
|
of our GitHub repository.
|
|
22
|
-
|
|
22
|
+
This Jupyter Notebook provides comprehensive documentation, suggested inputs, explanations,
|
|
23
23
|
mathematical derivations and verification tests.
|
|
24
|
-
|
|
24
|
+
It is highly recommended that new users read the non-optional parts of sections 1 and 2.
|
|
25
25
|
|
|
26
26
|
## PyTest and examples of how to use PythonicDISORT
|
|
27
27
|
|
|
28
|
-
Not only
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
Not only are there verification tests in `Pythonic-DISORT.ipynb`,
|
|
29
|
+
most of the test problems in Stamnes' `disotest.f90` (download DISORT 4.0.99 from http://www.rtatmocn.com/disort/) have also been recreated.
|
|
30
|
+
In these tests, the solutions from PythonicDISORT are compared against solutions
|
|
31
|
+
from a F2PY-wrapped Stamnes' DISORT (version 4.0.99; wrapper inspired by https://github.com/kconnour/pyRT_DISORT). With PyTest installed, execute the console command `pytest`
|
|
32
|
+
in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks that correspond to each test
|
|
33
|
+
to show how it was implemented. These notebooks double up as examples of how to use PythonicDISORT.
|
|
33
34
|
|
|
34
35
|
# Installation
|
|
35
36
|
|
|
36
37
|
* From PyPI: `pip install PythonicDISORT`
|
|
37
|
-
* From Conda-forge: (TODO:
|
|
38
|
+
* From Conda-forge: (TODO: need to first publish on Conda-forge)
|
|
38
39
|
* 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*)
|
|
39
40
|
|
|
40
41
|
## Requirements to run PythonicDISORT
|
|
@@ -43,21 +44,18 @@ to show how the tests were implemented. These notebooks double up as examples of
|
|
|
43
44
|
* `scipy >= 1.8.0`
|
|
44
45
|
* (OPTIONAL) `pytest >= 6.2.5` (Required to use the command `pytest`, see *PyTest and examples of how to use PythonicDISORT*)
|
|
45
46
|
|
|
46
|
-
## Additional requirements to run the Jupyter Notebook
|
|
47
|
+
## (OPTIONAL) Additional requirements to run the Jupyter Notebook
|
|
47
48
|
* `autograd >= 1.5`
|
|
48
49
|
* `jupyter > 1.0.0`
|
|
49
50
|
* `notebook > 6.5.2`
|
|
51
|
+
* `matplotlib >= 3.6.0`
|
|
50
52
|
|
|
51
|
-
In addition,
|
|
52
|
-
must be set up to run the last section (section 6).
|
|
53
|
+
In addition, a F2PY-wrapped Stamnes' DISORT, or equivalent, is required to properly run the last section (section 6).
|
|
53
54
|
|
|
54
55
|
## Compatibility
|
|
55
56
|
|
|
56
|
-
The PythonicDISORT package
|
|
57
|
-
|
|
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 our GitHub repository. Everything in the repository was built and tested on Windows 11 but
|
|
60
|
-
not yet tested on other systems.
|
|
57
|
+
The PythonicDISORT package should be system agnostic given its minimal dependencies and pure Python code.
|
|
58
|
+
Everything in the repository was built and tested on Windows 11.
|
|
61
59
|
|
|
62
60
|
# Acknowledgements
|
|
63
61
|
|
|
@@ -85,6 +83,8 @@ Science and Technology Center (STC) (Award #2019625) under which this package wa
|
|
|
85
83
|
|
|
86
84
|
10) Trefethen, L. N.. 1996. *Finite difference and spectral methods for ordinary and partial differential equations.* https://people.maths.ox.ac.uk/trefethen/pdetext.html.
|
|
87
85
|
|
|
88
|
-
11)
|
|
86
|
+
11) Knut Stamnes. 1982. *On the computation of angular distributions of radiation in planetary atmospheres.* https://www.sciencedirect.com/science/article/pii/0022407382900966.
|
|
89
87
|
|
|
90
|
-
12)
|
|
88
|
+
12) 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
|
+
|
|
90
|
+
13) Connour, Kyle and Wolff, Michael. 2020. *pyRT_DISORT: A pre-processing front-end to help make DISORT simulations easier in Python.* https://github.com/kconnour/pyRT_DISORT.
|
{pythonicdisort-0.7.0 → pythonicdisort-0.8.0}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py
RENAMED
|
@@ -28,9 +28,9 @@ def _assemble_intensity_and_fluxes(
|
|
|
28
28
|
NLayers_to_use_sparse, # Number of layers above or equal which to use sparse matrices
|
|
29
29
|
_autograd_compatible, # Should the output functions be compatible with autograd?
|
|
30
30
|
):
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
"""Assembles the solution functions: intensity `u`, upward flux `flux_up`, downward flux `flux_down`
|
|
32
|
+
from the previously solved eigenpairs and coefficients from the boundary conditions.
|
|
33
|
+
Returns these solution functions.
|
|
34
34
|
This function is wrapped by the `pydisort` function.
|
|
35
35
|
It should be called through `pydisort` and never directly.
|
|
36
36
|
It has many seemingly redundant arguments, which are described below,
|
|
@@ -161,7 +161,15 @@ def _assemble_intensity_and_fluxes(
|
|
|
161
161
|
|
|
162
162
|
# Construct the intensity function (refer to Section 3.7 of the Comprehensive Documentation)
|
|
163
163
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
164
|
-
def u(tau, phi, return_Fourier_error=False):
|
|
164
|
+
def u(tau, phi, return_Fourier_error=False, is_antiderivative_wrt_tau=False):
|
|
165
|
+
"""
|
|
166
|
+
Intensity function with arguments `(tau, phi)` of types `(array or float, array or float)`.
|
|
167
|
+
Returns an ndarray with axes corresponding to variation with `mu, tau, phi` respectively.
|
|
168
|
+
Pass `return_Fourier_error = True` (defaults to `False`) to return the
|
|
169
|
+
Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
|
|
170
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
171
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
172
|
+
"""
|
|
165
173
|
tau = np.atleast_1d(tau)
|
|
166
174
|
phi = np.atleast_1d(phi)
|
|
167
175
|
assert np.all(tau >= 0)
|
|
@@ -179,7 +187,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
179
187
|
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
180
188
|
else:
|
|
181
189
|
scaled_tau = tau
|
|
182
|
-
|
|
190
|
+
|
|
183
191
|
exponent = np.concatenate(
|
|
184
192
|
[
|
|
185
193
|
K_collect[:, l, :N] * (scaled_tau - scaled_tau_arr_lm1)[None, :, None],
|
|
@@ -187,14 +195,33 @@ def _assemble_intensity_and_fluxes(
|
|
|
187
195
|
],
|
|
188
196
|
axis=-1,
|
|
189
197
|
)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
198
|
+
|
|
199
|
+
if is_antiderivative_wrt_tau:
|
|
200
|
+
if there_is_beam_source:
|
|
201
|
+
um = np.einsum(
|
|
202
|
+
"mtij, mtj -> mti",
|
|
203
|
+
GC_collect[:, l, :, :],
|
|
204
|
+
np.exp(exponent) / (scale_tau[None, l, None] * K_collect[:, l, :]),
|
|
205
|
+
optimize=True,
|
|
206
|
+
) + B_collect[:, l, :] * np.exp(-scaled_tau[None, :, None] / mu0) / (
|
|
207
|
+
-scale_tau[None, l, None] / mu0
|
|
208
|
+
)
|
|
209
|
+
else:
|
|
210
|
+
um = np.einsum(
|
|
211
|
+
"mtij, mtj -> mti",
|
|
212
|
+
GC_collect[:, l, :, :],
|
|
213
|
+
np.exp(exponent) / (scale_tau[None, l, None] * K_collect[:, l, :]),
|
|
214
|
+
optimize=True,
|
|
215
|
+
)
|
|
194
216
|
else:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
217
|
+
if there_is_beam_source:
|
|
218
|
+
um = np.einsum(
|
|
219
|
+
"mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
|
|
220
|
+
) + B_collect[:, l, :] * np.exp(-scaled_tau[None, :, None] / mu0)
|
|
221
|
+
else:
|
|
222
|
+
um = np.einsum(
|
|
223
|
+
"mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
|
|
224
|
+
)
|
|
198
225
|
|
|
199
226
|
# Contribution from particular solution for isotropic internal sources
|
|
200
227
|
if there_is_iso_source:
|
|
@@ -207,6 +234,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
207
234
|
G_inv_collect_0,
|
|
208
235
|
mu_arr,
|
|
209
236
|
_autograd_compatible,
|
|
237
|
+
is_antiderivative_wrt_tau
|
|
210
238
|
)
|
|
211
239
|
|
|
212
240
|
if _autograd_compatible:
|
|
@@ -229,20 +257,43 @@ def _assemble_intensity_and_fluxes(
|
|
|
229
257
|
],
|
|
230
258
|
axis=-1,
|
|
231
259
|
)
|
|
232
|
-
if
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
260
|
+
if is_antiderivative_wrt_tau:
|
|
261
|
+
if there_is_beam_source:
|
|
262
|
+
ulast = np.einsum(
|
|
263
|
+
"tij, tj -> it",
|
|
264
|
+
GC_collect[-1, l, :, :],
|
|
265
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect[-1, l, :]),
|
|
266
|
+
optimize=True,
|
|
267
|
+
) + B_collect[-1, l, :].T * np.exp(-scaled_tau[None, :] / mu0) / (
|
|
268
|
+
-scale_tau[None, l] / mu0
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
ulast = np.einsum(
|
|
272
|
+
"tij, tj -> it",
|
|
273
|
+
GC_collect[-1, l, :, :],
|
|
274
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect[-1, l, :]),
|
|
275
|
+
optimize=True,
|
|
276
|
+
)
|
|
236
277
|
else:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
278
|
+
if there_is_beam_source:
|
|
279
|
+
ulast = np.einsum(
|
|
280
|
+
"tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
|
|
281
|
+
) + B_collect[-1, l, :].T * np.exp(-scaled_tau[None, :] / mu0)
|
|
282
|
+
else:
|
|
283
|
+
ulast = np.einsum(
|
|
284
|
+
"tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
|
|
285
|
+
)
|
|
286
|
+
|
|
240
287
|
intensities_abs = np.abs(intensities)
|
|
241
288
|
Fourier_error = np.max(
|
|
242
|
-
np.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
289
|
+
np.divide(
|
|
290
|
+
np.abs(
|
|
291
|
+
ulast[:, :, None] * np.cos((NFourier - 1) * (phi0 - phi))[None, None, :]
|
|
292
|
+
),
|
|
293
|
+
intensities_abs,
|
|
294
|
+
out=np.zeros_like(intensities_abs),
|
|
295
|
+
where=intensities_abs > 1e-8,
|
|
296
|
+
)
|
|
246
297
|
)
|
|
247
298
|
return I0_orig * np.squeeze(intensities), Fourier_error
|
|
248
299
|
else:
|
|
@@ -251,7 +302,16 @@ def _assemble_intensity_and_fluxes(
|
|
|
251
302
|
|
|
252
303
|
# Construct u0
|
|
253
304
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
254
|
-
def u0(tau,
|
|
305
|
+
def u0(tau, is_antiderivative_wrt_tau=False, _return_act_dscale_for_reclass=False):
|
|
306
|
+
"""
|
|
307
|
+
Zeroth Fourier mode of the intensity with argument `tau` (type: array or float).
|
|
308
|
+
Returns an ndarray with axes corresponding to variation with `mu` and `tau` respectively.
|
|
309
|
+
This function is useful for calculating actinic fluxes and other quantities of interest,
|
|
310
|
+
but reclassification of delta-scaled flux and other corrections must be done manually
|
|
311
|
+
(for actinic flux `generate_diff_act_flux_funcs` will automatically perform the reclassification).
|
|
312
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
313
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
314
|
+
"""
|
|
255
315
|
tau = np.atleast_1d(tau)
|
|
256
316
|
assert np.all(tau >= 0)
|
|
257
317
|
assert np.all(tau <= tau_arr[-1])
|
|
@@ -268,11 +328,14 @@ def _assemble_intensity_and_fluxes(
|
|
|
268
328
|
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
269
329
|
# The following complements the function `subroutines.generate_diff_act_flux_funcs`
|
|
270
330
|
# in performing the reclassification of delta-scaled actinic flux
|
|
271
|
-
if
|
|
272
|
-
|
|
331
|
+
if _return_act_dscale_for_reclass:
|
|
332
|
+
if is_antiderivative_wrt_tau:
|
|
333
|
+
act_dscale_reclassification = I0 * np.exp(-scaled_tau / mu0) / (-scale_tau[l] / mu0) - I0 * np.exp(-tau / mu0) * -mu0
|
|
334
|
+
else:
|
|
335
|
+
act_dscale_reclassification = I0 * np.exp(-scaled_tau / mu0) - I0 * np.exp(-tau / mu0)
|
|
273
336
|
else:
|
|
274
337
|
scaled_tau = tau
|
|
275
|
-
if
|
|
338
|
+
if _return_act_dscale_for_reclass:
|
|
276
339
|
act_dscale_reclassification = 0
|
|
277
340
|
|
|
278
341
|
exponent = np.concatenate(
|
|
@@ -282,14 +345,33 @@ def _assemble_intensity_and_fluxes(
|
|
|
282
345
|
],
|
|
283
346
|
axis=-1,
|
|
284
347
|
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
348
|
+
|
|
349
|
+
if is_antiderivative_wrt_tau:
|
|
350
|
+
if there_is_beam_source:
|
|
351
|
+
u0 = np.einsum(
|
|
352
|
+
"tij, tj -> it",
|
|
353
|
+
GC_collect_0[l, :, :],
|
|
354
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
|
|
355
|
+
optimize=True,
|
|
356
|
+
) + B_collect_0[l, :].T * np.exp(-scaled_tau[None, :] / mu0) / (
|
|
357
|
+
-scale_tau[None, l] / mu0
|
|
358
|
+
)
|
|
359
|
+
else:
|
|
360
|
+
u0 = np.einsum(
|
|
361
|
+
"tij, tj -> it",
|
|
362
|
+
GC_collect_0[l, :, :],
|
|
363
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
|
|
364
|
+
optimize=True,
|
|
365
|
+
)
|
|
289
366
|
else:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
367
|
+
if there_is_beam_source:
|
|
368
|
+
u0 = np.einsum(
|
|
369
|
+
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
370
|
+
) + B_collect_0[l, :].T * np.exp(-scaled_tau[None, :] / mu0)
|
|
371
|
+
else:
|
|
372
|
+
u0 = np.einsum(
|
|
373
|
+
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
374
|
+
)
|
|
293
375
|
|
|
294
376
|
# Contribution from particular solution for isotropic internal sources
|
|
295
377
|
if there_is_iso_source:
|
|
@@ -302,10 +384,11 @@ def _assemble_intensity_and_fluxes(
|
|
|
302
384
|
G_inv_collect_0,
|
|
303
385
|
mu_arr,
|
|
304
386
|
_autograd_compatible,
|
|
387
|
+
is_antiderivative_wrt_tau
|
|
305
388
|
)
|
|
306
|
-
u0
|
|
389
|
+
u0 = u0 + _mathscr_v_contribution
|
|
307
390
|
|
|
308
|
-
if
|
|
391
|
+
if _return_act_dscale_for_reclass:
|
|
309
392
|
return I0_orig * np.squeeze(u0), act_dscale_reclassification
|
|
310
393
|
else:
|
|
311
394
|
return I0_orig * np.squeeze(u0)
|
|
@@ -320,7 +403,13 @@ def _assemble_intensity_and_fluxes(
|
|
|
320
403
|
B_neg = B_collect_0[:, N:].T
|
|
321
404
|
|
|
322
405
|
|
|
323
|
-
def flux_up(tau):
|
|
406
|
+
def flux_up(tau, is_antiderivative_wrt_tau=False):
|
|
407
|
+
"""
|
|
408
|
+
(Energetic) Flux function with argument `tau` (type: array or float) for positive (upward) `mu` values.
|
|
409
|
+
Returns the diffuse flux magnitudes (same type and size as `tau`).
|
|
410
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
411
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
412
|
+
"""
|
|
324
413
|
tau = np.atleast_1d(tau)
|
|
325
414
|
assert np.all(tau >= 0)
|
|
326
415
|
assert np.all(tau <= tau_arr[-1])
|
|
@@ -348,12 +437,18 @@ def _assemble_intensity_and_fluxes(
|
|
|
348
437
|
G_inv_collect_0,
|
|
349
438
|
mu_arr,
|
|
350
439
|
_autograd_compatible,
|
|
440
|
+
is_antiderivative_wrt_tau
|
|
351
441
|
)[:N, :]
|
|
352
442
|
else:
|
|
353
443
|
_mathscr_v_contribution = 0
|
|
354
|
-
|
|
444
|
+
|
|
355
445
|
if there_is_beam_source:
|
|
356
|
-
|
|
446
|
+
if is_antiderivative_wrt_tau:
|
|
447
|
+
direct_beam_contribution = (
|
|
448
|
+
B_pos[:, l] * np.exp(-scaled_tau[None, :] / mu0) / (-scale_tau[None, l] / mu0)
|
|
449
|
+
)
|
|
450
|
+
else:
|
|
451
|
+
direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau[None, :] / mu0)
|
|
357
452
|
else:
|
|
358
453
|
direct_beam_contribution = 0
|
|
359
454
|
|
|
@@ -364,17 +459,36 @@ def _assemble_intensity_and_fluxes(
|
|
|
364
459
|
],
|
|
365
460
|
axis=-1,
|
|
366
461
|
)
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
462
|
+
|
|
463
|
+
if is_antiderivative_wrt_tau:
|
|
464
|
+
u0_pos = (
|
|
465
|
+
np.einsum(
|
|
466
|
+
"tij, tj -> it",
|
|
467
|
+
GC_pos[l, :, :],
|
|
468
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
|
|
469
|
+
)
|
|
470
|
+
+ direct_beam_contribution
|
|
471
|
+
+ _mathscr_v_contribution
|
|
472
|
+
)
|
|
473
|
+
else:
|
|
474
|
+
u0_pos = (
|
|
475
|
+
np.einsum("tij, tj -> it", GC_pos[l, :, :], np.exp(exponent))
|
|
476
|
+
+ direct_beam_contribution
|
|
477
|
+
+ _mathscr_v_contribution
|
|
478
|
+
)
|
|
372
479
|
|
|
373
480
|
flux = 2 * pi * (mu_arr_pos * W) @ u0_pos
|
|
374
481
|
return I0_orig * np.squeeze(flux)[()]
|
|
375
482
|
|
|
376
483
|
|
|
377
|
-
def flux_down(tau):
|
|
484
|
+
def flux_down(tau, is_antiderivative_wrt_tau=False):
|
|
485
|
+
"""
|
|
486
|
+
(Energetic) Flux function with argument `tau` (type: array or float) for negative (downward) `mu` values.
|
|
487
|
+
Returns a tuple of the diffuse and direct flux magnitudes respectively where each entry is of the
|
|
488
|
+
same type and size as `tau`.
|
|
489
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
490
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
491
|
+
"""
|
|
378
492
|
tau = np.atleast_1d(tau)
|
|
379
493
|
assert np.all(tau >= 0)
|
|
380
494
|
assert np.all(tau <= tau_arr[-1])
|
|
@@ -402,14 +516,20 @@ def _assemble_intensity_and_fluxes(
|
|
|
402
516
|
G_inv_collect_0,
|
|
403
517
|
mu_arr,
|
|
404
518
|
_autograd_compatible,
|
|
519
|
+
is_antiderivative_wrt_tau
|
|
405
520
|
)[N:, :]
|
|
406
521
|
else:
|
|
407
522
|
_mathscr_v_contribution = 0
|
|
408
523
|
|
|
409
524
|
if there_is_beam_source:
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
525
|
+
if is_antiderivative_wrt_tau:
|
|
526
|
+
direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau[None, :] / mu0) / (-scale_tau[None, l] / mu0)
|
|
527
|
+
direct_beam = I0 * mu0 * np.exp(-tau / mu0) * -mu0
|
|
528
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau[None, l] / mu0)
|
|
529
|
+
else:
|
|
530
|
+
direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau[None, :] / mu0)
|
|
531
|
+
direct_beam = I0 * mu0 * np.exp(-tau / mu0)
|
|
532
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0)
|
|
413
533
|
else:
|
|
414
534
|
direct_beam_contribution = 0
|
|
415
535
|
direct_beam = 0
|
|
@@ -422,11 +542,19 @@ def _assemble_intensity_and_fluxes(
|
|
|
422
542
|
],
|
|
423
543
|
axis=-1,
|
|
424
544
|
)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
545
|
+
|
|
546
|
+
if is_antiderivative_wrt_tau:
|
|
547
|
+
u0_neg = (
|
|
548
|
+
np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]))
|
|
549
|
+
+ direct_beam_contribution
|
|
550
|
+
+ _mathscr_v_contribution
|
|
551
|
+
)
|
|
552
|
+
else:
|
|
553
|
+
u0_neg = (
|
|
554
|
+
np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent))
|
|
555
|
+
+ direct_beam_contribution
|
|
556
|
+
+ _mathscr_v_contribution
|
|
557
|
+
)
|
|
430
558
|
|
|
431
559
|
diffuse_flux = 2 * pi * (mu_arr_pos * W) @ u0_neg + direct_beam_scaled - direct_beam
|
|
432
560
|
return (
|
|
@@ -29,9 +29,9 @@ def _solve_for_coeffs(
|
|
|
29
29
|
NLayers_to_use_sparse, # Number of layers above or equal which to use sparse matrices
|
|
30
30
|
):
|
|
31
31
|
"""
|
|
32
|
-
|
|
33
|
-
of
|
|
34
|
-
|
|
32
|
+
Uses the boundary conditions to solve for the unknown coefficients
|
|
33
|
+
of the general solution to the system of ordinary differential equations for each Fourier mode.
|
|
34
|
+
Returns the product of the coefficients and the eigenvectors.
|
|
35
35
|
This function is wrapped by the `_assemble_intensity_and_fluxes` function.
|
|
36
36
|
It has many seemingly redundant arguments to maximize precomputation in the `pydisort` function.
|
|
37
37
|
See the Jupyter Notebook, especially section 3, for documentation, explanation and derivation.
|