PythonicDISORT 0.9.1__py3-none-any.whl → 0.9.3__py3-none-any.whl
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/_assemble_intensity_and_fluxes.py +147 -134
- PythonicDISORT/_solve_for_coeffs.py +10 -11
- PythonicDISORT/_solve_for_gen_and_part_sols.py +24 -28
- PythonicDISORT/pydisort.py +100 -69
- PythonicDISORT/subroutines.py +99 -67
- {PythonicDISORT-0.9.1.dist-info → PythonicDISORT-0.9.3.dist-info}/METADATA +1 -1
- PythonicDISORT-0.9.3.dist-info/RECORD +11 -0
- {PythonicDISORT-0.9.1.dist-info → PythonicDISORT-0.9.3.dist-info}/WHEEL +1 -1
- PythonicDISORT-0.9.1.dist-info/RECORD +0 -11
- {PythonicDISORT-0.9.1.dist-info → PythonicDISORT-0.9.3.dist-info}/LICENSE.md +0 -0
- {PythonicDISORT-0.9.1.dist-info → PythonicDISORT-0.9.3.dist-info}/top_level.txt +0 -0
|
@@ -26,7 +26,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
26
26
|
scale_tau, # Delta-scale factor for tau
|
|
27
27
|
only_flux, # Only compute fluxes?
|
|
28
28
|
use_banded_solver_NLayers, # Number of layers above or equal which to use `scipy.linalg.solve_banded`
|
|
29
|
-
|
|
29
|
+
autograd_compatible, # Should the output functions be compatible with autograd?
|
|
30
30
|
):
|
|
31
31
|
"""Assembles the solution functions: intensity `u`, upward flux `flux_up`, downward flux `flux_down`
|
|
32
32
|
from the previously solved eigenpairs and coefficients from the boundary conditions.
|
|
@@ -73,7 +73,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
73
73
|
| `scale_tau` | `NLayers` |
|
|
74
74
|
| `only_flux` | boolean |
|
|
75
75
|
| `use_banded_solver_NLayers` | scalar |
|
|
76
|
-
| `
|
|
76
|
+
| `autograd_compatible` | boolean |
|
|
77
77
|
|
|
78
78
|
Notable internal variables of _assemble_intensity_and_fluxes
|
|
79
79
|
| Variable | Type / Shape |
|
|
@@ -92,7 +92,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
92
92
|
| `GC_pos` | `NLayers x NQuad/2 x NQuad` |
|
|
93
93
|
| `GC_neg` | `NLayers x NQuad/2 x NQuad` |
|
|
94
94
|
"""
|
|
95
|
-
if
|
|
95
|
+
if autograd_compatible:
|
|
96
96
|
import autograd.numpy as np
|
|
97
97
|
else:
|
|
98
98
|
import numpy as np
|
|
@@ -114,20 +114,21 @@ def _assemble_intensity_and_fluxes(
|
|
|
114
114
|
Nscoeffs,
|
|
115
115
|
there_is_iso_source,
|
|
116
116
|
)
|
|
117
|
-
if there_is_beam_source:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
G_inv_collect_0 = None
|
|
117
|
+
if there_is_beam_source and there_is_iso_source:
|
|
118
|
+
G_collect, K_collect, B_collect, G_inv_collect_0 = outputs
|
|
119
|
+
B_collect_0 = B_collect[0, :, :]
|
|
120
|
+
elif there_is_beam_source and not there_is_iso_source:
|
|
121
|
+
G_collect, K_collect, B_collect = outputs
|
|
123
122
|
B_collect_0 = B_collect[0, :, :]
|
|
123
|
+
G_inv_collect_0 = None
|
|
124
|
+
elif not there_is_beam_source and there_is_iso_source:
|
|
125
|
+
G_collect, K_collect, G_inv_collect_0 = outputs
|
|
126
|
+
B_collect = None
|
|
124
127
|
else:
|
|
125
|
-
|
|
126
|
-
G_collect, K_collect, G_inv_collect_0 = outputs
|
|
127
|
-
else:
|
|
128
|
-
G_collect, K_collect = outputs
|
|
129
|
-
G_inv_collect_0 = None
|
|
128
|
+
G_collect, K_collect = outputs
|
|
130
129
|
B_collect = None
|
|
130
|
+
G_inv_collect_0 = None
|
|
131
|
+
|
|
131
132
|
|
|
132
133
|
GC_collect = _solve_for_coeffs(
|
|
133
134
|
NFourier,
|
|
@@ -161,7 +162,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
161
162
|
|
|
162
163
|
# Construct the intensity function (refer to Section 3.7 of the Comprehensive Documentation)
|
|
163
164
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
164
|
-
def u(tau, phi, return_Fourier_error=False,
|
|
165
|
+
def u(tau, phi, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False):
|
|
165
166
|
"""
|
|
166
167
|
Intensity function with arguments `(tau, phi)` of types `(array or float, array or float)`.
|
|
167
168
|
Returns an ndarray with axes corresponding to variation with `mu, tau, phi` respectively.
|
|
@@ -172,8 +173,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
172
173
|
"""
|
|
173
174
|
tau = np.atleast_1d(tau)
|
|
174
175
|
phi = np.atleast_1d(phi)
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
if np.all(tau < 0) or np.all(tau > tau_arr[-1]):
|
|
177
|
+
raise ValueError("tau input outside the tau range specified for the atmosphere (check `tau_arr`).")
|
|
177
178
|
|
|
178
179
|
# Atmospheric layer indices
|
|
179
180
|
l = np.argmax(tau[:, None] <= tau_arr[None, :], axis=1)
|
|
@@ -196,34 +197,32 @@ def _assemble_intensity_and_fluxes(
|
|
|
196
197
|
axis=-1,
|
|
197
198
|
)
|
|
198
199
|
|
|
199
|
-
if is_antiderivative_wrt_tau:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
np.einsum(
|
|
203
|
-
"mtij, mtj -> mti",
|
|
204
|
-
GC_collect[:, l, :, :],
|
|
205
|
-
np.exp(exponent) / (scale_tau[None, :, None] * K_collect)[:, l, :],
|
|
206
|
-
optimize=True,
|
|
207
|
-
)
|
|
208
|
-
+ (B_collect / (-scale_tau / mu0)[None, :, None])[:, l, :]
|
|
209
|
-
* np.exp(-scaled_tau / mu0)[None, :, None]
|
|
210
|
-
)
|
|
211
|
-
else:
|
|
212
|
-
um = np.einsum(
|
|
200
|
+
if is_antiderivative_wrt_tau and there_is_beam_source:
|
|
201
|
+
um = (
|
|
202
|
+
np.einsum(
|
|
213
203
|
"mtij, mtj -> mti",
|
|
214
204
|
GC_collect[:, l, :, :],
|
|
215
|
-
np.exp(exponent) / (scale_tau[None,
|
|
205
|
+
np.exp(exponent) / (scale_tau[None, :, None] * K_collect)[:, l, :],
|
|
216
206
|
optimize=True,
|
|
217
207
|
)
|
|
208
|
+
+ (B_collect / (-scale_tau / mu0)[None, :, None])[:, l, :]
|
|
209
|
+
* np.exp(-scaled_tau / mu0)[None, :, None]
|
|
210
|
+
)
|
|
211
|
+
elif is_antiderivative_wrt_tau and not there_is_beam_source:
|
|
212
|
+
um = np.einsum(
|
|
213
|
+
"mtij, mtj -> mti",
|
|
214
|
+
GC_collect[:, l, :, :],
|
|
215
|
+
np.exp(exponent) / (scale_tau[None, l, None] * K_collect)[:, l, :],
|
|
216
|
+
optimize=True,
|
|
217
|
+
)
|
|
218
|
+
elif not is_antiderivative_wrt_tau and there_is_beam_source:
|
|
219
|
+
um = np.einsum(
|
|
220
|
+
"mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
|
|
221
|
+
) + B_collect[:, l, :] * np.exp(-scaled_tau / mu0)[None, :, None]
|
|
218
222
|
else:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
) + B_collect[:, l, :] * np.exp(-scaled_tau / mu0)[None, :, None]
|
|
223
|
-
else:
|
|
224
|
-
um = np.einsum(
|
|
225
|
-
"mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
|
|
226
|
-
)
|
|
223
|
+
um = np.einsum(
|
|
224
|
+
"mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
|
|
225
|
+
)
|
|
227
226
|
|
|
228
227
|
# Contribution from particular solution for isotropic internal sources
|
|
229
228
|
if there_is_iso_source:
|
|
@@ -236,18 +235,17 @@ def _assemble_intensity_and_fluxes(
|
|
|
236
235
|
G_inv_collect_0[l_uniq],
|
|
237
236
|
mu_arr,
|
|
238
237
|
is_antiderivative_wrt_tau,
|
|
239
|
-
|
|
238
|
+
autograd_compatible
|
|
240
239
|
)
|
|
241
240
|
|
|
242
|
-
if
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
um = um + np.concatenate((_mathscr_v_contribution.T, np.zeros((NFourier - 1, len(tau), NQuad))))
|
|
241
|
+
if NFourier == 1:
|
|
242
|
+
um = um + _mathscr_v_contribution.T
|
|
243
|
+
elif autograd_compatible:
|
|
244
|
+
um = um + np.concatenate((_mathscr_v_contribution.T, np.zeros((NFourier - 1, len(tau), NQuad))))
|
|
247
245
|
else:
|
|
248
246
|
um[0, :, :] += _mathscr_v_contribution.T
|
|
249
247
|
|
|
250
|
-
|
|
248
|
+
u = np.einsum(
|
|
251
249
|
"mti, mp -> itp",
|
|
252
250
|
um,
|
|
253
251
|
np.cos(np.arange(NFourier)[:, None] * (phi0 - phi)[None, :]),
|
|
@@ -262,54 +260,59 @@ def _assemble_intensity_and_fluxes(
|
|
|
262
260
|
],
|
|
263
261
|
axis=-1,
|
|
264
262
|
)
|
|
265
|
-
if is_antiderivative_wrt_tau:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
np.einsum(
|
|
269
|
-
"tij, tj -> it",
|
|
270
|
-
GC_collect[-1, l, :, :],
|
|
271
|
-
np.exp(exponent) / (scale_tau[:, None] * K_collect[-1, :, :])[l, :],
|
|
272
|
-
optimize=True,
|
|
273
|
-
)
|
|
274
|
-
+ (B_collect.T[-1, :, :] / (-scale_tau / mu0)[None, :])[:, l]
|
|
275
|
-
* np.exp(-scaled_tau / mu0)[None, :]
|
|
276
|
-
)
|
|
277
|
-
else:
|
|
278
|
-
ulast = np.einsum(
|
|
263
|
+
if is_antiderivative_wrt_tau and there_is_beam_source:
|
|
264
|
+
ulast = (
|
|
265
|
+
np.einsum(
|
|
279
266
|
"tij, tj -> it",
|
|
280
267
|
GC_collect[-1, l, :, :],
|
|
281
268
|
np.exp(exponent) / (scale_tau[:, None] * K_collect[-1, :, :])[l, :],
|
|
282
269
|
optimize=True,
|
|
283
270
|
)
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
271
|
+
+ (B_collect.T[-1, :, :] / (-scale_tau / mu0)[None, :])[:, l]
|
|
272
|
+
* np.exp(-scaled_tau / mu0)[None, :]
|
|
273
|
+
)
|
|
274
|
+
elif is_antiderivative_wrt_tau and not there_is_beam_source:
|
|
275
|
+
ulast = np.einsum(
|
|
276
|
+
"tij, tj -> it",
|
|
277
|
+
GC_collect[-1, l, :, :],
|
|
278
|
+
np.exp(exponent) / (scale_tau[:, None] * K_collect[-1, :, :])[l, :],
|
|
279
|
+
optimize=True,
|
|
292
280
|
)
|
|
281
|
+
elif not is_antiderivative_wrt_tau and not there_is_beam_source:
|
|
282
|
+
ulast = np.einsum(
|
|
283
|
+
"tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
|
|
284
|
+
) + B_collect.T[-1, :, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
285
|
+
else:
|
|
286
|
+
ulast = np.einsum(
|
|
287
|
+
"tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
|
|
288
|
+
)
|
|
293
289
|
|
|
294
|
-
|
|
290
|
+
u_abs = np.abs(u)
|
|
295
291
|
Fourier_error = np.max(
|
|
296
292
|
np.divide(
|
|
297
293
|
np.abs(
|
|
298
294
|
ulast[:, :, None] * np.cos((NFourier - 1) * (phi0 - phi))[None, None, :]
|
|
299
295
|
),
|
|
300
|
-
|
|
301
|
-
out=np.zeros_like(
|
|
302
|
-
where=
|
|
296
|
+
u_abs,
|
|
297
|
+
out=np.zeros_like(u_abs),
|
|
298
|
+
where=u_abs > 1e-15,
|
|
303
299
|
)
|
|
304
300
|
)
|
|
305
|
-
|
|
301
|
+
|
|
302
|
+
if return_tau_arr:
|
|
303
|
+
return I0_orig * np.squeeze(u), Fourier_error, tau_arr
|
|
304
|
+
else:
|
|
305
|
+
return I0_orig * np.squeeze(u), Fourier_error
|
|
306
306
|
else:
|
|
307
|
-
|
|
307
|
+
if return_tau_arr:
|
|
308
|
+
return I0_orig * np.squeeze(u), tau_arr
|
|
309
|
+
else:
|
|
310
|
+
return I0_orig * np.squeeze(u)
|
|
308
311
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
309
312
|
|
|
310
313
|
# Construct u0
|
|
311
314
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
312
|
-
def u0(tau, is_antiderivative_wrt_tau=False, _return_act_dscale_for_reclass=False):
|
|
315
|
+
def u0(tau, is_antiderivative_wrt_tau=False, return_tau_arr=False, _return_act_dscale_for_reclass=False):
|
|
313
316
|
"""
|
|
314
317
|
Zeroth Fourier mode of the intensity with argument `tau` (type: array or float).
|
|
315
318
|
Returns an ndarray with axes corresponding to variation with `mu` and `tau` respectively.
|
|
@@ -320,8 +323,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
320
323
|
to switch to an antiderivative of the function with respect to `tau`.
|
|
321
324
|
"""
|
|
322
325
|
tau = np.atleast_1d(tau)
|
|
323
|
-
|
|
324
|
-
|
|
326
|
+
if np.all(tau < 0) or np.all(tau > tau_arr[-1]):
|
|
327
|
+
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
325
328
|
|
|
326
329
|
# Atmospheric layer indices
|
|
327
330
|
l = np.argmax(tau[:, None] <= tau_arr[None, :], axis=1)
|
|
@@ -356,34 +359,32 @@ def _assemble_intensity_and_fluxes(
|
|
|
356
359
|
axis=-1,
|
|
357
360
|
)
|
|
358
361
|
|
|
359
|
-
if is_antiderivative_wrt_tau:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
np.einsum(
|
|
363
|
-
"tij, tj -> it",
|
|
364
|
-
GC_collect_0[l, :, :],
|
|
365
|
-
np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :],
|
|
366
|
-
optimize=True,
|
|
367
|
-
)
|
|
368
|
-
+ (B_collect_0.T / (-scale_tau / mu0)[None, :])[:, l]
|
|
369
|
-
* np.exp(-scaled_tau / mu0)[None, :]
|
|
370
|
-
)
|
|
371
|
-
else:
|
|
372
|
-
u0 = np.einsum(
|
|
362
|
+
if is_antiderivative_wrt_tau and there_is_beam_source:
|
|
363
|
+
u0 = (
|
|
364
|
+
np.einsum(
|
|
373
365
|
"tij, tj -> it",
|
|
374
366
|
GC_collect_0[l, :, :],
|
|
375
367
|
np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :],
|
|
376
368
|
optimize=True,
|
|
377
369
|
)
|
|
370
|
+
+ (B_collect_0.T / (-scale_tau / mu0)[None, :])[:, l]
|
|
371
|
+
* np.exp(-scaled_tau / mu0)[None, :]
|
|
372
|
+
)
|
|
373
|
+
if is_antiderivative_wrt_tau and not there_is_beam_source:
|
|
374
|
+
u0 = np.einsum(
|
|
375
|
+
"tij, tj -> it",
|
|
376
|
+
GC_collect_0[l, :, :],
|
|
377
|
+
np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :],
|
|
378
|
+
optimize=True,
|
|
379
|
+
)
|
|
380
|
+
if not is_antiderivative_wrt_tau and there_is_beam_source:
|
|
381
|
+
u0 = np.einsum(
|
|
382
|
+
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
383
|
+
) + B_collect_0.T[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
378
384
|
else:
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
) + B_collect_0.T[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
383
|
-
else:
|
|
384
|
-
u0 = np.einsum(
|
|
385
|
-
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
386
|
-
)
|
|
385
|
+
u0 = np.einsum(
|
|
386
|
+
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
387
|
+
)
|
|
387
388
|
|
|
388
389
|
# Contribution from particular solution for isotropic internal sources
|
|
389
390
|
if there_is_iso_source:
|
|
@@ -396,11 +397,15 @@ def _assemble_intensity_and_fluxes(
|
|
|
396
397
|
G_inv_collect_0[l_uniq],
|
|
397
398
|
mu_arr,
|
|
398
399
|
is_antiderivative_wrt_tau,
|
|
399
|
-
|
|
400
|
+
autograd_compatible
|
|
400
401
|
)
|
|
401
402
|
u0 = u0 + _mathscr_v_contribution
|
|
402
|
-
|
|
403
|
-
if _return_act_dscale_for_reclass:
|
|
403
|
+
|
|
404
|
+
if return_tau_arr and _return_act_dscale_for_reclass:
|
|
405
|
+
return I0_orig * np.squeeze(u0), tau_arr, act_dscale_reclassification
|
|
406
|
+
elif return_tau_arr and not _return_act_dscale_for_reclass:
|
|
407
|
+
return I0_orig * np.squeeze(u0), tau_arr
|
|
408
|
+
elif not return_tau_arr and _return_act_dscale_for_reclass:
|
|
404
409
|
return I0_orig * np.squeeze(u0), act_dscale_reclassification
|
|
405
410
|
else:
|
|
406
411
|
return I0_orig * np.squeeze(u0)
|
|
@@ -415,7 +420,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
415
420
|
B_neg = B_collect_0[:, N:].T
|
|
416
421
|
|
|
417
422
|
|
|
418
|
-
def flux_up(tau, is_antiderivative_wrt_tau=False):
|
|
423
|
+
def flux_up(tau, is_antiderivative_wrt_tau=False, return_tau_arr=False):
|
|
419
424
|
"""
|
|
420
425
|
(Energetic) Flux function with argument `tau` (type: array or float) for positive (upward) `mu` values.
|
|
421
426
|
Returns the diffuse flux magnitudes (same type and size as `tau`).
|
|
@@ -423,8 +428,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
423
428
|
to switch to an antiderivative of the function with respect to `tau`.
|
|
424
429
|
"""
|
|
425
430
|
tau = np.atleast_1d(tau)
|
|
426
|
-
|
|
427
|
-
|
|
431
|
+
if np.all(tau < 0) or np.all(tau > tau_arr[-1]):
|
|
432
|
+
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
428
433
|
|
|
429
434
|
# Atmospheric layer indices
|
|
430
435
|
l = np.argmax(tau[:, None] <= tau_arr[None, :], axis=1)
|
|
@@ -449,18 +454,17 @@ def _assemble_intensity_and_fluxes(
|
|
|
449
454
|
G_inv_collect_0[l_uniq],
|
|
450
455
|
mu_arr,
|
|
451
456
|
is_antiderivative_wrt_tau,
|
|
452
|
-
|
|
457
|
+
autograd_compatible
|
|
453
458
|
)
|
|
454
459
|
else:
|
|
455
460
|
_mathscr_v_contribution = 0
|
|
456
461
|
|
|
457
|
-
if there_is_beam_source:
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
462
|
+
if there_is_beam_source and is_antiderivative_wrt_tau:
|
|
463
|
+
direct_beam_contribution = (
|
|
464
|
+
(B_pos / (-scale_tau / mu0)[None, :])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
465
|
+
)
|
|
466
|
+
elif there_is_beam_source and not is_antiderivative_wrt_tau:
|
|
467
|
+
direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
464
468
|
else:
|
|
465
469
|
direct_beam_contribution = 0
|
|
466
470
|
|
|
@@ -488,12 +492,15 @@ def _assemble_intensity_and_fluxes(
|
|
|
488
492
|
+ direct_beam_contribution
|
|
489
493
|
+ _mathscr_v_contribution
|
|
490
494
|
)
|
|
491
|
-
|
|
492
495
|
flux = 2 * pi * (mu_arr_pos * W) @ u0_pos
|
|
493
|
-
|
|
496
|
+
|
|
497
|
+
if return_tau_arr:
|
|
498
|
+
return I0_orig * np.squeeze(flux)[()], tau_arr
|
|
499
|
+
else:
|
|
500
|
+
return I0_orig * np.squeeze(flux)[()]
|
|
494
501
|
|
|
495
502
|
|
|
496
|
-
def flux_down(tau, is_antiderivative_wrt_tau=False):
|
|
503
|
+
def flux_down(tau, is_antiderivative_wrt_tau=False, return_tau_arr=False):
|
|
497
504
|
"""
|
|
498
505
|
(Energetic) Flux function with argument `tau` (type: array or float) for negative (downward) `mu` values.
|
|
499
506
|
Returns a tuple of the diffuse and direct flux magnitudes respectively where each entry is of the
|
|
@@ -502,8 +509,8 @@ def _assemble_intensity_and_fluxes(
|
|
|
502
509
|
to switch to an antiderivative of the function with respect to `tau`.
|
|
503
510
|
"""
|
|
504
511
|
tau = np.atleast_1d(tau)
|
|
505
|
-
|
|
506
|
-
|
|
512
|
+
if np.all(tau < 0) or np.all(tau > tau_arr[-1]):
|
|
513
|
+
raise ValueError("tau input outside the tau range given for the atmosphere (check `tau_arr`).")
|
|
507
514
|
|
|
508
515
|
# Atmospheric layer indices
|
|
509
516
|
l = np.argmax(tau[:, None] <= tau_arr[None, :], axis=1)
|
|
@@ -528,20 +535,19 @@ def _assemble_intensity_and_fluxes(
|
|
|
528
535
|
G_inv_collect_0[l_uniq],
|
|
529
536
|
mu_arr,
|
|
530
537
|
is_antiderivative_wrt_tau,
|
|
531
|
-
|
|
538
|
+
autograd_compatible
|
|
532
539
|
)
|
|
533
540
|
else:
|
|
534
541
|
_mathscr_v_contribution = 0
|
|
535
542
|
|
|
536
|
-
if there_is_beam_source:
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0)
|
|
543
|
+
if there_is_beam_source and is_antiderivative_wrt_tau:
|
|
544
|
+
direct_beam_contribution = (B_neg / (-scale_tau / mu0)[None, l])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
545
|
+
direct_beam = I0 * mu0 * np.exp(-tau / mu0) * -mu0
|
|
546
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau / mu0)[None, l]
|
|
547
|
+
elif there_is_beam_source and not is_antiderivative_wrt_tau:
|
|
548
|
+
direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
549
|
+
direct_beam = I0 * mu0 * np.exp(-tau / mu0)
|
|
550
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0)
|
|
545
551
|
else:
|
|
546
552
|
direct_beam_contribution = 0
|
|
547
553
|
direct_beam = 0
|
|
@@ -567,12 +573,19 @@ def _assemble_intensity_and_fluxes(
|
|
|
567
573
|
+ direct_beam_contribution
|
|
568
574
|
+ _mathscr_v_contribution
|
|
569
575
|
)
|
|
570
|
-
|
|
571
576
|
diffuse_flux = 2 * pi * (mu_arr_pos * W) @ u0_neg + direct_beam_scaled - direct_beam
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
577
|
+
|
|
578
|
+
if return_tau_arr:
|
|
579
|
+
return (
|
|
580
|
+
I0_orig * np.squeeze(diffuse_flux)[()],
|
|
581
|
+
I0_orig * I0 * np.squeeze(direct_beam)[()],
|
|
582
|
+
tau_arr
|
|
583
|
+
)
|
|
584
|
+
else:
|
|
585
|
+
return (
|
|
586
|
+
I0_orig * np.squeeze(diffuse_flux)[()],
|
|
587
|
+
I0_orig * I0 * np.squeeze(direct_beam)[()],
|
|
588
|
+
)
|
|
576
589
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
577
590
|
|
|
578
591
|
if only_flux:
|
|
@@ -109,18 +109,17 @@ def _solve_for_coeffs(
|
|
|
109
109
|
# Assemble RHS
|
|
110
110
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
111
111
|
# Ensure the BCs are of the correct shape
|
|
112
|
-
if b_pos_is_scalar:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
b_pos_m = np.zeros(N)
|
|
112
|
+
if b_pos_is_scalar and m_equals_0:
|
|
113
|
+
b_pos_m = np.full(N, b_pos)
|
|
114
|
+
elif b_pos_is_scalar and not m_equals_0:
|
|
115
|
+
b_pos_m = np.zeros(N)
|
|
117
116
|
else:
|
|
118
117
|
b_pos_m = b_pos[:, m]
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
|
|
119
|
+
if b_neg_is_scalar and m_equals_0:
|
|
120
|
+
b_neg_m = np.full(N, b_neg)
|
|
121
|
+
elif b_neg_is_scalar and not m_equals_0:
|
|
122
|
+
b_neg_m = np.zeros(N)
|
|
124
123
|
else:
|
|
125
124
|
b_neg_m = b_neg[:, m]
|
|
126
125
|
|
|
@@ -205,7 +204,7 @@ def _solve_for_coeffs(
|
|
|
205
204
|
l_range = np.arange(1, NLayers)
|
|
206
205
|
RHS_middle = (
|
|
207
206
|
(B_collect_m[l_range, :] - B_collect_m[l_range - 1, :])
|
|
208
|
-
* np.exp(-
|
|
207
|
+
* np.exp(-scaled_tau_arr_with_0 / mu0)[l_range, None]
|
|
209
208
|
).ravel()
|
|
210
209
|
|
|
211
210
|
if BDRF_bool:
|
|
@@ -103,9 +103,7 @@ def _solve_for_gen_and_part_sols(
|
|
|
103
103
|
for l in range(NLayers):
|
|
104
104
|
weighted_asso_Leg_coeffs_l = weighted_scaled_Leg_coeffs[l, :][ells] * fac
|
|
105
105
|
|
|
106
|
-
if np.any(weighted_asso_Leg_coeffs_l > 0)
|
|
107
|
-
np.isfinite(asso_leg_term_pos) # We take precautions against overflow and underflow (this shouldn't happen though)
|
|
108
|
-
):
|
|
106
|
+
if np.any(weighted_asso_Leg_coeffs_l > 0): # There are shortcuts if the coefficients are all zero
|
|
109
107
|
scaled_omega_l = scaled_omega_arr[l]
|
|
110
108
|
|
|
111
109
|
# Generate D
|
|
@@ -213,29 +211,27 @@ def _solve_for_gen_and_part_sols(
|
|
|
213
211
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
214
212
|
|
|
215
213
|
|
|
216
|
-
if there_is_beam_source:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
214
|
+
if there_is_beam_source and there_is_iso_source:
|
|
215
|
+
return (
|
|
216
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
217
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
218
|
+
B_collect.reshape((NFourier, NLayers, NQuad)),
|
|
219
|
+
G_inv_collect_0,
|
|
220
|
+
)
|
|
221
|
+
elif there_is_beam_source and not there_is_iso_source:
|
|
222
|
+
return (
|
|
223
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
224
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
225
|
+
B_collect.reshape((NFourier, NLayers, NQuad)),
|
|
226
|
+
)
|
|
227
|
+
elif not there_is_beam_source and there_is_iso_source:
|
|
228
|
+
return (
|
|
229
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
230
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
231
|
+
G_inv_collect_0,
|
|
232
|
+
)
|
|
230
233
|
else:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
G_inv_collect_0,
|
|
236
|
-
)
|
|
237
|
-
else:
|
|
238
|
-
return (
|
|
239
|
-
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
240
|
-
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
241
|
-
)
|
|
234
|
+
return (
|
|
235
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
236
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
237
|
+
)
|
PythonicDISORT/pydisort.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from PythonicDISORT import subroutines
|
|
2
2
|
from PythonicDISORT._assemble_intensity_and_fluxes import _assemble_intensity_and_fluxes
|
|
3
|
+
import warnings
|
|
3
4
|
|
|
4
5
|
import numpy as np
|
|
5
6
|
import scipy as sc
|
|
@@ -23,12 +24,12 @@ def pydisort(
|
|
|
23
24
|
BDRF_Fourier_modes=[],
|
|
24
25
|
s_poly_coeffs=np.array([[]]),
|
|
25
26
|
use_banded_solver_NLayers=13,
|
|
26
|
-
|
|
27
|
+
autograd_compatible = False,
|
|
27
28
|
):
|
|
28
29
|
"""Solves the 1D RTE for the fluxes, and optionally intensity,
|
|
29
30
|
of a multi-layer atmosphere with the specified optical properties, boundary conditions
|
|
30
31
|
and sources. Optionally performs delta-M scaling and NT corrections.
|
|
31
|
-
Refer to the
|
|
32
|
+
Refer to the ``*_test.ipynb`` Jupyter Notebooks in the ``pydisotest`` directory for examples of use.
|
|
32
33
|
|
|
33
34
|
See https://pythonic-disort.readthedocs.io/en/latest/Pythonic-DISORT.html#1.-USER-INPUT-REQUIRED:-Choose-parameters
|
|
34
35
|
for a more detailed explanation of each parameter.
|
|
@@ -43,7 +44,7 @@ def pydisort(
|
|
|
43
44
|
omega_arr : array or float
|
|
44
45
|
Single-scattering albedo of each atmospheric layer.
|
|
45
46
|
NQuad : int
|
|
46
|
-
Number of
|
|
47
|
+
Number of ``mu`` quadrature nodes.
|
|
47
48
|
Leg_coeffs_all : 2darray
|
|
48
49
|
All available unweighted phase function Legendre coefficients.
|
|
49
50
|
Each row of coefficients pertain to an atmospheric layer.
|
|
@@ -70,47 +71,55 @@ def pydisort(
|
|
|
70
71
|
NT_cor : optional, bool
|
|
71
72
|
Perform Nakajima-Tanaka intensity corrections?
|
|
72
73
|
BDRF_Fourier_modes : optional, list
|
|
73
|
-
BDRF Fourier modes, each a function with arguments
|
|
74
|
+
BDRF Fourier modes, each a function with arguments ``mu, -mu_p`` of type array
|
|
74
75
|
and which output has the same dimensions as the outer product of the two arrays.
|
|
75
76
|
s_poly_coeffs : optional, array
|
|
76
77
|
Polynomial coefficients of isotropic internal sources.
|
|
77
78
|
Arrange coefficients from lowest order term to highest.
|
|
78
79
|
use_banded_solver_NLayers : optional, int
|
|
79
|
-
At or above how many atmospheric layers should
|
|
80
|
+
At or above how many atmospheric layers should ``scipy.linalg.solve_banded`` be used?
|
|
81
|
+
autograd_compatible : optional, bool
|
|
82
|
+
If ``True``, the autograd package: https://github.com/HIPS/autograd can be used to compute
|
|
83
|
+
the tau-derivatives of the function outputs but ``pydisort`` will be less efficient.
|
|
80
84
|
|
|
81
85
|
Returns
|
|
82
86
|
-------
|
|
83
87
|
mu_arr : array or float
|
|
84
|
-
All
|
|
88
|
+
All ``mu`` (cosine of polar angle) quadrature nodes.
|
|
85
89
|
Fp(tau) : function
|
|
86
|
-
(Energetic) Flux function with argument
|
|
87
|
-
Returns the diffuse flux magnitudes (same type and size as
|
|
88
|
-
Pass
|
|
89
|
-
to switch to an antiderivative of the function with respect to
|
|
90
|
-
For example,
|
|
91
|
-
will
|
|
90
|
+
(Energetic) Flux function with argument ``tau`` (type: array or float) for positive (upward) ``mu`` values.
|
|
91
|
+
Returns the diffuse flux magnitudes (same type and size as ``tau``).
|
|
92
|
+
Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
|
|
93
|
+
to switch to an antiderivative of the function with respect to ``tau``.
|
|
94
|
+
For example, ``Fp(1, is_antiderivative_wrt_tau=True) - Fp(0, is_antiderivative_wrt_tau=True)``
|
|
95
|
+
will produce the tau-integral over ``[1, 0]`` assuming both values belong to the same layer, and
|
|
96
|
+
``Fp(tau_arr, is_antiderivative_wrt_tau = True) - Fp(np.insert(tau_arr[:-1] + 1e-15, 0, 0), is_antiderivative_wrt_tau = True)``
|
|
97
|
+
will produce an array of the tau-integral over each layer.
|
|
98
|
+
Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
|
|
92
99
|
Fm(tau) : function
|
|
93
|
-
(Energetic) Flux function with argument
|
|
100
|
+
(Energetic) Flux function with argument ``tau`` (type: array or float) for negative (downward) ``mu`` values.
|
|
94
101
|
Returns a tuple of the diffuse and direct flux magnitudes respectively where each entry is of the
|
|
95
|
-
same type and size as
|
|
96
|
-
Pass
|
|
97
|
-
to switch to an antiderivative of the function with respect to
|
|
102
|
+
same type and size as ``tau``.
|
|
103
|
+
Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
|
|
104
|
+
to switch to an antiderivative of the function with respect to ``tau``.
|
|
105
|
+
Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
|
|
98
106
|
u0(tau) : function
|
|
99
|
-
Zeroth Fourier mode of the intensity with argument
|
|
100
|
-
Returns an ndarray with axes corresponding to variation with
|
|
107
|
+
Zeroth Fourier mode of the intensity with argument ``tau`` (type: array or float).
|
|
108
|
+
Returns an ndarray with axes corresponding to variation with ``mu`` and ``tau`` respectively.
|
|
101
109
|
This function is useful for calculating actinic fluxes and other quantities of interest,
|
|
102
110
|
but reclassification of delta-scaled flux and other corrections must be done manually
|
|
103
|
-
(for actinic flux
|
|
104
|
-
Pass
|
|
105
|
-
to switch to an antiderivative of the function with respect to
|
|
111
|
+
(for actinic flux ``subroutines.generate_diff_act_flux_funcs`` will automatically perform the reclassification).
|
|
112
|
+
Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
|
|
113
|
+
to switch to an antiderivative of the function with respect to ``tau``.
|
|
114
|
+
Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
|
|
106
115
|
u(tau, phi) : function, optional
|
|
107
|
-
Intensity function with arguments
|
|
108
|
-
Returns an ndarray with axes corresponding to variation with
|
|
109
|
-
Pass
|
|
116
|
+
Intensity function with arguments ``(tau, phi)`` each of type array or float.
|
|
117
|
+
Returns an ndarray with axes corresponding to variation with ``mu, tau, phi`` respectively.
|
|
118
|
+
Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
|
|
119
|
+
to switch to an antiderivative of the function with respect to ``tau``.
|
|
120
|
+
Pass ``return_Fourier_error = True`` (defaults to ``False``) to return the
|
|
110
121
|
Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
|
|
111
|
-
Pass
|
|
112
|
-
to switch to an antiderivative of the function with respect to `tau`.
|
|
113
|
-
|
|
122
|
+
Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
|
|
114
123
|
"""
|
|
115
124
|
|
|
116
125
|
"""
|
|
@@ -159,7 +168,7 @@ def pydisort(
|
|
|
159
168
|
| `scaled_mu0` | scalar |
|
|
160
169
|
"""
|
|
161
170
|
|
|
162
|
-
if
|
|
171
|
+
if autograd_compatible:
|
|
163
172
|
import autograd.numpy as np
|
|
164
173
|
else:
|
|
165
174
|
import numpy as np
|
|
@@ -201,50 +210,71 @@ def pydisort(
|
|
|
201
210
|
# Input checks (refer to Section 1 of the Comprehensive Documentation)
|
|
202
211
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
203
212
|
# Optical depths and thickness must be positive
|
|
204
|
-
|
|
205
|
-
|
|
213
|
+
if not np.all(tau_arr > 0):
|
|
214
|
+
raise ValueError("tau values cannot be negative.")
|
|
215
|
+
if not np.all(thickness_arr > 0):
|
|
216
|
+
raise ValueError("Layer thickness cannot be negative.")
|
|
206
217
|
# Single-scattering albedo must be between 0 and 1, excluding 1
|
|
207
|
-
|
|
208
|
-
|
|
218
|
+
if not (np.all(omega_arr >= 0) and np.all(omega_arr < 1)):
|
|
219
|
+
raise ValueError("Single-scattering albedo must be between 0 and 1, excluding 1.")
|
|
209
220
|
# There must be a positive number of Legendre coefficients each with magnitude <= 1
|
|
210
221
|
# The user must supply at least as many phase function Legendre coefficients as intended for use
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
222
|
+
if not NLeg > 0:
|
|
223
|
+
raise ValueError("The number of phase function Legendre coefficients must be positive.")
|
|
224
|
+
if not np.all(Leg_coeffs_all[:, 0] == 1):
|
|
225
|
+
raise ValueError("The first phase function Legendre coefficient must equal 1.")
|
|
226
|
+
if not (np.all(0 <= Leg_coeffs_all) and np.all(Leg_coeffs_all <= 1)):
|
|
227
|
+
raise ValueError("The phase function Legendre coefficients must all be between 0 and 1.")
|
|
228
|
+
if not np.all(np.array([f.__code__.co_argcount for f in BDRF_Fourier_modes]) == 2):
|
|
229
|
+
raise ValueError("Some functions in the list `BDRF_Fourier_modes` are of the wrong form.")
|
|
230
|
+
if not NLeg <= NLeg_all:
|
|
231
|
+
raise ValueError("`NLeg` cannot be larger than the number of phase function Legendre coefficients provided.")
|
|
216
232
|
# Ensure that the first dimension of the following inputs corresponds to the number of layers
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if len(
|
|
220
|
-
|
|
221
|
-
if
|
|
222
|
-
|
|
233
|
+
if not np.shape(Leg_coeffs_all)[0] == NLayers:
|
|
234
|
+
raise ValueError("The zeroth dimension of the shape of `Leg_coeffs_all` does not match the number of layers as deduced from the length of `tau_arr`.")
|
|
235
|
+
if not len(omega_arr) == NLayers:
|
|
236
|
+
raise ValueError("The zeroth dimension of the shape of `omega_arr` does not match the number of layers as deduced from the length of `tau_arr`.")
|
|
237
|
+
if np.any(f_arr != 0) and not len(f_arr) == NLayers:
|
|
238
|
+
raise ValueError("The length of `f_arr` does not match the number of layers as deduced from the length of `tau_arr`.")
|
|
239
|
+
if there_is_iso_source and not np.shape(s_poly_coeffs)[0] == NLayers:
|
|
240
|
+
raise ValueError("The zeroth dimension of the shape of `s_poly_coeffs` does not match the number of layers as deduced from the length of `tau_arr`.")
|
|
223
241
|
# Conditions on the number of quadrature angles (NQuad), Legendre coefficients (NLeg) and loops (NFourier)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
242
|
+
if not NQuad >= 2:
|
|
243
|
+
raise ValueError("There must be at least two streams.")
|
|
244
|
+
if not NQuad % 2 == 0:
|
|
245
|
+
raise ValueError("The number of streams must be even.")
|
|
246
|
+
if not NFourier > 0:
|
|
247
|
+
raise ValueError("The number of Fourier modes to use in the solution must be positive.")
|
|
248
|
+
if not NFourier <= NLeg:
|
|
249
|
+
raise ValueError("The number of Fourier modes to use in the solution must be less than or equal to the number of phase function Legendre coefficients used.")
|
|
250
|
+
if NFourier > 64 and not only_flux:
|
|
251
|
+
warnings.warn("`NFourier` is large and may cause errors, consider decreasing `NFourier` to 64 and it probably should be even less.")
|
|
228
252
|
# Not strictly necessary but there will be tremendous inaccuracies if this is violated
|
|
229
|
-
|
|
253
|
+
if not NLeg <= NQuad:
|
|
254
|
+
raise ValueError("There should be more streams than the number of phase function Legendre coefficients used.")
|
|
230
255
|
# We require principal angles and a downward incident beam
|
|
231
|
-
|
|
256
|
+
if not I0 >= 0:
|
|
257
|
+
raise ValueError("The incident beam must have positive intensity.")
|
|
232
258
|
if there_is_beam_source:
|
|
233
|
-
|
|
234
|
-
|
|
259
|
+
if not 0 < mu0 and mu0 <= 1:
|
|
260
|
+
raise ValueError("The cosine of the polar angle of the incident beam must be between 0 and 1, excluding 0.")
|
|
261
|
+
if not 0 <= phi0 and phi0 < 2 * pi:
|
|
262
|
+
raise ValueError("Provide the principal azimuthal angle for the incident beam (must be between 0 and 2pi, excluding 2pi).")
|
|
235
263
|
# Ensure that the BC inputs are of the correct shape
|
|
236
264
|
if len(np.atleast_1d(b_pos)) == 1:
|
|
237
265
|
b_pos_is_scalar = True
|
|
238
|
-
|
|
239
|
-
|
|
266
|
+
elif not np.shape(b_pos) == (N, NFourier):
|
|
267
|
+
raise ValueError("The shape of the bottom of atmosphere boundary conditions must equal (NQuad // 2, NFourier).")
|
|
240
268
|
if len(np.atleast_1d(b_neg)) == 1:
|
|
241
269
|
b_neg_is_scalar = True
|
|
242
|
-
|
|
243
|
-
|
|
270
|
+
elif not np.shape(b_neg) == (N, NFourier):
|
|
271
|
+
raise ValueError("The shape of the top of atmosphere boundary conditions must equal (NQuad // 2, NFourier).")
|
|
244
272
|
# The fractional scattering must be between 0 and 1
|
|
245
|
-
|
|
273
|
+
if not (np.all(0 <= f_arr) and np.all(f_arr <= 1)):
|
|
274
|
+
raise ValueError("The fractional scattering must be between 0 and 1.")
|
|
246
275
|
# The minimum threshold is 3 else the matrix will not be banded
|
|
247
|
-
|
|
276
|
+
if not use_banded_solver_NLayers >= 3:
|
|
277
|
+
raise ValueError("The minimum threshold `use_banded_solver_NLayers` is 3, else the matrix will not be banded.")
|
|
248
278
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
249
279
|
|
|
250
280
|
# Some more setup
|
|
@@ -268,7 +298,8 @@ def pydisort(
|
|
|
268
298
|
M_inv = 1 / mu_arr_pos
|
|
269
299
|
|
|
270
300
|
# We do not allow mu0 to equal a quadrature / computational angle
|
|
271
|
-
|
|
301
|
+
if np.any(np.abs(mu_arr_pos - mu0) < 1e-8):
|
|
302
|
+
raise ValueError("Some quadrature angles come too close to `mu0`. Perturb `NQuad` or `mu0` to rectify this error.")
|
|
272
303
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
273
304
|
|
|
274
305
|
# Delta-M scaling; there is no scaling if f = 0
|
|
@@ -317,7 +348,7 @@ def pydisort(
|
|
|
317
348
|
scale_tau,
|
|
318
349
|
only_flux,
|
|
319
350
|
use_banded_solver_NLayers,
|
|
320
|
-
|
|
351
|
+
autograd_compatible,
|
|
321
352
|
)
|
|
322
353
|
|
|
323
354
|
# TMS correction for the intensity (see Section 3.7.2)
|
|
@@ -527,7 +558,7 @@ def pydisort(
|
|
|
527
558
|
axis=1,
|
|
528
559
|
)
|
|
529
560
|
|
|
530
|
-
if
|
|
561
|
+
if autograd_compatible:
|
|
531
562
|
if not any_pos_contribution:
|
|
532
563
|
TMS_correction_pos = np.zeros((N, Ntau, Nphi))
|
|
533
564
|
if not any_neg_contribution:
|
|
@@ -579,6 +610,7 @@ def pydisort(
|
|
|
579
610
|
(tau[None, :] - 1 / x[:, None]) * np.exp(-tau / scaled_mu0)[None, :]
|
|
580
611
|
+ np.exp(-tau[None, :] / mu_arr_pos[:, None]) / x[:, None]
|
|
581
612
|
) / (mu_arr_pos * scaled_mu0 * x)[:, None]
|
|
613
|
+
|
|
582
614
|
return (
|
|
583
615
|
I0
|
|
584
616
|
/ (4 * pi)
|
|
@@ -593,26 +625,25 @@ def pydisort(
|
|
|
593
625
|
|
|
594
626
|
# The corrected intensity
|
|
595
627
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
596
|
-
def u_corrected(tau, phi, return_Fourier_error=False,
|
|
628
|
+
def u_corrected(tau, phi, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False):
|
|
597
629
|
tau = np.atleast_1d(tau)
|
|
598
630
|
phi = np.atleast_1d(phi)
|
|
599
631
|
NT_corrections = TMS_correction(tau, phi, is_antiderivative_wrt_tau)
|
|
600
632
|
|
|
601
|
-
if
|
|
633
|
+
if autograd_compatible:
|
|
602
634
|
NT_corrections = NT_corrections + np.concatenate(
|
|
603
635
|
[np.zeros((N, len(tau), len(phi))), IMS_correction(tau, phi, is_antiderivative_wrt_tau)], axis=0
|
|
604
636
|
)
|
|
605
637
|
else:
|
|
606
638
|
NT_corrections[N:, :, :] += IMS_correction(tau, phi, is_antiderivative_wrt_tau)
|
|
607
|
-
|
|
608
|
-
if return_Fourier_error:
|
|
609
|
-
u_star_outputs = u_star(tau, phi,
|
|
639
|
+
|
|
640
|
+
if return_Fourier_error or return_tau_arr:
|
|
641
|
+
u_star_outputs = u_star(tau, phi, is_antiderivative_wrt_tau, return_Fourier_error, return_tau_arr)
|
|
610
642
|
return (
|
|
611
643
|
u_star_outputs[0] + I0_orig * np.squeeze(NT_corrections),
|
|
612
|
-
|
|
613
|
-
)
|
|
644
|
+
) + u_star_outputs[1:]
|
|
614
645
|
else:
|
|
615
|
-
return u_star(tau, phi,
|
|
646
|
+
return u_star(tau, phi, is_antiderivative_wrt_tau) + I0_orig * np.squeeze(NT_corrections)
|
|
616
647
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
617
648
|
|
|
618
649
|
return mu_arr, flux_up, flux_down, u0, u_corrected
|
|
@@ -641,5 +672,5 @@ def pydisort(
|
|
|
641
672
|
scale_tau,
|
|
642
673
|
only_flux,
|
|
643
674
|
use_banded_solver_NLayers,
|
|
644
|
-
|
|
675
|
+
autograd_compatible,
|
|
645
676
|
)
|
PythonicDISORT/subroutines.py
CHANGED
|
@@ -28,6 +28,7 @@ def transform_interval(arr, c, d, a, b):
|
|
|
28
28
|
return (((arr - a) * (d - c)) / (b - a)) + c
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
|
|
31
32
|
def transform_weights(weights, c, d, a, b):
|
|
32
33
|
"""Transforms an array of quadrature weights from interval [a, b] to [c, d].
|
|
33
34
|
|
|
@@ -53,9 +54,10 @@ def transform_weights(weights, c, d, a, b):
|
|
|
53
54
|
return weights * (d - c) / (b - a)
|
|
54
55
|
|
|
55
56
|
|
|
57
|
+
|
|
56
58
|
def calculate_nu(mu, phi, mu_p, phi_p):
|
|
57
|
-
"""Calculates the cosine of the scattering angle
|
|
58
|
-
between incident angle
|
|
59
|
+
"""Calculates the cosine of the scattering angle ``nu``
|
|
60
|
+
between incident angle ``(mu_p, phi_p)`` and scattering angle ``(mu, phi)``.
|
|
59
61
|
|
|
60
62
|
Parameters
|
|
61
63
|
----------
|
|
@@ -71,7 +73,7 @@ def calculate_nu(mu, phi, mu_p, phi_p):
|
|
|
71
73
|
Returns
|
|
72
74
|
-------
|
|
73
75
|
nu : ndarray
|
|
74
|
-
Cosine of scattering angles which axes capture variation with
|
|
76
|
+
Cosine of scattering angles which axes capture variation with ``mu, phi, mu_p, phi_p`` respectively.
|
|
75
77
|
|
|
76
78
|
"""
|
|
77
79
|
mu, phi, mu_p, phi_p = np.atleast_1d(mu, phi, mu_p, phi_p)
|
|
@@ -83,12 +85,13 @@ def calculate_nu(mu, phi, mu_p, phi_p):
|
|
|
83
85
|
return np.squeeze(nu)
|
|
84
86
|
|
|
85
87
|
|
|
88
|
+
|
|
86
89
|
def Gauss_Legendre_quad(N, c=0, d=1):
|
|
87
90
|
"""Generates Gauss-Legendre quadrature zero points and weights for integration from c to d.
|
|
88
91
|
|
|
89
92
|
Parameters
|
|
90
93
|
----------
|
|
91
|
-
N : int
|
|
94
|
+
N : int (will be converted to int)
|
|
92
95
|
Number of quadrature nodes.
|
|
93
96
|
c : float, optional
|
|
94
97
|
Start of integration interval.
|
|
@@ -108,6 +111,7 @@ def Gauss_Legendre_quad(N, c=0, d=1):
|
|
|
108
111
|
return transform_interval(mu_arr_pos, c, d, -1, 1), transform_weights(W, c, d, -1, 1)
|
|
109
112
|
|
|
110
113
|
|
|
114
|
+
|
|
111
115
|
def Clenshaw_Curtis_quad(Nphi, c=0, d=(2 * pi)):
|
|
112
116
|
"""Generates Clenshaw-Curtis quadrature zero points and weights for integration from c to d.
|
|
113
117
|
|
|
@@ -129,8 +133,8 @@ def Clenshaw_Curtis_quad(Nphi, c=0, d=(2 * pi)):
|
|
|
129
133
|
|
|
130
134
|
"""
|
|
131
135
|
# Ensure that the number of nodes is odd and greater than 2
|
|
132
|
-
|
|
133
|
-
|
|
136
|
+
if not (Nphi > 2 and Nphi % 2 == 1):
|
|
137
|
+
raise ValueError("The number of quadrature nodes must be odd and greater than 2.")
|
|
134
138
|
|
|
135
139
|
Nphi -= 1 # The extra index corresponds to the point 0 which we will add later
|
|
136
140
|
Nphi_pos = Nphi // 2
|
|
@@ -144,9 +148,10 @@ def Clenshaw_Curtis_quad(Nphi, c=0, d=(2 * pi)):
|
|
|
144
148
|
return transform_interval(phi_arr, c, d, -1, 1), transform_weights(full_weights_phi, c, d, -1, 1)
|
|
145
149
|
|
|
146
150
|
|
|
151
|
+
|
|
147
152
|
def generate_FD_mat(Ntau, a, b):
|
|
148
153
|
"""Generates a sparse first derivative central difference (second-order accuracy)
|
|
149
|
-
matrix on [a,b] in
|
|
154
|
+
matrix on [a,b] in ``csr`` format with ``Ntau`` grid points.
|
|
150
155
|
Second order forward or backward differences are used at the boundaries.
|
|
151
156
|
|
|
152
157
|
Parameters
|
|
@@ -183,6 +188,7 @@ def generate_FD_mat(Ntau, a, b):
|
|
|
183
188
|
return tau_arr, first_deriv.tocsr()
|
|
184
189
|
|
|
185
190
|
|
|
191
|
+
|
|
186
192
|
def atleast_2d_append(*arys):
|
|
187
193
|
"""View inputs as arrays with at least two dimensions.
|
|
188
194
|
Dimensions are added as necessary to the back of the shape tuple rather than to the front.
|
|
@@ -219,7 +225,8 @@ def atleast_2d_append(*arys):
|
|
|
219
225
|
return res[0]
|
|
220
226
|
else:
|
|
221
227
|
return res
|
|
222
|
-
|
|
228
|
+
|
|
229
|
+
|
|
223
230
|
|
|
224
231
|
def generate_diff_act_flux_funcs(u0):
|
|
225
232
|
"""Generates the up and down diffuse actinic flux functions respectively.
|
|
@@ -237,73 +244,95 @@ def generate_diff_act_flux_funcs(u0):
|
|
|
237
244
|
Returns
|
|
238
245
|
-------
|
|
239
246
|
Fp_act(tau) : function
|
|
240
|
-
Actinic flux function with argument
|
|
247
|
+
Actinic flux function with argument ``tau`` (type: array) for positive (upward) ``mu`` values.
|
|
241
248
|
Returns the diffuse flux magnitudes (type: array).
|
|
242
|
-
Pass
|
|
243
|
-
to switch to an antiderivative of the function with respect to
|
|
249
|
+
Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
|
|
250
|
+
to switch to an antiderivative of the function with respect to ``tau``.
|
|
251
|
+
Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
|
|
244
252
|
Fm_act(tau) : function
|
|
245
|
-
Actinic flux function with argument
|
|
253
|
+
Actinic flux function with argument ``tau`` (type: array) for negative (downward) ``mu`` values.
|
|
246
254
|
Returns the diffuse flux magnitudes (type: array).
|
|
247
|
-
Pass
|
|
248
|
-
to switch to an antiderivative of the function with respect to
|
|
255
|
+
Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
|
|
256
|
+
to switch to an antiderivative of the function with respect to ``tau``.
|
|
257
|
+
Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
|
|
249
258
|
|
|
250
259
|
"""
|
|
251
|
-
N =
|
|
260
|
+
N = len(u0(0)) // 2
|
|
252
261
|
GL_weights = Gauss_Legendre_quad(N, 0, 1)[1]
|
|
253
262
|
|
|
254
263
|
# Note that the zeroth axis of the array u0(tau) captures variation with mu
|
|
255
|
-
def flux_act_up(tau, is_antiderivative_wrt_tau=False):
|
|
256
|
-
|
|
264
|
+
def flux_act_up(tau, is_antiderivative_wrt_tau=False, return_tau_arr=False):
|
|
265
|
+
if return_tau_arr:
|
|
266
|
+
(
|
|
267
|
+
u0_cache,
|
|
268
|
+
tau_arr,
|
|
269
|
+
) = u0(tau, is_antiderivative_wrt_tau, True)
|
|
270
|
+
return np.squeeze(2 * pi * GL_weights @ u0_cache[:N])[()], tau_arr
|
|
271
|
+
else:
|
|
272
|
+
return np.squeeze(2 * pi * GL_weights @ u0(tau, is_antiderivative_wrt_tau)[:N])[()]
|
|
257
273
|
|
|
258
|
-
def flux_act_down_diffuse(tau, is_antiderivative_wrt_tau=False):
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
274
|
+
def flux_act_down_diffuse(tau, is_antiderivative_wrt_tau=False, return_tau_arr=False):
|
|
275
|
+
if return_tau_arr:
|
|
276
|
+
(
|
|
277
|
+
u0_cache,
|
|
278
|
+
tau_arr,
|
|
279
|
+
act_dscale_reclassification,
|
|
280
|
+
) = u0(tau, is_antiderivative_wrt_tau, True, _return_act_dscale_for_reclass=True)
|
|
281
|
+
result_without_reclassification = 2 * pi * GL_weights @ u0_cache[N:]
|
|
282
|
+
return np.squeeze(result_without_reclassification + act_dscale_reclassification)[()], tau_arr
|
|
283
|
+
else:
|
|
284
|
+
(
|
|
285
|
+
u0_cache,
|
|
286
|
+
act_dscale_reclassification,
|
|
287
|
+
) = u0(tau, is_antiderivative_wrt_tau, False, _return_act_dscale_for_reclass=True)
|
|
288
|
+
result_without_reclassification = 2 * pi * GL_weights @ u0_cache[N:]
|
|
289
|
+
return np.squeeze(result_without_reclassification + act_dscale_reclassification)[()]
|
|
262
290
|
|
|
263
291
|
return flux_act_up, flux_act_down_diffuse
|
|
264
292
|
|
|
265
293
|
|
|
266
|
-
|
|
294
|
+
|
|
295
|
+
def interpolate(u):
|
|
267
296
|
"""Polynomial (Barycentric) interpolation with respect to mu. The output
|
|
268
297
|
is a function that is continuous and variable in all three arguments: mu, tau and phi.
|
|
269
298
|
Discussed in sections 3.7 and 6.3 in the Comprehensive Documentation.
|
|
270
299
|
|
|
271
300
|
Parameters
|
|
272
301
|
----------
|
|
273
|
-
mu_arr_pos : array
|
|
274
|
-
Positive `mu` (cosine of polar angle) quadrature nodes.
|
|
275
|
-
Equivalent to `mu_arr[:N]`.
|
|
276
302
|
u : function
|
|
277
|
-
Non-interpolated intensity function as
|
|
303
|
+
Non-interpolated intensity function as given by ``pydisort``.
|
|
278
304
|
|
|
279
305
|
Returns
|
|
280
306
|
-------
|
|
281
307
|
u_interpol : function
|
|
282
|
-
Intensity function with arguments
|
|
283
|
-
Returns an ndarray with axes corresponding to variation with
|
|
284
|
-
Pass
|
|
308
|
+
Intensity function with arguments ``(mu, tau, phi)`` each of type array or float.
|
|
309
|
+
Returns an ndarray with axes corresponding to variation with ``mu, tau, phi`` respectively.
|
|
310
|
+
Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
|
|
311
|
+
to switch to an antiderivative of the function with respect to ``tau``.
|
|
312
|
+
Pass ``return_Fourier_error = True`` (defaults to ``False``) to return the
|
|
285
313
|
Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
|
|
286
|
-
Pass
|
|
287
|
-
to switch to an antiderivative of the function with respect to `tau`.
|
|
314
|
+
Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
|
|
288
315
|
|
|
289
316
|
"""
|
|
290
|
-
N = len(
|
|
317
|
+
N = len(u(0, 0)) // 2
|
|
318
|
+
mu_arr_pos = Gauss_Legendre_quad(N)[0]
|
|
319
|
+
|
|
291
320
|
u_pos_interpol = sc.interpolate.BarycentricInterpolator(mu_arr_pos)
|
|
292
321
|
u_neg_interpol = sc.interpolate.BarycentricInterpolator(-mu_arr_pos)
|
|
293
322
|
|
|
294
|
-
def u_interpol(mu, tau, phi, return_Fourier_error=False,
|
|
295
|
-
|
|
323
|
+
def u_interpol(mu, tau, phi, is_antiderivative_wrt_tau=False, return_Fourier_error=False, return_tau_arr=False):
|
|
324
|
+
if not np.all(np.abs(mu) <= 1):
|
|
325
|
+
raise ValueError("mu values must be between -1 and 1.")
|
|
326
|
+
|
|
296
327
|
mu = np.atleast_1d(mu)
|
|
297
328
|
|
|
298
|
-
if return_Fourier_error:
|
|
299
|
-
|
|
329
|
+
if return_Fourier_error or return_tau_arr:
|
|
330
|
+
u_outputs = u(tau, phi, is_antiderivative_wrt_tau, return_Fourier_error, return_tau_arr)
|
|
331
|
+
u_cache = u_outputs[0]
|
|
300
332
|
else:
|
|
301
|
-
u_cache = u(tau, phi,
|
|
333
|
+
u_cache = u(tau, phi, is_antiderivative_wrt_tau)
|
|
302
334
|
|
|
303
|
-
|
|
304
|
-
assert N == u_shape[0] // 2 # Check that mu_arr_pos (of length N) matches the function u
|
|
305
|
-
|
|
306
|
-
results = np.empty((len(mu),) + u_shape[1:])
|
|
335
|
+
results = np.empty((len(mu),) + np.shape(u_cache)[1:])
|
|
307
336
|
mask_pos = mu > 0
|
|
308
337
|
mask_else = ~mask_pos
|
|
309
338
|
|
|
@@ -314,17 +343,18 @@ def interpolate(mu_arr_pos, u):
|
|
|
314
343
|
u_neg_interpol.set_yi(u_cache[N:])
|
|
315
344
|
results[mask_else] = u_neg_interpol(mu[mask_else])
|
|
316
345
|
|
|
317
|
-
if return_Fourier_error:
|
|
318
|
-
return np.squeeze(results)[()],
|
|
346
|
+
if return_Fourier_error or return_tau_arr:
|
|
347
|
+
return (np.squeeze(results)[()],) + u_outputs[1:]
|
|
319
348
|
else:
|
|
320
349
|
return np.squeeze(results)[()]
|
|
321
350
|
|
|
322
351
|
return u_interpol
|
|
323
|
-
|
|
352
|
+
|
|
353
|
+
|
|
324
354
|
|
|
325
355
|
def to_diag_ordered_form(A, sym_offset):
|
|
326
356
|
"""
|
|
327
|
-
Convert a matrix A to the diagonal ordered form required by
|
|
357
|
+
Convert a matrix A to the diagonal ordered form required by ``scipy.linalg.solve_banded``.
|
|
328
358
|
We assume that the matrix has the same number of super- and sub-diagonals.
|
|
329
359
|
|
|
330
360
|
Parameters
|
|
@@ -356,6 +386,7 @@ def to_diag_ordered_form(A, sym_offset):
|
|
|
356
386
|
axis=0,
|
|
357
387
|
)
|
|
358
388
|
|
|
389
|
+
|
|
359
390
|
|
|
360
391
|
def _mathscr_v(tau, # Input optical depths
|
|
361
392
|
l, # Layer index of each input optical depth
|
|
@@ -366,40 +397,40 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
366
397
|
G_inv, # Inverse of eigenvector matrix
|
|
367
398
|
mu_arr, # Quadrature nodes for both hemispheres
|
|
368
399
|
is_antiderivative_wrt_tau=False, # Switch to an antiderivative of the function?
|
|
369
|
-
|
|
400
|
+
autograd_compatible=False, # Should the output functions be compatible with autograd?
|
|
370
401
|
):
|
|
371
402
|
"""Particular solution for isotropic internal sources.
|
|
372
403
|
Refer to Section 3.6.1 of the Comprehensive Documentation.
|
|
373
404
|
It has many seemingly redundant arguments to maximize
|
|
374
|
-
precomputation in the
|
|
375
|
-
and
|
|
405
|
+
precomputation in the ``_assemble_intensity_and_fluxes``
|
|
406
|
+
and ``_solve_for_coeffs`` functions which call it.
|
|
376
407
|
|
|
377
408
|
Arguments of _mathscr_v
|
|
378
409
|
| Variable | Shape |
|
|
379
410
|
| --------------------------- | ------------------------------------- |
|
|
380
|
-
|
|
|
381
|
-
|
|
|
382
|
-
|
|
|
383
|
-
|
|
|
384
|
-
|
|
|
385
|
-
|
|
|
386
|
-
|
|
|
387
|
-
|
|
|
388
|
-
|
|
|
389
|
-
|
|
|
411
|
+
| ``tau`` | ``Ntau`` |
|
|
412
|
+
| ``l`` | ``Ntau`` |
|
|
413
|
+
| ``Nscoeffs`` | scalar |
|
|
414
|
+
| ``s_poly_coeffs`` | ``NLayers x Nscoeffs`` or ``Nscoeffs`` |
|
|
415
|
+
| ``G`` | ``NLayers<= x NQuad x NQuad`` |
|
|
416
|
+
| ``K`` | ``NLayers<= x NQuad`` |
|
|
417
|
+
| ``G_inv`` | ``NLayers<= x NQuad x NQuad`` or ``None`` |
|
|
418
|
+
| ``mu_arr`` | ``NQuad`` |
|
|
419
|
+
| ``is_antiderivative_wrt_tau`` | boolean |
|
|
420
|
+
| ``autograd_compatible`` | boolean |
|
|
390
421
|
|
|
391
422
|
Notable internal variables of _mathscr_v
|
|
392
423
|
| Variable | Shape |
|
|
393
424
|
| ---------------- | ----------------------------------- |
|
|
394
|
-
| i_arr |
|
|
395
|
-
| i_arr_repeat |
|
|
396
|
-
| j_arr |
|
|
397
|
-
| s_poly_coeffs_nj |
|
|
398
|
-
| OUTPUT |
|
|
425
|
+
| i_arr | ``Nscoeffs`` |
|
|
426
|
+
| i_arr_repeat | ``Nscoeffs*(Nscoeffs+1)/2`` | Using triangular number formula
|
|
427
|
+
| j_arr | ``Nscoeffs*(Nscoeffs+1)/2`` | Using triangular number formula
|
|
428
|
+
| s_poly_coeffs_nj | ``NLayers x Nscoeffs*(Nscoeffs+1)/2`` |
|
|
429
|
+
| OUTPUT | ``NQuad x Ntau`` |
|
|
399
430
|
"""
|
|
400
431
|
n = Nscoeffs - 1
|
|
401
432
|
|
|
402
|
-
if
|
|
433
|
+
if autograd_compatible:
|
|
403
434
|
import autograd.numpy as np
|
|
404
435
|
|
|
405
436
|
def mathscr_b(i):
|
|
@@ -407,9 +438,9 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
407
438
|
Notable internal variables of mathscr_b
|
|
408
439
|
| Variable | Shape |
|
|
409
440
|
| ---------------- | ------------------------------------- |
|
|
410
|
-
| j_arr |
|
|
411
|
-
| s_poly_coeffs_nj |
|
|
412
|
-
| OUTPUT |
|
|
441
|
+
| j_arr | ``i + 1`` |
|
|
442
|
+
| s_poly_coeffs_nj | ``i + 1`` |
|
|
443
|
+
| OUTPUT | ``Nscoeffs x (i + 1) x NQuad`` |
|
|
413
444
|
"""
|
|
414
445
|
|
|
415
446
|
j_arr = np.arange(i + 1)
|
|
@@ -460,6 +491,7 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
460
491
|
)
|
|
461
492
|
|
|
462
493
|
|
|
494
|
+
|
|
463
495
|
def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u):
|
|
464
496
|
"""Performs pointwise comparisons between results from Stamnes' DISORT,
|
|
465
497
|
which are stored in .npz files, against results from PythonicDISORT. Used in our PyTests.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PythonicDISORT
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
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
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
PythonicDISORT/__init__.py,sha256=V1obNzaFgqInxqYJ5h_r6JHbZRxx4VM5VgzCIS6EEzI,84
|
|
2
|
+
PythonicDISORT/_assemble_intensity_and_fluxes.py,sha256=PsZJN-eHKKMP2VHqk8zFVw1CZ5Em2tc0aJOej3qdg2k,29134
|
|
3
|
+
PythonicDISORT/_solve_for_coeffs.py,sha256=8xnjR6fenaTiPXXzxvtXmETFZRnwOnGkHIuuOFpz7Lc,15402
|
|
4
|
+
PythonicDISORT/_solve_for_gen_and_part_sols.py,sha256=1IvyEZrcqLcafAQgAFpXtJ2H6hCvIM11OgBHF3KqmGc,12960
|
|
5
|
+
PythonicDISORT/pydisort.py,sha256=AZmhV2niPW_XK5cGi_tMSgQQemBtApQB1sKLbEQXqLY,37039
|
|
6
|
+
PythonicDISORT/subroutines.py,sha256=tq9_m4iXE8zLE8hh69aWh4wNfRHyCkgZFxZI48DRNE0,21312
|
|
7
|
+
PythonicDISORT-0.9.3.dist-info/LICENSE.md,sha256=q1i1QVh0OLzK0fj6kaNRNWsM_KZca66JwWgWwbuCPlA,1092
|
|
8
|
+
PythonicDISORT-0.9.3.dist-info/METADATA,sha256=elndi6Hlfg3SmgDWdV-Esuu3JML574zlvDvpwrI4I8k,7102
|
|
9
|
+
PythonicDISORT-0.9.3.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
10
|
+
PythonicDISORT-0.9.3.dist-info/top_level.txt,sha256=aX6kCeI1E7naZz3xfw4_mIbWh5KBh9dmF3FIvgP5m3Y,15
|
|
11
|
+
PythonicDISORT-0.9.3.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
PythonicDISORT/__init__.py,sha256=V1obNzaFgqInxqYJ5h_r6JHbZRxx4VM5VgzCIS6EEzI,84
|
|
2
|
-
PythonicDISORT/_assemble_intensity_and_fluxes.py,sha256=j3w78Y8844HlI1z8JFmOgNs4ogDsma8VySbI8W2-m-8,27868
|
|
3
|
-
PythonicDISORT/_solve_for_coeffs.py,sha256=UccDeCrr0jBCKbK73Eq9SwWEkSkjVyAVheew6nYcWdw,15368
|
|
4
|
-
PythonicDISORT/_solve_for_gen_and_part_sols.py,sha256=MNNg5f4S5XY0D6btH3D-9p-eye9M_rT7GUlaHnys7Ts,13138
|
|
5
|
-
PythonicDISORT/pydisort.py,sha256=SNpTpEcRPHNZqnVL86xV5AzPPNu7AVgmpXhV6a5Rqh8,33204
|
|
6
|
-
PythonicDISORT/subroutines.py,sha256=on0VpAtUc8b7ysaQqy8VetSnTMhsE_yEDAwyRHX2IuU,20049
|
|
7
|
-
PythonicDISORT-0.9.1.dist-info/LICENSE.md,sha256=q1i1QVh0OLzK0fj6kaNRNWsM_KZca66JwWgWwbuCPlA,1092
|
|
8
|
-
PythonicDISORT-0.9.1.dist-info/METADATA,sha256=SLwWEGhdEqHtpHNN14-Y0Kd2tvIYByXubdqzpr1nqKc,7102
|
|
9
|
-
PythonicDISORT-0.9.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
|
10
|
-
PythonicDISORT-0.9.1.dist-info/top_level.txt,sha256=aX6kCeI1E7naZz3xfw4_mIbWh5KBh9dmF3FIvgP5m3Y,15
|
|
11
|
-
PythonicDISORT-0.9.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|