PythonicDISORT 0.7.0__py3-none-any.whl → 0.9.0__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 +189 -58
- PythonicDISORT/_solve_for_coeffs.py +34 -20
- PythonicDISORT/_solve_for_gen_and_part_sols.py +17 -15
- PythonicDISORT/pydisort.py +174 -97
- PythonicDISORT/subroutines.py +107 -31
- {PythonicDISORT-0.7.0.dist-info → PythonicDISORT-0.9.0.dist-info}/METADATA +28 -26
- PythonicDISORT-0.9.0.dist-info/RECORD +11 -0
- {PythonicDISORT-0.7.0.dist-info → PythonicDISORT-0.9.0.dist-info}/WHEEL +1 -1
- PythonicDISORT-0.7.0.dist-info/RECORD +0 -11
- {PythonicDISORT-0.7.0.dist-info → PythonicDISORT-0.9.0.dist-info}/LICENSE.md +0 -0
- {PythonicDISORT-0.7.0.dist-info → PythonicDISORT-0.9.0.dist-info}/top_level.txt +0 -0
|
@@ -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:
|
|
@@ -206,11 +233,15 @@ def _assemble_intensity_and_fluxes(
|
|
|
206
233
|
K_collect_0,
|
|
207
234
|
G_inv_collect_0,
|
|
208
235
|
mu_arr,
|
|
209
|
-
|
|
236
|
+
is_antiderivative_wrt_tau,
|
|
237
|
+
_autograd_compatible
|
|
210
238
|
)
|
|
211
239
|
|
|
212
240
|
if _autograd_compatible:
|
|
213
|
-
|
|
241
|
+
if NFourier == 1:
|
|
242
|
+
um = um + _mathscr_v_contribution.T
|
|
243
|
+
else:
|
|
244
|
+
um = um + np.concatenate((_mathscr_v_contribution.T, np.zeros((NFourier - 1, len(tau), NQuad))))
|
|
214
245
|
else:
|
|
215
246
|
um[0, :, :] += _mathscr_v_contribution.T
|
|
216
247
|
|
|
@@ -229,20 +260,43 @@ def _assemble_intensity_and_fluxes(
|
|
|
229
260
|
],
|
|
230
261
|
axis=-1,
|
|
231
262
|
)
|
|
232
|
-
if
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
263
|
+
if is_antiderivative_wrt_tau:
|
|
264
|
+
if there_is_beam_source:
|
|
265
|
+
ulast = np.einsum(
|
|
266
|
+
"tij, tj -> it",
|
|
267
|
+
GC_collect[-1, l, :, :],
|
|
268
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect[-1, l, :]),
|
|
269
|
+
optimize=True,
|
|
270
|
+
) + B_collect[-1, l, :].T * np.exp(-scaled_tau[None, :] / mu0) / (
|
|
271
|
+
-scale_tau[None, l] / mu0
|
|
272
|
+
)
|
|
273
|
+
else:
|
|
274
|
+
ulast = np.einsum(
|
|
275
|
+
"tij, tj -> it",
|
|
276
|
+
GC_collect[-1, l, :, :],
|
|
277
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect[-1, l, :]),
|
|
278
|
+
optimize=True,
|
|
279
|
+
)
|
|
236
280
|
else:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
281
|
+
if there_is_beam_source:
|
|
282
|
+
ulast = np.einsum(
|
|
283
|
+
"tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
|
|
284
|
+
) + B_collect[-1, l, :].T * np.exp(-scaled_tau[None, :] / mu0)
|
|
285
|
+
else:
|
|
286
|
+
ulast = np.einsum(
|
|
287
|
+
"tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
|
|
288
|
+
)
|
|
289
|
+
|
|
240
290
|
intensities_abs = np.abs(intensities)
|
|
241
291
|
Fourier_error = np.max(
|
|
242
|
-
np.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
292
|
+
np.divide(
|
|
293
|
+
np.abs(
|
|
294
|
+
ulast[:, :, None] * np.cos((NFourier - 1) * (phi0 - phi))[None, None, :]
|
|
295
|
+
),
|
|
296
|
+
intensities_abs,
|
|
297
|
+
out=np.zeros_like(intensities_abs),
|
|
298
|
+
where=intensities_abs > 1e-8,
|
|
299
|
+
)
|
|
246
300
|
)
|
|
247
301
|
return I0_orig * np.squeeze(intensities), Fourier_error
|
|
248
302
|
else:
|
|
@@ -251,7 +305,16 @@ def _assemble_intensity_and_fluxes(
|
|
|
251
305
|
|
|
252
306
|
# Construct u0
|
|
253
307
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
254
|
-
def u0(tau,
|
|
308
|
+
def u0(tau, is_antiderivative_wrt_tau=False, _return_act_dscale_for_reclass=False):
|
|
309
|
+
"""
|
|
310
|
+
Zeroth Fourier mode of the intensity with argument `tau` (type: array or float).
|
|
311
|
+
Returns an ndarray with axes corresponding to variation with `mu` and `tau` respectively.
|
|
312
|
+
This function is useful for calculating actinic fluxes and other quantities of interest,
|
|
313
|
+
but reclassification of delta-scaled flux and other corrections must be done manually
|
|
314
|
+
(for actinic flux `generate_diff_act_flux_funcs` will automatically perform the reclassification).
|
|
315
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
316
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
317
|
+
"""
|
|
255
318
|
tau = np.atleast_1d(tau)
|
|
256
319
|
assert np.all(tau >= 0)
|
|
257
320
|
assert np.all(tau <= tau_arr[-1])
|
|
@@ -268,11 +331,14 @@ def _assemble_intensity_and_fluxes(
|
|
|
268
331
|
scaled_tau = scaled_tau_arr_l - scaled_tau_dist_from_top
|
|
269
332
|
# The following complements the function `subroutines.generate_diff_act_flux_funcs`
|
|
270
333
|
# in performing the reclassification of delta-scaled actinic flux
|
|
271
|
-
if
|
|
272
|
-
|
|
334
|
+
if _return_act_dscale_for_reclass:
|
|
335
|
+
if is_antiderivative_wrt_tau:
|
|
336
|
+
act_dscale_reclassification = I0 * np.exp(-scaled_tau / mu0) / (-scale_tau[l] / mu0) - I0 * np.exp(-tau / mu0) * -mu0
|
|
337
|
+
else:
|
|
338
|
+
act_dscale_reclassification = I0 * np.exp(-scaled_tau / mu0) - I0 * np.exp(-tau / mu0)
|
|
273
339
|
else:
|
|
274
340
|
scaled_tau = tau
|
|
275
|
-
if
|
|
341
|
+
if _return_act_dscale_for_reclass:
|
|
276
342
|
act_dscale_reclassification = 0
|
|
277
343
|
|
|
278
344
|
exponent = np.concatenate(
|
|
@@ -282,14 +348,33 @@ def _assemble_intensity_and_fluxes(
|
|
|
282
348
|
],
|
|
283
349
|
axis=-1,
|
|
284
350
|
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
351
|
+
|
|
352
|
+
if is_antiderivative_wrt_tau:
|
|
353
|
+
if there_is_beam_source:
|
|
354
|
+
u0 = np.einsum(
|
|
355
|
+
"tij, tj -> it",
|
|
356
|
+
GC_collect_0[l, :, :],
|
|
357
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
|
|
358
|
+
optimize=True,
|
|
359
|
+
) + B_collect_0[l, :].T * np.exp(-scaled_tau[None, :] / mu0) / (
|
|
360
|
+
-scale_tau[None, l] / mu0
|
|
361
|
+
)
|
|
362
|
+
else:
|
|
363
|
+
u0 = np.einsum(
|
|
364
|
+
"tij, tj -> it",
|
|
365
|
+
GC_collect_0[l, :, :],
|
|
366
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
|
|
367
|
+
optimize=True,
|
|
368
|
+
)
|
|
289
369
|
else:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
370
|
+
if there_is_beam_source:
|
|
371
|
+
u0 = np.einsum(
|
|
372
|
+
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
373
|
+
) + B_collect_0[l, :].T * np.exp(-scaled_tau[None, :] / mu0)
|
|
374
|
+
else:
|
|
375
|
+
u0 = np.einsum(
|
|
376
|
+
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
377
|
+
)
|
|
293
378
|
|
|
294
379
|
# Contribution from particular solution for isotropic internal sources
|
|
295
380
|
if there_is_iso_source:
|
|
@@ -301,11 +386,12 @@ def _assemble_intensity_and_fluxes(
|
|
|
301
386
|
K_collect_0,
|
|
302
387
|
G_inv_collect_0,
|
|
303
388
|
mu_arr,
|
|
304
|
-
|
|
389
|
+
is_antiderivative_wrt_tau,
|
|
390
|
+
_autograd_compatible
|
|
305
391
|
)
|
|
306
|
-
u0
|
|
392
|
+
u0 = u0 + _mathscr_v_contribution
|
|
307
393
|
|
|
308
|
-
if
|
|
394
|
+
if _return_act_dscale_for_reclass:
|
|
309
395
|
return I0_orig * np.squeeze(u0), act_dscale_reclassification
|
|
310
396
|
else:
|
|
311
397
|
return I0_orig * np.squeeze(u0)
|
|
@@ -320,7 +406,13 @@ def _assemble_intensity_and_fluxes(
|
|
|
320
406
|
B_neg = B_collect_0[:, N:].T
|
|
321
407
|
|
|
322
408
|
|
|
323
|
-
def flux_up(tau):
|
|
409
|
+
def flux_up(tau, is_antiderivative_wrt_tau=False):
|
|
410
|
+
"""
|
|
411
|
+
(Energetic) Flux function with argument `tau` (type: array or float) for positive (upward) `mu` values.
|
|
412
|
+
Returns the diffuse flux magnitudes (same type and size as `tau`).
|
|
413
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
414
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
415
|
+
"""
|
|
324
416
|
tau = np.atleast_1d(tau)
|
|
325
417
|
assert np.all(tau >= 0)
|
|
326
418
|
assert np.all(tau <= tau_arr[-1])
|
|
@@ -347,13 +439,19 @@ def _assemble_intensity_and_fluxes(
|
|
|
347
439
|
K_collect_0,
|
|
348
440
|
G_inv_collect_0,
|
|
349
441
|
mu_arr,
|
|
350
|
-
|
|
442
|
+
is_antiderivative_wrt_tau,
|
|
443
|
+
_autograd_compatible
|
|
351
444
|
)[:N, :]
|
|
352
445
|
else:
|
|
353
446
|
_mathscr_v_contribution = 0
|
|
354
|
-
|
|
447
|
+
|
|
355
448
|
if there_is_beam_source:
|
|
356
|
-
|
|
449
|
+
if is_antiderivative_wrt_tau:
|
|
450
|
+
direct_beam_contribution = (
|
|
451
|
+
B_pos[:, l] * np.exp(-scaled_tau[None, :] / mu0) / (-scale_tau[None, l] / mu0)
|
|
452
|
+
)
|
|
453
|
+
else:
|
|
454
|
+
direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau[None, :] / mu0)
|
|
357
455
|
else:
|
|
358
456
|
direct_beam_contribution = 0
|
|
359
457
|
|
|
@@ -364,17 +462,36 @@ def _assemble_intensity_and_fluxes(
|
|
|
364
462
|
],
|
|
365
463
|
axis=-1,
|
|
366
464
|
)
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
465
|
+
|
|
466
|
+
if is_antiderivative_wrt_tau:
|
|
467
|
+
u0_pos = (
|
|
468
|
+
np.einsum(
|
|
469
|
+
"tij, tj -> it",
|
|
470
|
+
GC_pos[l, :, :],
|
|
471
|
+
np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
|
|
472
|
+
)
|
|
473
|
+
+ direct_beam_contribution
|
|
474
|
+
+ _mathscr_v_contribution
|
|
475
|
+
)
|
|
476
|
+
else:
|
|
477
|
+
u0_pos = (
|
|
478
|
+
np.einsum("tij, tj -> it", GC_pos[l, :, :], np.exp(exponent))
|
|
479
|
+
+ direct_beam_contribution
|
|
480
|
+
+ _mathscr_v_contribution
|
|
481
|
+
)
|
|
372
482
|
|
|
373
483
|
flux = 2 * pi * (mu_arr_pos * W) @ u0_pos
|
|
374
484
|
return I0_orig * np.squeeze(flux)[()]
|
|
375
485
|
|
|
376
486
|
|
|
377
|
-
def flux_down(tau):
|
|
487
|
+
def flux_down(tau, is_antiderivative_wrt_tau=False):
|
|
488
|
+
"""
|
|
489
|
+
(Energetic) Flux function with argument `tau` (type: array or float) for negative (downward) `mu` values.
|
|
490
|
+
Returns a tuple of the diffuse and direct flux magnitudes respectively where each entry is of the
|
|
491
|
+
same type and size as `tau`.
|
|
492
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
493
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
494
|
+
"""
|
|
378
495
|
tau = np.atleast_1d(tau)
|
|
379
496
|
assert np.all(tau >= 0)
|
|
380
497
|
assert np.all(tau <= tau_arr[-1])
|
|
@@ -401,15 +518,21 @@ def _assemble_intensity_and_fluxes(
|
|
|
401
518
|
K_collect_0,
|
|
402
519
|
G_inv_collect_0,
|
|
403
520
|
mu_arr,
|
|
404
|
-
|
|
521
|
+
is_antiderivative_wrt_tau,
|
|
522
|
+
_autograd_compatible
|
|
405
523
|
)[N:, :]
|
|
406
524
|
else:
|
|
407
525
|
_mathscr_v_contribution = 0
|
|
408
526
|
|
|
409
527
|
if there_is_beam_source:
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
528
|
+
if is_antiderivative_wrt_tau:
|
|
529
|
+
direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau[None, :] / mu0) / (-scale_tau[None, l] / mu0)
|
|
530
|
+
direct_beam = I0 * mu0 * np.exp(-tau / mu0) * -mu0
|
|
531
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau[None, l] / mu0)
|
|
532
|
+
else:
|
|
533
|
+
direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau[None, :] / mu0)
|
|
534
|
+
direct_beam = I0 * mu0 * np.exp(-tau / mu0)
|
|
535
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0)
|
|
413
536
|
else:
|
|
414
537
|
direct_beam_contribution = 0
|
|
415
538
|
direct_beam = 0
|
|
@@ -422,11 +545,19 @@ def _assemble_intensity_and_fluxes(
|
|
|
422
545
|
],
|
|
423
546
|
axis=-1,
|
|
424
547
|
)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
548
|
+
|
|
549
|
+
if is_antiderivative_wrt_tau:
|
|
550
|
+
u0_neg = (
|
|
551
|
+
np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]))
|
|
552
|
+
+ direct_beam_contribution
|
|
553
|
+
+ _mathscr_v_contribution
|
|
554
|
+
)
|
|
555
|
+
else:
|
|
556
|
+
u0_neg = (
|
|
557
|
+
np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent))
|
|
558
|
+
+ direct_beam_contribution
|
|
559
|
+
+ _mathscr_v_contribution
|
|
560
|
+
)
|
|
430
561
|
|
|
431
562
|
diffuse_flux = 2 * pi * (mu_arr_pos * W) @ u0_neg + direct_beam_scaled - direct_beam
|
|
432
563
|
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.
|
|
@@ -124,7 +124,8 @@ def _solve_for_coeffs(
|
|
|
124
124
|
# _mathscr_v_contribution
|
|
125
125
|
if there_is_iso_source and m_equals_0:
|
|
126
126
|
_mathscr_v_contribution_top = -_mathscr_v(
|
|
127
|
-
np.array([0]),
|
|
127
|
+
np.array([0]),
|
|
128
|
+
np.array([0]),
|
|
128
129
|
s_poly_coeffs[0:1, :],
|
|
129
130
|
Nscoeffs,
|
|
130
131
|
G_collect_m[0:1, N:, :],
|
|
@@ -139,38 +140,51 @@ def _solve_for_coeffs(
|
|
|
139
140
|
_mathscr_v_contribution_middle = (
|
|
140
141
|
_mathscr_v(
|
|
141
142
|
tau_arr[:-1],
|
|
142
|
-
indices,
|
|
143
|
-
s_poly_coeffs
|
|
143
|
+
indices + 1,
|
|
144
|
+
s_poly_coeffs,
|
|
144
145
|
Nscoeffs,
|
|
145
|
-
G_collect_m
|
|
146
|
-
K_collect_m
|
|
147
|
-
G_inv_collect_0
|
|
146
|
+
G_collect_m,
|
|
147
|
+
K_collect_m,
|
|
148
|
+
G_inv_collect_0,
|
|
148
149
|
mu_arr,
|
|
149
150
|
)
|
|
150
151
|
- _mathscr_v(
|
|
151
152
|
tau_arr[:-1],
|
|
152
153
|
indices,
|
|
153
|
-
s_poly_coeffs
|
|
154
|
+
s_poly_coeffs,
|
|
154
155
|
Nscoeffs,
|
|
155
|
-
G_collect_m
|
|
156
|
-
K_collect_m
|
|
157
|
-
G_inv_collect_0
|
|
156
|
+
G_collect_m,
|
|
157
|
+
K_collect_m,
|
|
158
|
+
G_inv_collect_0,
|
|
158
159
|
mu_arr,
|
|
159
160
|
)
|
|
160
|
-
).ravel()
|
|
161
|
+
).ravel(order='F')
|
|
161
162
|
|
|
162
|
-
_mathscr_v_contribution_bottom = _mathscr_v(
|
|
163
|
-
tau_arr[-1:],
|
|
163
|
+
_mathscr_v_contribution_bottom = -_mathscr_v(
|
|
164
|
+
tau_arr[-1:],
|
|
165
|
+
np.array([0]),
|
|
164
166
|
s_poly_coeffs[-1:, :],
|
|
165
167
|
Nscoeffs,
|
|
166
|
-
G_collect_m[-1:, N
|
|
168
|
+
G_collect_m[-1:, :N, :],
|
|
167
169
|
K_collect_m[-1:, :],
|
|
168
170
|
G_inv_collect_0[-1:, :, :],
|
|
169
|
-
mu_arr
|
|
171
|
+
mu_arr
|
|
172
|
+
).ravel()
|
|
170
173
|
if NBDRF > 0:
|
|
171
174
|
_mathscr_v_contribution_bottom = (
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
_mathscr_v_contribution_bottom
|
|
176
|
+
+ R
|
|
177
|
+
@ _mathscr_v(
|
|
178
|
+
tau_arr[-1:],
|
|
179
|
+
np.array([0]),
|
|
180
|
+
s_poly_coeffs[-1:, :],
|
|
181
|
+
Nscoeffs,
|
|
182
|
+
G_collect_m[-1:, N:, :],
|
|
183
|
+
K_collect_m[-1:, :],
|
|
184
|
+
G_inv_collect_0[-1:, :, :],
|
|
185
|
+
mu_arr
|
|
186
|
+
).ravel()
|
|
187
|
+
)
|
|
174
188
|
|
|
175
189
|
_mathscr_v_contribution = np.concatenate(
|
|
176
190
|
[
|
|
@@ -17,9 +17,9 @@ def _solve_for_gen_and_part_sols(
|
|
|
17
17
|
there_is_iso_source, # Is there an isotropic source?
|
|
18
18
|
):
|
|
19
19
|
"""
|
|
20
|
-
|
|
21
|
-
and the eigenpairs
|
|
22
|
-
|
|
20
|
+
Diagonalizes the coefficient matrix of the system of ordinary differential equations (ODEs)
|
|
21
|
+
for each Fourier mode and returns the eigenpairs which give the general solution up to unknown coefficients.
|
|
22
|
+
Also solves for the particular solution to each system of ODEs and returns its coefficient vector.
|
|
23
23
|
This function is wrapped by the `_assemble_intensity_and_fluxes` function.
|
|
24
24
|
It has many seemingly redundant arguments to maximize precomputation in the `pydisort` function.
|
|
25
25
|
See the Jupyter Notebook, especially section 3, for documentation, explanation and derivation.
|
|
@@ -46,16 +46,18 @@ def _solve_for_gen_and_part_sols(
|
|
|
46
46
|
| `there_is_iso_source` | boolean |
|
|
47
47
|
|
|
48
48
|
Notable internal variables of _solve_for_gen_and_part_sols
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
| `ells_all`
|
|
52
|
-
| `G_collect`
|
|
53
|
-
| `K_collect`
|
|
54
|
-
| `alpha_arr`
|
|
55
|
-
| `beta_arr`
|
|
56
|
-
| `X_tilde_arr`
|
|
57
|
-
| `B_collect`
|
|
58
|
-
| `
|
|
49
|
+
| Variable | Type / Shape |
|
|
50
|
+
| ------------------- | -------------------------------------- |
|
|
51
|
+
| `ells_all` | `NLeg` |
|
|
52
|
+
| `G_collect` | `NFourier*NLayers x NQuad x NQuad` | Reshaped to NFourier x NLayers x NQuad x NQuad
|
|
53
|
+
| `K_collect` | `NFourier*NLayers x NQuad` | Reshaped to NFourier x NLayers x NQuad
|
|
54
|
+
| `alpha_arr` | `NFourier*NLayers x NQuad/2 x NQuad/2` | Reshaped to NFourier x NLayers x NQuad/2 x NQuad/2
|
|
55
|
+
| `beta_arr` | `NFourier*NLayers x NQuad/2 x NQuad/2` | Reshaped to NFourier x NLayers x NQuad/2 x NQuad/2
|
|
56
|
+
| `X_tilde_arr` | `NFourier*NLayers x NQuad` | Reshaped to NFourier x NLayers x NQuad
|
|
57
|
+
| `B_collect` | `NFourier*NLayers x NQuad` or `None` | Reshaped to NFourier x NLayers x NQuad
|
|
58
|
+
| `eigenvecs_GpG_arr` | `NFourier*NLayers x NQuad/2 x NQuad |
|
|
59
|
+
| `eigenvecs_GmG_arr` | `NFourier*NLayers x NQuad/2 x NQuad |
|
|
60
|
+
| `G_inv_collect_0` | `NLayers x NQuad x NQuad` or `None` |
|
|
59
61
|
|
|
60
62
|
"""
|
|
61
63
|
############################### Assemble system and diagonalize coefficient matrix #########################################
|
|
@@ -109,7 +111,7 @@ def _solve_for_gen_and_part_sols(
|
|
|
109
111
|
if np.any(weighted_asso_Leg_coeffs_l > 0) and np.all(
|
|
110
112
|
np.isfinite(asso_leg_term_pos)
|
|
111
113
|
):
|
|
112
|
-
# Generate
|
|
114
|
+
# Generate D and X (phase function terms)
|
|
113
115
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
114
116
|
D_temp = weighted_asso_Leg_coeffs_l[None, :] * asso_leg_term_pos.T
|
|
115
117
|
D_pos = (scaled_omega_l / 2) * D_temp @ asso_leg_term_pos
|
|
@@ -190,7 +192,7 @@ def _solve_for_gen_and_part_sols(
|
|
|
190
192
|
G_inv_collect_0[no_shortcut_indices_0, :, :] = G_inv_arr[: len(no_shortcut_indices_0), :, :]
|
|
191
193
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
192
194
|
|
|
193
|
-
# Particular solution for the
|
|
195
|
+
# Particular solution for the direct beam source (refer to Section 3.6.1 of the Comprehensive Documentation)
|
|
194
196
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
195
197
|
if there_is_beam_source:
|
|
196
198
|
X_tilde_arr = X_tilde_arr[: len(no_shortcut_indices), :]
|
PythonicDISORT/pydisort.py
CHANGED
|
@@ -28,6 +28,7 @@ def pydisort(
|
|
|
28
28
|
"""Solves the 1D RTE for the fluxes, and optionally intensity,
|
|
29
29
|
of a multi-layer atmosphere with the specified optical properties, boundary conditions
|
|
30
30
|
and sources. Optionally performs delta-M scaling and NT corrections.
|
|
31
|
+
Refer to the `*_test.ipynb` Jupyter Notebooks in the `pydisotest` directory for examples of use.
|
|
31
32
|
|
|
32
33
|
See https://pythonic-disort.readthedocs.io/en/latest/Pythonic-DISORT.html#1.-USER-INPUT-REQUIRED:-Choose-parameters
|
|
33
34
|
for a more detailed explanation of each parameter.
|
|
@@ -64,7 +65,7 @@ def pydisort(
|
|
|
64
65
|
Dirichlet boundary condition for the downward direction.
|
|
65
66
|
only_flux : optional, bool
|
|
66
67
|
Do NOT compute the intensity function?
|
|
67
|
-
f_arr : optional, array
|
|
68
|
+
f_arr : optional, array or float
|
|
68
69
|
Fractional scattering into peak for each atmospheric layer.
|
|
69
70
|
NT_cor : optional, bool
|
|
70
71
|
Perform Nakajima-Tanaka intensity corrections?
|
|
@@ -79,25 +80,36 @@ def pydisort(
|
|
|
79
80
|
|
|
80
81
|
Returns
|
|
81
82
|
-------
|
|
82
|
-
mu_arr : array
|
|
83
|
+
mu_arr : array or float
|
|
83
84
|
All `mu` (cosine of polar angle) quadrature nodes.
|
|
84
|
-
Fp(tau) : function
|
|
85
|
-
(Energetic) Flux function with argument `tau` (type: array) for positive (upward) `mu` values.
|
|
86
|
-
Returns the diffuse flux magnitudes (type
|
|
85
|
+
Fp(tau) : function
|
|
86
|
+
(Energetic) Flux function with argument `tau` (type: array or float) for positive (upward) `mu` values.
|
|
87
|
+
Returns the diffuse flux magnitudes (same type and size as `tau`).
|
|
88
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
89
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
90
|
+
For example, `Fp(tau0, is_antiderivative_wrt_tau=True) - Fp(0, is_antiderivative_wrt_tau=True)`
|
|
91
|
+
will give the tau-integral of `Fp` from `tau0` to `0`.
|
|
87
92
|
Fm(tau) : function
|
|
88
|
-
(Energetic) Flux function with argument `tau` (type: array) for negative (downward) `mu` values.
|
|
89
|
-
Returns a tuple of the diffuse and direct flux magnitudes respectively
|
|
93
|
+
(Energetic) Flux function with argument `tau` (type: array or float) for negative (downward) `mu` values.
|
|
94
|
+
Returns a tuple of the diffuse and direct flux magnitudes respectively where each entry is of the
|
|
95
|
+
same type and size as `tau`.
|
|
96
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
97
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
90
98
|
u0(tau) : function
|
|
91
|
-
Zeroth Fourier mode of the intensity with argument `tau` (type: array).
|
|
99
|
+
Zeroth Fourier mode of the intensity with argument `tau` (type: array or float).
|
|
92
100
|
Returns an ndarray with axes corresponding to variation with `mu` and `tau` respectively.
|
|
93
101
|
This function is useful for calculating actinic fluxes and other quantities of interest,
|
|
94
102
|
but reclassification of delta-scaled flux and other corrections must be done manually
|
|
95
103
|
(for actinic flux `generate_diff_act_flux_funcs` will automatically perform the reclassification).
|
|
104
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
105
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
96
106
|
u(tau, phi) : function, optional
|
|
97
|
-
Intensity function with arguments `(tau, phi
|
|
107
|
+
Intensity function with arguments `(tau, phi)` each of type array or float.
|
|
98
108
|
Returns an ndarray with axes corresponding to variation with `mu, tau, phi` respectively.
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
Pass `return_Fourier_error = True` (defaults to `False`) to return the
|
|
110
|
+
Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
|
|
111
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
112
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
101
113
|
|
|
102
114
|
"""
|
|
103
115
|
|
|
@@ -310,9 +322,9 @@ def pydisort(
|
|
|
310
322
|
_autograd_compatible,
|
|
311
323
|
)
|
|
312
324
|
|
|
313
|
-
# TMS correction
|
|
325
|
+
# TMS correction for the intensity (see Section 3.7.2)
|
|
314
326
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
315
|
-
def TMS_correction(tau, phi):
|
|
327
|
+
def TMS_correction(tau, phi, is_antiderivative_wrt_tau):
|
|
316
328
|
Ntau = len(tau)
|
|
317
329
|
Nphi = len(phi)
|
|
318
330
|
# Atmospheric layer indices
|
|
@@ -357,20 +369,36 @@ def pydisort(
|
|
|
357
369
|
)
|
|
358
370
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
359
371
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
372
|
+
if is_antiderivative_wrt_tau:
|
|
373
|
+
TMS_correction_pos = (
|
|
374
|
+
np.exp(-scaled_tau / mu0)[None, :] / (-scale_tau[None, l] / mu0)
|
|
375
|
+
- np.exp(
|
|
376
|
+
(scaled_tau - scaled_tau_arr_l)[None, :] / mu_arr_pos[:, None]
|
|
377
|
+
- scaled_tau_arr_l[None, :] / mu0
|
|
378
|
+
) / (scale_tau[None, l] / mu_arr_pos[:, None])
|
|
365
379
|
)
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
-
|
|
380
|
+
TMS_correction_neg = (
|
|
381
|
+
np.exp(-scaled_tau / mu0)[None, :] / (-scale_tau[None, l] / mu0)
|
|
382
|
+
- np.exp(
|
|
383
|
+
(scaled_tau_arr_lm1 - scaled_tau)[None, :] / mu_arr_pos[:, None]
|
|
384
|
+
- scaled_tau_arr_lm1[None, :] / mu0
|
|
385
|
+
) / (-scale_tau[None, l] / mu_arr_pos[:, None])
|
|
386
|
+
)
|
|
387
|
+
else:
|
|
388
|
+
TMS_correction_pos = (
|
|
389
|
+
np.exp(-scaled_tau / mu0)[None, :]
|
|
390
|
+
- np.exp(
|
|
391
|
+
(scaled_tau - scaled_tau_arr_l)[None, :] / mu_arr_pos[:, None]
|
|
392
|
+
- scaled_tau_arr_l[None, :] / mu0
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
TMS_correction_neg = (
|
|
396
|
+
np.exp(-scaled_tau / mu0)[None, :]
|
|
397
|
+
- np.exp(
|
|
398
|
+
(scaled_tau_arr_lm1 - scaled_tau)[None, :] / mu_arr_pos[:, None]
|
|
399
|
+
- scaled_tau_arr_lm1[None, :] / mu0
|
|
400
|
+
)
|
|
372
401
|
)
|
|
373
|
-
)
|
|
374
402
|
|
|
375
403
|
## Contribution from other layers
|
|
376
404
|
# TODO: Despite being vectorized and despite many attempts at further optimization,
|
|
@@ -392,72 +420,114 @@ def pydisort(
|
|
|
392
420
|
|
|
393
421
|
layers_arr_repeat = np.repeat(layers_arr, Ntau)
|
|
394
422
|
scaled_tau_tile = np.tile(scaled_tau, NLayers)
|
|
395
|
-
scaled_tau_arr_with_0_repeat = np.repeat(scaled_tau_arr_with_0, Ntau)
|
|
396
423
|
|
|
397
424
|
if any_pos_contribution:
|
|
398
425
|
contribution_from_each_other_layer_pos = np.zeros((N, NLayers * Ntau, Nphi))
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
]
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
426
|
+
scaled_tau_tile_pos = scaled_tau_tile[pos_contribution_mask]
|
|
427
|
+
|
|
428
|
+
layers_arr_repeat_pos = layers_arr_repeat[pos_contribution_mask]
|
|
429
|
+
mathscr_B_repeat_pos = mathscr_B[:N, layers_arr_repeat_pos, :]
|
|
430
|
+
scaled_tau_arr_with_0_l_repeat_pos = scaled_tau_arr_with_0[:-1][layers_arr_repeat_pos]
|
|
431
|
+
scaled_tau_arr_with_0_lp1_repeat_pos = scaled_tau_arr_with_0[1:][layers_arr_repeat_pos]
|
|
432
|
+
|
|
433
|
+
if is_antiderivative_wrt_tau:
|
|
434
|
+
scale_tau_repeat_pos = scale_tau[layers_arr_repeat_pos]
|
|
435
|
+
contribution_from_each_other_layer_pos[:, pos_contribution_mask, :] = (
|
|
436
|
+
mathscr_B_repeat_pos
|
|
437
|
+
* (
|
|
438
|
+
(
|
|
439
|
+
np.exp(
|
|
440
|
+
(scaled_tau_tile_pos - scaled_tau_arr_with_0_l_repeat_pos)
|
|
441
|
+
/ mu_arr_pos[:, None]
|
|
442
|
+
- scaled_tau_arr_with_0_l_repeat_pos / mu0
|
|
443
|
+
)
|
|
444
|
+
- np.exp(
|
|
445
|
+
(scaled_tau_tile_pos - scaled_tau_arr_with_0_lp1_repeat_pos)
|
|
446
|
+
/ mu_arr_pos[:, None]
|
|
447
|
+
- scaled_tau_arr_with_0_lp1_repeat_pos / mu0
|
|
448
|
+
)
|
|
449
|
+
)
|
|
450
|
+
/ (scale_tau_repeat_pos / mu_arr_pos[:, None])
|
|
451
|
+
)[:, :, None]
|
|
452
|
+
)
|
|
453
|
+
contribution_from_other_layers_pos = np.sum(
|
|
454
|
+
contribution_from_each_other_layer_pos.reshape((N, NLayers, Ntau, Nphi)),
|
|
455
|
+
axis=1,
|
|
456
|
+
)
|
|
457
|
+
else:
|
|
458
|
+
contribution_from_each_other_layer_pos[:, pos_contribution_mask, :] = (
|
|
459
|
+
mathscr_B_repeat_pos
|
|
460
|
+
* (
|
|
461
|
+
np.exp(
|
|
462
|
+
(scaled_tau_tile_pos - scaled_tau_arr_with_0_l_repeat_pos)
|
|
463
|
+
/ mu_arr_pos[:, None]
|
|
464
|
+
- scaled_tau_arr_with_0_l_repeat_pos / mu0
|
|
465
|
+
)
|
|
466
|
+
- np.exp(
|
|
467
|
+
(scaled_tau_tile_pos - scaled_tau_arr_with_0_lp1_repeat_pos)
|
|
468
|
+
/ mu_arr_pos[:, None]
|
|
469
|
+
- scaled_tau_arr_with_0_lp1_repeat_pos / mu0
|
|
470
|
+
)
|
|
471
|
+
)[:, :, None]
|
|
472
|
+
)
|
|
473
|
+
contribution_from_other_layers_pos = np.sum(
|
|
474
|
+
contribution_from_each_other_layer_pos.reshape((N, NLayers, Ntau, Nphi)),
|
|
475
|
+
axis=1,
|
|
476
|
+
)
|
|
477
|
+
|
|
429
478
|
if any_neg_contribution:
|
|
430
479
|
contribution_from_each_other_layer_neg = np.zeros((N, NLayers * Ntau, Nphi))
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
]
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
]
|
|
438
|
-
scaled_tau_arr_with_0_lp1_tile_negmasked = scaled_tau_arr_with_0_repeat[Ntau:][
|
|
439
|
-
neg_contribution_mask
|
|
440
|
-
]
|
|
480
|
+
scaled_tau_tile_neg = scaled_tau_tile[neg_contribution_mask]
|
|
481
|
+
|
|
482
|
+
layers_arr_repeat_neg = layers_arr_repeat[neg_contribution_mask]
|
|
483
|
+
mathscr_B_repeat_neg = mathscr_B[N:, layers_arr_repeat_neg, :]
|
|
484
|
+
scaled_tau_arr_with_0_l_repeat_neg = scaled_tau_arr_with_0[:-1][layers_arr_repeat_neg]
|
|
485
|
+
scaled_tau_arr_with_0_lp1_repeat_neg = scaled_tau_arr_with_0[1:][layers_arr_repeat_neg]
|
|
441
486
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
487
|
+
if is_antiderivative_wrt_tau:
|
|
488
|
+
scale_tau_repeat_neg = scale_tau[layers_arr_repeat_neg]
|
|
489
|
+
contribution_from_each_other_layer_neg[:, neg_contribution_mask, :] = (
|
|
490
|
+
mathscr_B_repeat_neg
|
|
491
|
+
* (
|
|
492
|
+
(
|
|
493
|
+
np.exp(
|
|
494
|
+
(scaled_tau_arr_with_0_lp1_repeat_neg - scaled_tau_tile_neg)
|
|
495
|
+
/ mu_arr_pos[:, None]
|
|
496
|
+
- scaled_tau_arr_with_0_lp1_repeat_neg / mu0
|
|
497
|
+
)
|
|
498
|
+
- np.exp(
|
|
499
|
+
(scaled_tau_arr_with_0_l_repeat_neg - scaled_tau_tile_neg)
|
|
500
|
+
/ mu_arr_pos[:, None]
|
|
501
|
+
- scaled_tau_arr_with_0_l_repeat_neg / mu0
|
|
502
|
+
)
|
|
503
|
+
)
|
|
504
|
+
/ (-scale_tau_repeat_pos / mu_arr_pos[:, None])
|
|
505
|
+
)[:, :, None]
|
|
506
|
+
)
|
|
507
|
+
contribution_from_other_layers_neg = np.sum(
|
|
508
|
+
contribution_from_each_other_layer_neg.reshape((N, NLayers, Ntau, Nphi)),
|
|
509
|
+
axis=1,
|
|
510
|
+
)
|
|
511
|
+
else:
|
|
512
|
+
contribution_from_each_other_layer_neg[:, neg_contribution_mask, :] = (
|
|
513
|
+
mathscr_B_repeat_neg
|
|
514
|
+
* (
|
|
515
|
+
np.exp(
|
|
516
|
+
(scaled_tau_arr_with_0_lp1_repeat_neg - scaled_tau_tile_neg)
|
|
517
|
+
/ mu_arr_pos[:, None]
|
|
518
|
+
- scaled_tau_arr_with_0_lp1_repeat_neg / mu0
|
|
519
|
+
)
|
|
520
|
+
- np.exp(
|
|
521
|
+
(scaled_tau_arr_with_0_l_repeat_neg - scaled_tau_tile_neg)
|
|
522
|
+
/ mu_arr_pos[:, None]
|
|
523
|
+
- scaled_tau_arr_with_0_l_repeat_neg / mu0
|
|
524
|
+
)
|
|
525
|
+
)[:, :, None]
|
|
526
|
+
)
|
|
527
|
+
contribution_from_other_layers_neg = np.sum(
|
|
528
|
+
contribution_from_each_other_layer_neg.reshape((N, NLayers, Ntau, Nphi)),
|
|
529
|
+
axis=1,
|
|
530
|
+
)
|
|
461
531
|
|
|
462
532
|
if _autograd_compatible:
|
|
463
533
|
if not any_pos_contribution:
|
|
@@ -481,7 +551,7 @@ def pydisort(
|
|
|
481
551
|
return solution
|
|
482
552
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
483
553
|
|
|
484
|
-
# IMS correction
|
|
554
|
+
# IMS correction for the intensity (see Section 3.7.2)
|
|
485
555
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
486
556
|
sum1 = np.sum(omega_arr * tau_arr)
|
|
487
557
|
omega_avg = sum1 / np.sum(tau_arr)
|
|
@@ -495,15 +565,22 @@ def pydisort(
|
|
|
495
565
|
)
|
|
496
566
|
scaled_mu0 = mu0 / (1 - omega_avg * f_avg)
|
|
497
567
|
|
|
498
|
-
def IMS_correction(tau, phi):
|
|
568
|
+
def IMS_correction(tau, phi, is_antiderivative_wrt_tau):
|
|
499
569
|
nu = subroutines.atleast_2d_append(
|
|
500
570
|
subroutines.calculate_nu(-mu_arr_pos, phi, -mu0, phi0)
|
|
501
571
|
)
|
|
502
572
|
x = 1 / mu_arr_pos - 1 / scaled_mu0
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
573
|
+
if is_antiderivative_wrt_tau:
|
|
574
|
+
chi = (
|
|
575
|
+
(scaled_mu0 - x[:, None] * scaled_mu0 * (scaled_mu0 + tau)[None, :])
|
|
576
|
+
* np.exp(-tau / scaled_mu0)[None, :]
|
|
577
|
+
- mu_arr_pos[:, None] * np.exp(-tau[None, :] / mu_arr_pos[:, None])
|
|
578
|
+
) / (mu_arr_pos * scaled_mu0 * x**2)[:, None]
|
|
579
|
+
else:
|
|
580
|
+
chi = (
|
|
581
|
+
(tau[None, :] - 1 / x[:, None]) * np.exp(-tau / scaled_mu0)[None, :]
|
|
582
|
+
+ np.exp(-tau[None, :] / mu_arr_pos[:, None]) / x[:, None]
|
|
583
|
+
) / (mu_arr_pos * scaled_mu0 * x)[:, None]
|
|
507
584
|
return (
|
|
508
585
|
I0
|
|
509
586
|
/ (4 * pi)
|
|
@@ -518,26 +595,26 @@ def pydisort(
|
|
|
518
595
|
|
|
519
596
|
# The corrected intensity
|
|
520
597
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
521
|
-
def u_corrected(tau, phi, return_Fourier_error=False):
|
|
598
|
+
def u_corrected(tau, phi, return_Fourier_error=False, is_antiderivative_wrt_tau=False):
|
|
522
599
|
tau = np.atleast_1d(tau)
|
|
523
600
|
phi = np.atleast_1d(phi)
|
|
524
|
-
NT_corrections = TMS_correction(tau, phi)
|
|
601
|
+
NT_corrections = TMS_correction(tau, phi, is_antiderivative_wrt_tau)
|
|
525
602
|
|
|
526
603
|
if _autograd_compatible:
|
|
527
604
|
NT_corrections = NT_corrections + np.concatenate(
|
|
528
|
-
[np.zeros((N, len(tau), len(phi))), IMS_correction(tau, phi)], axis=0
|
|
605
|
+
[np.zeros((N, len(tau), len(phi))), IMS_correction(tau, phi, is_antiderivative_wrt_tau)], axis=0
|
|
529
606
|
)
|
|
530
607
|
else:
|
|
531
|
-
NT_corrections[N:, :, :] += IMS_correction(tau, phi)
|
|
608
|
+
NT_corrections[N:, :, :] += IMS_correction(tau, phi, is_antiderivative_wrt_tau)
|
|
532
609
|
|
|
533
610
|
if return_Fourier_error:
|
|
534
|
-
u_star_outputs = u_star(tau, phi, True)
|
|
611
|
+
u_star_outputs = u_star(tau, phi, True, is_antiderivative_wrt_tau)
|
|
535
612
|
return (
|
|
536
613
|
u_star_outputs[0] + I0_orig * np.squeeze(NT_corrections),
|
|
537
614
|
u_star_outputs[1],
|
|
538
615
|
)
|
|
539
616
|
else:
|
|
540
|
-
return u_star(tau, phi, False) + I0_orig * np.squeeze(NT_corrections)
|
|
617
|
+
return u_star(tau, phi, False, is_antiderivative_wrt_tau) + I0_orig * np.squeeze(NT_corrections)
|
|
541
618
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
542
619
|
|
|
543
620
|
return mu_arr, flux_up, flux_down, u0, u_corrected
|
PythonicDISORT/subroutines.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import scipy as sc
|
|
3
3
|
from math import pi
|
|
4
|
-
from numpy.polynomial.legendre import leggauss
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
def transform_interval(arr, c, d, a, b):
|
|
@@ -71,7 +70,7 @@ def calculate_nu(mu, phi, mu_p, phi_p):
|
|
|
71
70
|
|
|
72
71
|
Returns
|
|
73
72
|
-------
|
|
74
|
-
ndarray
|
|
73
|
+
nu : ndarray
|
|
75
74
|
Cosine of scattering angles which axes capture variation with `mu, phi, mu_p, phi_p` respectively.
|
|
76
75
|
|
|
77
76
|
"""
|
|
@@ -104,7 +103,7 @@ def Gauss_Legendre_quad(N, c=0, d=1):
|
|
|
104
103
|
Quadrature weights.
|
|
105
104
|
|
|
106
105
|
"""
|
|
107
|
-
mu_arr_pos, W = leggauss(int(N))
|
|
106
|
+
mu_arr_pos, W = np.polynomial.legendre.leggauss(int(N))
|
|
108
107
|
|
|
109
108
|
return transform_interval(mu_arr_pos, c, d, -1, 1), transform_weights(W, c, d, -1, 1)
|
|
110
109
|
|
|
@@ -224,8 +223,10 @@ def atleast_2d_append(*arys):
|
|
|
224
223
|
|
|
225
224
|
def generate_diff_act_flux_funcs(u0):
|
|
226
225
|
"""Generates the up and down diffuse actinic flux functions respectively.
|
|
227
|
-
This is a use case of the ``u0`` function
|
|
226
|
+
This is a use case of the ``u0`` function which is an output of ``pydisort``.
|
|
228
227
|
The reclassification of delta-scaled actinic flux is automatically performed.
|
|
228
|
+
The computation of actinic fluxes is similar to that of energetic fluxes
|
|
229
|
+
and the latter is discussed in section 3.8 of the Comprehensive Documentation.
|
|
229
230
|
|
|
230
231
|
Parameters
|
|
231
232
|
----------
|
|
@@ -235,36 +236,102 @@ def generate_diff_act_flux_funcs(u0):
|
|
|
235
236
|
|
|
236
237
|
Returns
|
|
237
238
|
-------
|
|
238
|
-
function
|
|
239
|
+
Fp_act(tau) : function
|
|
239
240
|
Actinic flux function with argument `tau` (type: array) for positive (upward) `mu` values.
|
|
240
241
|
Returns the diffuse flux magnitudes (type: array).
|
|
241
|
-
|
|
242
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
243
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
244
|
+
Fm_act(tau) : function
|
|
242
245
|
Actinic flux function with argument `tau` (type: array) for negative (downward) `mu` values.
|
|
243
246
|
Returns the diffuse flux magnitudes (type: array).
|
|
247
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
248
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
244
249
|
|
|
245
250
|
"""
|
|
246
251
|
N = np.shape(u0(0))[0] // 2
|
|
247
252
|
GL_weights = Gauss_Legendre_quad(N, 0, 1)[1]
|
|
248
253
|
|
|
249
254
|
# Note that the zeroth axis of the array u0(tau) captures variation with mu
|
|
250
|
-
flux_act_up =
|
|
251
|
-
|
|
252
|
-
|
|
255
|
+
def flux_act_up(tau, is_antiderivative_wrt_tau=False):
|
|
256
|
+
return np.squeeze(2 * pi * GL_weights @ u0(tau, is_antiderivative_wrt_tau)[:N])[()]
|
|
257
|
+
|
|
258
|
+
def flux_act_down_diffuse(tau, is_antiderivative_wrt_tau=False):
|
|
259
|
+
u0_cache, act_dscale_reclassification = u0(tau, is_antiderivative_wrt_tau, _return_act_dscale_for_reclass=True)
|
|
253
260
|
result_without_reclassification = 2 * pi * GL_weights @ u0_cache[N:]
|
|
254
|
-
return result_without_reclassification + act_dscale_reclassification
|
|
261
|
+
return np.squeeze(result_without_reclassification + act_dscale_reclassification)[()]
|
|
255
262
|
|
|
256
263
|
return flux_act_up, flux_act_down_diffuse
|
|
257
264
|
|
|
258
265
|
|
|
259
|
-
def
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
266
|
+
def interpolate(mu_arr_pos, u):
|
|
267
|
+
"""Polynomial (Barycentric) interpolation with respect to mu. The output
|
|
268
|
+
is a function that is continuous and variable in all three arguments: mu, tau and phi.
|
|
269
|
+
Discussed in sections 3.7 and 6.3 in the Comprehensive Documentation.
|
|
270
|
+
|
|
271
|
+
Parameters
|
|
272
|
+
----------
|
|
273
|
+
mu_arr_pos : array
|
|
274
|
+
Positive `mu` (cosine of polar angle) quadrature nodes.
|
|
275
|
+
Equivalent to `mu_arr[:N]`.
|
|
276
|
+
u : function
|
|
277
|
+
Non-interpolated intensity function as outputted by `pydisort`.
|
|
278
|
+
|
|
279
|
+
Returns
|
|
280
|
+
-------
|
|
281
|
+
u_interpol : function
|
|
282
|
+
Intensity function with arguments `(mu, tau, phi)` each of type array or float.
|
|
283
|
+
Returns an ndarray with axes corresponding to variation with `mu, tau, phi` respectively.
|
|
284
|
+
Pass `return_Fourier_error = True` (defaults to `False`) to return the
|
|
285
|
+
Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
|
|
286
|
+
Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
|
|
287
|
+
to switch to an antiderivative of the function with respect to `tau`.
|
|
288
|
+
|
|
289
|
+
"""
|
|
290
|
+
N = len(mu_arr_pos)
|
|
291
|
+
u_pos_interpol = sc.interpolate.BarycentricInterpolator(mu_arr_pos)
|
|
292
|
+
u_neg_interpol = sc.interpolate.BarycentricInterpolator(-mu_arr_pos)
|
|
293
|
+
|
|
294
|
+
def u_interpol(mu, tau, phi, return_Fourier_error=False, is_antiderivative_wrt_tau=False):
|
|
295
|
+
assert np.all(np.abs(mu) <= 1)
|
|
296
|
+
mu = np.atleast_1d(mu)
|
|
297
|
+
|
|
298
|
+
if return_Fourier_error:
|
|
299
|
+
u_cache, Fourier_error = u(tau, phi, True, is_antiderivative_wrt_tau)
|
|
300
|
+
else:
|
|
301
|
+
u_cache = u(tau, phi, False, is_antiderivative_wrt_tau)
|
|
302
|
+
|
|
303
|
+
u_shape = np.shape(u_cache)
|
|
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:])
|
|
307
|
+
mask_pos = mu > 0
|
|
308
|
+
mask_else = ~mask_pos
|
|
309
|
+
|
|
310
|
+
if np.any(mask_pos):
|
|
311
|
+
u_pos_interpol.set_yi(u_cache[:N])
|
|
312
|
+
results[mask_pos] = u_pos_interpol(mu[mask_pos])
|
|
313
|
+
if np.any(mask_else):
|
|
314
|
+
u_neg_interpol.set_yi(u_cache[N:])
|
|
315
|
+
results[mask_else] = u_neg_interpol(mu[mask_else])
|
|
316
|
+
|
|
317
|
+
if return_Fourier_error:
|
|
318
|
+
return np.squeeze(results)[()], Fourier_error
|
|
319
|
+
else:
|
|
320
|
+
return np.squeeze(results)[()]
|
|
321
|
+
|
|
322
|
+
return u_interpol
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _mathscr_v(tau, # Input optical depths
|
|
326
|
+
l, # Layer index of each input optical depth
|
|
327
|
+
s_poly_coeffs, # Polynomial coefficients of isotropic source
|
|
328
|
+
Nscoeffs, # Number of isotropic source polynomial coefficients
|
|
329
|
+
G, # Eigenvector matrices
|
|
330
|
+
K, # Eigenvalues
|
|
331
|
+
G_inv, # Inverse of eigenvector matrix
|
|
332
|
+
mu_arr, # Quadrature nodes for both hemispheres
|
|
333
|
+
is_antiderivative_wrt_tau=False, # Switch to an antiderivative of the function?
|
|
334
|
+
_autograd_compatible=False, # Should the output functions be compatible with autograd?
|
|
268
335
|
):
|
|
269
336
|
"""Particular solution for isotropic internal sources.
|
|
270
337
|
Refer to Section 3.6.1 of the Comprehensive Documentation.
|
|
@@ -273,16 +340,17 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
273
340
|
and `_solve_for_coeffs` functions which call it.
|
|
274
341
|
|
|
275
342
|
Arguments of _mathscr_v
|
|
276
|
-
|
|
|
277
|
-
|
|
|
278
|
-
| `tau`
|
|
279
|
-
| `l`
|
|
280
|
-
| `s_poly_coeffs`
|
|
281
|
-
| `G`
|
|
282
|
-
| `K`
|
|
283
|
-
| `G_inv`
|
|
284
|
-
| `mu_arr`
|
|
285
|
-
| `
|
|
343
|
+
| Variable | Shape |
|
|
344
|
+
| --------------------------- | ------------------------------------- |
|
|
345
|
+
| `tau` | `Ntau` |
|
|
346
|
+
| `l` | `Ntau` |
|
|
347
|
+
| `s_poly_coeffs` | `NLayers x Nscoeffs` or `Nscoeffs` |
|
|
348
|
+
| `G` | `NLayers<= x NQuad x NQuad` |
|
|
349
|
+
| `K` | `NLayers<= x NQuad` |
|
|
350
|
+
| `G_inv` | `NLayers<= x NQuad x NQuad` or `None` |
|
|
351
|
+
| `mu_arr` | `NQuad` |
|
|
352
|
+
| `is_antiderivative_wrt_tau` | boolean |
|
|
353
|
+
| `_autograd_compatible` | boolean |
|
|
286
354
|
|
|
287
355
|
Notable internal variables of _mathscr_v
|
|
288
356
|
| Variable | Shape |
|
|
@@ -340,11 +408,17 @@ def _mathscr_v(tau, # Input optical depths
|
|
|
340
408
|
0,
|
|
341
409
|
),
|
|
342
410
|
)
|
|
343
|
-
|
|
411
|
+
|
|
412
|
+
powers = np.flip(np.arange(Nscoeffs))[None, :]
|
|
413
|
+
if is_antiderivative_wrt_tau:
|
|
414
|
+
tau_poly = tau[:, None] ** (powers + 1) / (powers + 1)
|
|
415
|
+
else:
|
|
416
|
+
tau_poly = tau[:, None] ** powers
|
|
417
|
+
|
|
344
418
|
return np.einsum(
|
|
345
419
|
"tik, tc, ctk, tkj, j -> it",
|
|
346
420
|
G[l, :, :],
|
|
347
|
-
|
|
421
|
+
tau_poly,
|
|
348
422
|
mathscr_v_coeffs[:, l, :],
|
|
349
423
|
G_inv[l, :, :],
|
|
350
424
|
1 / mu_arr,
|
|
@@ -365,6 +439,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u):
|
|
|
365
439
|
which are stored in .npz files, against results from PythonicDISORT. Used in our PyTests.
|
|
366
440
|
|
|
367
441
|
"""
|
|
442
|
+
# Load saved results from Stamnes' DISORT
|
|
368
443
|
uu = results["uu"]
|
|
369
444
|
flup = results["flup"]
|
|
370
445
|
rfldn = results["rfldn"]
|
|
@@ -438,6 +513,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u):
|
|
|
438
513
|
diff_tau_pt = tau_test_arr[max_diff_tau_index]
|
|
439
514
|
ratio_tau_pt = tau_test_arr[max_ratio_tau_index]
|
|
440
515
|
print("Intensities")
|
|
516
|
+
print()
|
|
441
517
|
print("At tau = " + str(diff_tau_pt))
|
|
442
518
|
print("Max pointwise difference =", np.max(diff[:, max_diff_tau_index, :]))
|
|
443
519
|
print("At tau = " + str(ratio_tau_pt))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PythonicDISORT
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.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,45 @@ Provides-Extra: pytest
|
|
|
20
20
|
Requires-Dist: pytest >=6.2.5 ; extra == 'pytest'
|
|
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
|
|
30
|
-
Dirichlet boundary conditions (diffuse flux boundary sources), Bi-Directional Reflectance Function (BDRF) for surface reflection,
|
|
31
|
-
|
|
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),
|
|
31
|
+
Dirichlet boundary conditions (diffuse flux boundary sources), Bi-Directional Reflectance Function (BDRF) for surface reflection,
|
|
32
|
+
interpolation with respect to polar angle and more.
|
|
33
|
+
In addition, we added a subroutine to calculate actinic fluxes to satisfy a user request, and integration with respect to tau was also added.
|
|
34
|
+
Further feature requests as well as feedback are welcome.
|
|
32
35
|
|
|
33
36
|
You may contact me, Dion, through dh3065@columbia.edu.
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
The original was downloaded from http://www.rtatmocn.com/disort/. The wrapper is inspired by https://github.com/kconnour/pyRT_DISORT.
|
|
38
|
+
The **GitHub repository** is https://github.com/LDEO-CREW/Pythonic-DISORT.
|
|
37
39
|
|
|
38
40
|
# Documentation
|
|
39
41
|
https://pythonic-disort.readthedocs.io/en/latest/
|
|
40
42
|
|
|
41
43
|
Also see the accompanying Jupyter Notebook `Pythonic-DISORT.ipynb` in the `docs` directory
|
|
42
44
|
of our GitHub repository.
|
|
43
|
-
|
|
45
|
+
This Jupyter Notebook provides comprehensive documentation, suggested inputs, explanations,
|
|
44
46
|
mathematical derivations and verification tests.
|
|
45
|
-
|
|
47
|
+
It is highly recommended that new users read the non-optional parts of sections 1 and 2.
|
|
46
48
|
|
|
47
49
|
## PyTest and examples of how to use PythonicDISORT
|
|
48
50
|
|
|
49
|
-
Not only
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
Not only are there verification tests in `Pythonic-DISORT.ipynb`,
|
|
52
|
+
most of the test problems in Stamnes' `disotest.f90` (download DISORT 4.0.99 from http://www.rtatmocn.com/disort/) have also been recreated.
|
|
53
|
+
In these tests, the solutions from PythonicDISORT are compared against solutions
|
|
54
|
+
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`
|
|
55
|
+
in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks to show the implementation of each test.
|
|
56
|
+
These notebooks double up as examples of how to use PythonicDISORT.
|
|
54
57
|
|
|
55
58
|
# Installation
|
|
56
59
|
|
|
57
60
|
* From PyPI: `pip install PythonicDISORT`
|
|
58
|
-
* From Conda-forge: (TODO:
|
|
61
|
+
* From Conda-forge: (TODO: need to first publish on Conda-forge)
|
|
59
62
|
* 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
63
|
|
|
61
64
|
## Requirements to run PythonicDISORT
|
|
@@ -64,21 +67,18 @@ to show how the tests were implemented. These notebooks double up as examples of
|
|
|
64
67
|
* `scipy >= 1.8.0`
|
|
65
68
|
* (OPTIONAL) `pytest >= 6.2.5` (Required to use the command `pytest`, see *PyTest and examples of how to use PythonicDISORT*)
|
|
66
69
|
|
|
67
|
-
## Additional requirements to run the Jupyter Notebook
|
|
70
|
+
## (OPTIONAL) Additional requirements to run the Jupyter Notebook
|
|
68
71
|
* `autograd >= 1.5`
|
|
69
72
|
* `jupyter > 1.0.0`
|
|
70
73
|
* `notebook > 6.5.2`
|
|
74
|
+
* `matplotlib >= 3.6.0`
|
|
71
75
|
|
|
72
|
-
In addition,
|
|
73
|
-
must be set up to run the last section (section 6).
|
|
76
|
+
In addition, a F2PY-wrapped Stamnes' DISORT, or equivalent, is required to properly run the last section (section 6).
|
|
74
77
|
|
|
75
78
|
## Compatibility
|
|
76
79
|
|
|
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.
|
|
80
|
+
The PythonicDISORT package should be system agnostic given its minimal dependencies and pure Python code.
|
|
81
|
+
Everything in the repository was built and tested on Windows 11.
|
|
82
82
|
|
|
83
83
|
# Acknowledgements
|
|
84
84
|
|
|
@@ -106,6 +106,8 @@ Science and Technology Center (STC) (Award #2019625) under which this package wa
|
|
|
106
106
|
|
|
107
107
|
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
108
|
|
|
109
|
-
11)
|
|
109
|
+
11) Knut Stamnes. 1982. *On the computation of angular distributions of radiation in planetary atmospheres.* https://www.sciencedirect.com/science/article/pii/0022407382900966.
|
|
110
110
|
|
|
111
|
-
12)
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
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.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
PythonicDISORT/__init__.py,sha256=V1obNzaFgqInxqYJ5h_r6JHbZRxx4VM5VgzCIS6EEzI,84
|
|
2
|
+
PythonicDISORT/_assemble_intensity_and_fluxes.py,sha256=SFX5SMUhKNt_MU-0t3kv5lLXxc1EzVK2EIDLnggoIEw,27206
|
|
3
|
+
PythonicDISORT/_solve_for_coeffs.py,sha256=ZGThwif73zAlhzXYdVzV2nJxdxRioq4MIFsni3_5-Hc,19594
|
|
4
|
+
PythonicDISORT/_solve_for_gen_and_part_sols.py,sha256=9ScaxY7SH3lYaE2UBnEIsmsDeXdjcf74_QWaeqyOIRs,12973
|
|
5
|
+
PythonicDISORT/pydisort.py,sha256=HmAdvbDHQoHQy1xXToJmT3X5MGRudMKoI0L7zAaOd-8,33188
|
|
6
|
+
PythonicDISORT/subroutines.py,sha256=2cJLQc46ctAiqY_qARlsxQZPYVcBOwkXf0-diCtnGXo,19446
|
|
7
|
+
PythonicDISORT-0.9.0.dist-info/LICENSE.md,sha256=q1i1QVh0OLzK0fj6kaNRNWsM_KZca66JwWgWwbuCPlA,1092
|
|
8
|
+
PythonicDISORT-0.9.0.dist-info/METADATA,sha256=dLzNm79C3rWzVMzC2GIboGpMloxs4Fiy3RpQgOMIMP4,7102
|
|
9
|
+
PythonicDISORT-0.9.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
10
|
+
PythonicDISORT-0.9.0.dist-info/top_level.txt,sha256=aX6kCeI1E7naZz3xfw4_mIbWh5KBh9dmF3FIvgP5m3Y,15
|
|
11
|
+
PythonicDISORT-0.9.0.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
PythonicDISORT/__init__.py,sha256=V1obNzaFgqInxqYJ5h_r6JHbZRxx4VM5VgzCIS6EEzI,84
|
|
2
|
-
PythonicDISORT/_assemble_intensity_and_fluxes.py,sha256=IH1OYHfMLh1LrNiwSCnaJduELvHpStJgXqTycQ869X8,20410
|
|
3
|
-
PythonicDISORT/_solve_for_coeffs.py,sha256=8x7bDARYjS_fnoFGt5LRnm6yUtjyPa_YQ2Mj0LA2AFY,19162
|
|
4
|
-
PythonicDISORT/_solve_for_gen_and_part_sols.py,sha256=okJFq60712DN48-jdodipvP5S11yYK_yobDVpg0kTi8,12841
|
|
5
|
-
PythonicDISORT/pydisort.py,sha256=xjhiRnwmVIPGKJqEuapwLwGLicL3mjizO-w3r5kaP1M,27880
|
|
6
|
-
PythonicDISORT/subroutines.py,sha256=oqPIluJaFF88d7N7iS72wFOKfiYAmT3uA33tpPjuLP0,15857
|
|
7
|
-
PythonicDISORT-0.7.0.dist-info/LICENSE.md,sha256=q1i1QVh0OLzK0fj6kaNRNWsM_KZca66JwWgWwbuCPlA,1092
|
|
8
|
-
PythonicDISORT-0.7.0.dist-info/METADATA,sha256=1V3vyKC8Qa9uEH_WvCfdUnpU28HqOEnb3PdPCIR7Ij4,7230
|
|
9
|
-
PythonicDISORT-0.7.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
10
|
-
PythonicDISORT-0.7.0.dist-info/top_level.txt,sha256=aX6kCeI1E7naZz3xfw4_mIbWh5KBh9dmF3FIvgP5m3Y,15
|
|
11
|
-
PythonicDISORT-0.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|