PythonicDISORT 1.1__tar.gz → 1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pythonicdisort-1.1/src/PythonicDISORT.egg-info → pythonicdisort-1.2}/PKG-INFO +3 -3
- {pythonicdisort-1.1 → pythonicdisort-1.2}/README.md +2 -2
- {pythonicdisort-1.1 → pythonicdisort-1.2}/pyproject.toml +1 -1
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py +103 -90
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT/_solve_for_gen_and_part_sols.py +9 -25
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT/pydisort.py +18 -10
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT/subroutines.py +7 -26
- {pythonicdisort-1.1 → pythonicdisort-1.2/src/PythonicDISORT.egg-info}/PKG-INFO +3 -3
- {pythonicdisort-1.1 → pythonicdisort-1.2}/LICENSE.md +0 -0
- {pythonicdisort-1.1 → pythonicdisort-1.2}/setup.cfg +0 -0
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT/__init__.py +0 -0
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT/_solve_for_coeffs.py +0 -0
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT.egg-info/SOURCES.txt +0 -0
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT.egg-info/dependency_links.txt +0 -0
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT.egg-info/requires.txt +0 -0
- {pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PythonicDISORT
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: Discrete Ordinates Solver for the (1D) Radiative Transfer Equation in a single or multi-layer atmosphere.
|
|
5
5
|
Author-email: Dion HO Jia Xu <dh3065@columbia.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/LDEO-CREW/Pythonic-DISORT
|
|
@@ -67,8 +67,8 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
67
67
|
* Test Problem 5: Cloud C.1 Scattering, Beam Source
|
|
68
68
|
* Test Problem 6: No Scattering, Increasingly Complex Sources (relevant for modeling longwave radiation)
|
|
69
69
|
* Test Problem 7: Absorption + Scattering + All Possible Sources, Lambertian and Hapke Surface Reflectivities (one layer)
|
|
70
|
-
* Test Problem 8: Absorbing / Isotropic-Scattering Medium
|
|
71
|
-
* Test Problem 9: General Emitting / Absorbing / Scattering Medium
|
|
70
|
+
* Test Problem 8: Absorbing / Isotropic-Scattering Medium (multiple layers)
|
|
71
|
+
* Test Problem 9: General Emitting / Absorbing / Scattering Medium (multiple layers)
|
|
72
72
|
* Test Problem 11: Single-Layer vs. Multiple Layers (no corresponding Jupyter Notebook)
|
|
73
73
|
|
|
74
74
|
# Installation
|
|
@@ -46,8 +46,8 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
46
46
|
* Test Problem 5: Cloud C.1 Scattering, Beam Source
|
|
47
47
|
* Test Problem 6: No Scattering, Increasingly Complex Sources (relevant for modeling longwave radiation)
|
|
48
48
|
* Test Problem 7: Absorption + Scattering + All Possible Sources, Lambertian and Hapke Surface Reflectivities (one layer)
|
|
49
|
-
* Test Problem 8: Absorbing / Isotropic-Scattering Medium
|
|
50
|
-
* Test Problem 9: General Emitting / Absorbing / Scattering Medium
|
|
49
|
+
* Test Problem 8: Absorbing / Isotropic-Scattering Medium (multiple layers)
|
|
50
|
+
* Test Problem 9: General Emitting / Absorbing / Scattering Medium (multiple layers)
|
|
51
51
|
* Test Problem 11: Single-Layer vs. Multiple Layers (no corresponding Jupyter Notebook)
|
|
52
52
|
|
|
53
53
|
# Installation
|
{pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT/_assemble_intensity_and_fluxes.py
RENAMED
|
@@ -201,7 +201,13 @@ def _assemble_intensity_and_fluxes(
|
|
|
201
201
|
axis=-1,
|
|
202
202
|
)
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
direct_beam_contribution = 0
|
|
205
|
+
if is_antiderivative_wrt_tau:
|
|
206
|
+
if there_is_beam_source:
|
|
207
|
+
direct_beam_contribution = (B_collect / (-scale_tau / mu0)[None, :, None])[
|
|
208
|
+
:, l, :
|
|
209
|
+
] * np.exp(-scaled_tau / mu0)[None, :, None]
|
|
210
|
+
|
|
205
211
|
um = (
|
|
206
212
|
np.einsum(
|
|
207
213
|
"mtij, mtj -> mti",
|
|
@@ -209,23 +215,19 @@ def _assemble_intensity_and_fluxes(
|
|
|
209
215
|
np.exp(exponent) / (scale_tau[None, :, None] * K_collect)[:, l, :],
|
|
210
216
|
optimize=True,
|
|
211
217
|
)
|
|
212
|
-
+
|
|
213
|
-
* np.exp(-scaled_tau / mu0)[None, :, None]
|
|
214
|
-
)
|
|
215
|
-
elif is_antiderivative_wrt_tau and not there_is_beam_source:
|
|
216
|
-
um = np.einsum(
|
|
217
|
-
"mtij, mtj -> mti",
|
|
218
|
-
GC_collect[:, l, :, :],
|
|
219
|
-
np.exp(exponent) / (scale_tau[None, l, None] * K_collect)[:, l, :],
|
|
220
|
-
optimize=True,
|
|
218
|
+
+ direct_beam_contribution
|
|
221
219
|
)
|
|
222
|
-
elif not is_antiderivative_wrt_tau and there_is_beam_source:
|
|
223
|
-
um = np.einsum(
|
|
224
|
-
"mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
|
|
225
|
-
) + B_collect[:, l, :] * np.exp(-scaled_tau / mu0)[None, :, None]
|
|
226
220
|
else:
|
|
227
|
-
|
|
228
|
-
|
|
221
|
+
if there_is_beam_source:
|
|
222
|
+
direct_beam_contribution = (
|
|
223
|
+
B_collect[:, l, :] * np.exp(-scaled_tau / mu0)[None, :, None]
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
um = (
|
|
227
|
+
np.einsum(
|
|
228
|
+
"mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
|
|
229
|
+
)
|
|
230
|
+
+ direct_beam_contribution
|
|
229
231
|
)
|
|
230
232
|
|
|
231
233
|
# Contribution from particular solution for isotropic internal sources
|
|
@@ -255,6 +257,9 @@ def _assemble_intensity_and_fluxes(
|
|
|
255
257
|
np.cos(np.arange(NFourier)[:, None] * (phi0 - phi)[None, :]),
|
|
256
258
|
optimize=True,
|
|
257
259
|
)
|
|
260
|
+
|
|
261
|
+
outputs = (rescale_factor * np.squeeze(u),)
|
|
262
|
+
outputs_len = 1
|
|
258
263
|
|
|
259
264
|
if return_Fourier_error:
|
|
260
265
|
exponent = np.concatenate(
|
|
@@ -264,7 +269,14 @@ def _assemble_intensity_and_fluxes(
|
|
|
264
269
|
],
|
|
265
270
|
axis=-1,
|
|
266
271
|
)
|
|
267
|
-
|
|
272
|
+
|
|
273
|
+
direct_beam_contribution = 0
|
|
274
|
+
if is_antiderivative_wrt_tau:
|
|
275
|
+
if there_is_beam_source:
|
|
276
|
+
direct_beam_contribution = (
|
|
277
|
+
B_collect.T[-1, :, :] / (-scale_tau / mu0)[None, :]
|
|
278
|
+
)[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
279
|
+
|
|
268
280
|
ulast = (
|
|
269
281
|
np.einsum(
|
|
270
282
|
"tij, tj -> it",
|
|
@@ -272,25 +284,21 @@ def _assemble_intensity_and_fluxes(
|
|
|
272
284
|
np.exp(exponent) / (scale_tau[:, None] * K_collect[-1, :, :])[l, :],
|
|
273
285
|
optimize=True,
|
|
274
286
|
)
|
|
275
|
-
+
|
|
276
|
-
* np.exp(-scaled_tau / mu0)[None, :]
|
|
287
|
+
+ direct_beam_contribution
|
|
277
288
|
)
|
|
278
|
-
elif is_antiderivative_wrt_tau and not there_is_beam_source:
|
|
279
|
-
ulast = np.einsum(
|
|
280
|
-
"tij, tj -> it",
|
|
281
|
-
GC_collect[-1, l, :, :],
|
|
282
|
-
np.exp(exponent) / (scale_tau[:, None] * K_collect[-1, :, :])[l, :],
|
|
283
|
-
optimize=True,
|
|
284
|
-
)
|
|
285
|
-
elif not is_antiderivative_wrt_tau and not there_is_beam_source:
|
|
286
|
-
ulast = np.einsum(
|
|
287
|
-
"tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
|
|
288
|
-
) + B_collect.T[-1, :, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
289
289
|
else:
|
|
290
|
-
|
|
291
|
-
|
|
290
|
+
if there_is_beam_source:
|
|
291
|
+
direct_beam_contribution = (
|
|
292
|
+
B_collect.T[-1, :, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
ulast = (
|
|
296
|
+
np.einsum(
|
|
297
|
+
"tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
|
|
298
|
+
)
|
|
299
|
+
+ direct_beam_contribution
|
|
292
300
|
)
|
|
293
|
-
|
|
301
|
+
|
|
294
302
|
u_abs = np.abs(u)
|
|
295
303
|
Fourier_error = np.max(
|
|
296
304
|
np.divide(
|
|
@@ -299,19 +307,18 @@ def _assemble_intensity_and_fluxes(
|
|
|
299
307
|
),
|
|
300
308
|
u_abs,
|
|
301
309
|
out=np.zeros_like(u_abs),
|
|
302
|
-
where=u_abs > 1e-
|
|
310
|
+
where=u_abs > 1e-8,
|
|
303
311
|
)
|
|
304
312
|
)
|
|
305
313
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return rescale_factor * np.squeeze(u)
|
|
314
|
+
outputs += (Fourier_error,)
|
|
315
|
+
outputs_len += 1
|
|
316
|
+
|
|
317
|
+
if return_tau_arr:
|
|
318
|
+
outputs += (tau_arr,)
|
|
319
|
+
outputs_len += 1
|
|
320
|
+
|
|
321
|
+
return outputs[0] if outputs_len == 1 else outputs
|
|
315
322
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
316
323
|
|
|
317
324
|
# Construct u0
|
|
@@ -363,7 +370,13 @@ def _assemble_intensity_and_fluxes(
|
|
|
363
370
|
axis=-1,
|
|
364
371
|
)
|
|
365
372
|
|
|
373
|
+
direct_beam_contribution = 0
|
|
366
374
|
if is_antiderivative_wrt_tau and there_is_beam_source:
|
|
375
|
+
if there_is_beam_source:
|
|
376
|
+
direct_beam_contribution = (B_collect_0.T / (-scale_tau / mu0)[None, :])[
|
|
377
|
+
:, l
|
|
378
|
+
] * np.exp(-scaled_tau / mu0)[None, :]
|
|
379
|
+
|
|
367
380
|
u0 = (
|
|
368
381
|
np.einsum(
|
|
369
382
|
"tij, tj -> it",
|
|
@@ -371,25 +384,21 @@ def _assemble_intensity_and_fluxes(
|
|
|
371
384
|
np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :],
|
|
372
385
|
optimize=True,
|
|
373
386
|
)
|
|
374
|
-
+
|
|
375
|
-
* np.exp(-scaled_tau / mu0)[None, :]
|
|
376
|
-
)
|
|
377
|
-
elif is_antiderivative_wrt_tau and not there_is_beam_source:
|
|
378
|
-
u0 = np.einsum(
|
|
379
|
-
"tij, tj -> it",
|
|
380
|
-
GC_collect_0[l, :, :],
|
|
381
|
-
np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :],
|
|
382
|
-
optimize=True,
|
|
387
|
+
+ direct_beam_contribution
|
|
383
388
|
)
|
|
384
|
-
elif not is_antiderivative_wrt_tau and there_is_beam_source:
|
|
385
|
-
u0 = np.einsum(
|
|
386
|
-
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
387
|
-
) + B_collect_0.T[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
388
389
|
else:
|
|
389
|
-
|
|
390
|
-
|
|
390
|
+
if there_is_beam_source:
|
|
391
|
+
direct_beam_contribution = (
|
|
392
|
+
B_collect_0.T[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
u0 = (
|
|
396
|
+
np.einsum(
|
|
397
|
+
"tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
|
|
398
|
+
)
|
|
399
|
+
+ direct_beam_contribution
|
|
391
400
|
)
|
|
392
|
-
|
|
401
|
+
|
|
393
402
|
# Contribution from particular solution for isotropic internal sources
|
|
394
403
|
if there_is_iso_source:
|
|
395
404
|
l_uniq, l_inv = np.unique(l, return_inverse=True)
|
|
@@ -405,14 +414,16 @@ def _assemble_intensity_and_fluxes(
|
|
|
405
414
|
)
|
|
406
415
|
u0 = u0 + _mathscr_v_contribution
|
|
407
416
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
417
|
+
outputs = (rescale_factor * np.squeeze(u0),)
|
|
418
|
+
outputs_len = 1
|
|
419
|
+
if return_tau_arr:
|
|
420
|
+
outputs += (tau_arr,)
|
|
421
|
+
outputs_len += 1
|
|
422
|
+
if _return_act_dscale_for_reclass:
|
|
423
|
+
outputs += (act_dscale_reclassification,)
|
|
424
|
+
outputs_len += 1
|
|
425
|
+
return outputs[0] if outputs_len == 1 else outputs
|
|
426
|
+
|
|
416
427
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
417
428
|
|
|
418
429
|
# Construct the flux functions (refer to section 3.8 of the Comprehensive Documentation)
|
|
@@ -463,15 +474,6 @@ def _assemble_intensity_and_fluxes(
|
|
|
463
474
|
else:
|
|
464
475
|
_mathscr_v_contribution = 0
|
|
465
476
|
|
|
466
|
-
if there_is_beam_source and is_antiderivative_wrt_tau:
|
|
467
|
-
direct_beam_contribution = (
|
|
468
|
-
(B_pos / (-scale_tau / mu0)[None, :])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
469
|
-
)
|
|
470
|
-
elif there_is_beam_source and not is_antiderivative_wrt_tau:
|
|
471
|
-
direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
472
|
-
else:
|
|
473
|
-
direct_beam_contribution = 0
|
|
474
|
-
|
|
475
477
|
exponent = np.concatenate(
|
|
476
478
|
[
|
|
477
479
|
K_collect_0[l, :N] * (scaled_tau - scaled_tau_arr_lm1)[:, None],
|
|
@@ -480,7 +482,13 @@ def _assemble_intensity_and_fluxes(
|
|
|
480
482
|
axis=-1,
|
|
481
483
|
)
|
|
482
484
|
|
|
485
|
+
direct_beam_contribution = 0
|
|
483
486
|
if is_antiderivative_wrt_tau:
|
|
487
|
+
if there_is_beam_source:
|
|
488
|
+
direct_beam_contribution = (
|
|
489
|
+
(B_pos / (-scale_tau / mu0)[None, :])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
490
|
+
)
|
|
491
|
+
|
|
484
492
|
u0_pos = (
|
|
485
493
|
np.einsum(
|
|
486
494
|
"tij, tj -> it",
|
|
@@ -491,11 +499,15 @@ def _assemble_intensity_and_fluxes(
|
|
|
491
499
|
+ _mathscr_v_contribution
|
|
492
500
|
)
|
|
493
501
|
else:
|
|
502
|
+
if there_is_beam_source:
|
|
503
|
+
direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
504
|
+
|
|
494
505
|
u0_pos = (
|
|
495
506
|
np.einsum("tij, tj -> it", GC_pos[l, :, :], np.exp(exponent))
|
|
496
507
|
+ direct_beam_contribution
|
|
497
508
|
+ _mathscr_v_contribution
|
|
498
509
|
)
|
|
510
|
+
|
|
499
511
|
flux = 2 * pi * (mu_arr_pos * W) @ u0_pos
|
|
500
512
|
|
|
501
513
|
if return_tau_arr:
|
|
@@ -543,20 +555,7 @@ def _assemble_intensity_and_fluxes(
|
|
|
543
555
|
)
|
|
544
556
|
else:
|
|
545
557
|
_mathscr_v_contribution = 0
|
|
546
|
-
|
|
547
|
-
if there_is_beam_source and is_antiderivative_wrt_tau:
|
|
548
|
-
direct_beam_contribution = (B_neg / (-scale_tau / mu0)[None, l])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
549
|
-
direct_beam = I0 * mu0 * np.exp(-tau / mu0) * -mu0
|
|
550
|
-
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau / mu0)[None, l]
|
|
551
|
-
elif there_is_beam_source and not is_antiderivative_wrt_tau:
|
|
552
|
-
direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
553
|
-
direct_beam = I0 * mu0 * np.exp(-tau / mu0)
|
|
554
|
-
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0)
|
|
555
|
-
else:
|
|
556
|
-
direct_beam_contribution = 0
|
|
557
|
-
direct_beam = 0
|
|
558
|
-
direct_beam_scaled = 0
|
|
559
|
-
|
|
558
|
+
|
|
560
559
|
exponent = np.concatenate(
|
|
561
560
|
[
|
|
562
561
|
K_collect_0[l, :N] * (scaled_tau - scaled_tau_arr_lm1)[:, None],
|
|
@@ -565,18 +564,32 @@ def _assemble_intensity_and_fluxes(
|
|
|
565
564
|
axis=-1,
|
|
566
565
|
)
|
|
567
566
|
|
|
567
|
+
direct_beam_contribution = 0
|
|
568
|
+
direct_beam = 0
|
|
569
|
+
direct_beam_scaled = 0
|
|
568
570
|
if is_antiderivative_wrt_tau:
|
|
571
|
+
if there_is_beam_source:
|
|
572
|
+
direct_beam_contribution = (B_neg / (-scale_tau / mu0)[None, l])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
573
|
+
direct_beam = I0 * mu0 * np.exp(-tau / mu0) * -mu0
|
|
574
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau / mu0)[None, l]
|
|
575
|
+
|
|
569
576
|
u0_neg = (
|
|
570
577
|
np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :])
|
|
571
578
|
+ direct_beam_contribution
|
|
572
579
|
+ _mathscr_v_contribution
|
|
573
580
|
)
|
|
574
581
|
else:
|
|
582
|
+
if there_is_beam_source:
|
|
583
|
+
direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau / mu0)[None, :]
|
|
584
|
+
direct_beam = I0 * mu0 * np.exp(-tau / mu0)
|
|
585
|
+
direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0)
|
|
586
|
+
|
|
575
587
|
u0_neg = (
|
|
576
588
|
np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent))
|
|
577
589
|
+ direct_beam_contribution
|
|
578
590
|
+ _mathscr_v_contribution
|
|
579
591
|
)
|
|
592
|
+
|
|
580
593
|
diffuse_flux = 2 * pi * (mu_arr_pos * W) @ u0_neg + direct_beam_scaled - direct_beam
|
|
581
594
|
|
|
582
595
|
if return_tau_arr:
|
{pythonicdisort-1.1 → pythonicdisort-1.2}/src/PythonicDISORT/_solve_for_gen_and_part_sols.py
RENAMED
|
@@ -210,28 +210,12 @@ def _solve_for_gen_and_part_sols(
|
|
|
210
210
|
|
|
211
211
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
221
|
-
|
|
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
|
-
)
|
|
233
|
-
else:
|
|
234
|
-
return (
|
|
235
|
-
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
236
|
-
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
237
|
-
)
|
|
213
|
+
outputs = (
|
|
214
|
+
G_collect.reshape((NFourier, NLayers, NQuad, NQuad)),
|
|
215
|
+
K_collect.reshape((NFourier, NLayers, NQuad)),
|
|
216
|
+
)
|
|
217
|
+
if there_is_beam_source:
|
|
218
|
+
outputs += (B_collect.reshape((NFourier, NLayers, NQuad)),)
|
|
219
|
+
if there_is_iso_source:
|
|
220
|
+
outputs += (G_inv_collect_0,)
|
|
221
|
+
return outputs
|
|
@@ -243,9 +243,10 @@ def pydisort(
|
|
|
243
243
|
raise ValueError("The zeroth dimension of the shape of `s_poly_coeffs` does not match the number of layers which is deduced from the length of `tau_arr`.")
|
|
244
244
|
# Value checks on the phase function Legendre coefficients
|
|
245
245
|
if not np.all(omega_arr * Leg_coeffs_all[:, 0] == omega_arr):
|
|
246
|
-
|
|
246
|
+
warnings.warn("The zeroth index phase function Legendre coefficient must be, and has been corrected to, 1.")
|
|
247
|
+
Leg_coeffs_all[:, 0] = 1
|
|
247
248
|
if not (np.all(-1 <= Leg_coeffs_all) and np.all(Leg_coeffs_all <= 1)):
|
|
248
|
-
raise ValueError("The phase function Legendre coefficients must all be between -1 and 1.")
|
|
249
|
+
raise ValueError("The phase function Legendre coefficients must all be between -1 and 1.")
|
|
249
250
|
# Conditions on the number of quadrature angles (NQuad), Legendre coefficients (NLeg) and loops (NFourier)
|
|
250
251
|
if not NQuad >= 2:
|
|
251
252
|
raise ValueError("There must be at least two streams.")
|
|
@@ -256,7 +257,7 @@ def pydisort(
|
|
|
256
257
|
if not NFourier <= NLeg:
|
|
257
258
|
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.")
|
|
258
259
|
if NFourier > 64 and not only_flux:
|
|
259
|
-
warnings.warn("`NFourier` is large and may cause errors, consider decreasing `NFourier` to 64 and it probably should be even less.")
|
|
260
|
+
warnings.warn("`NFourier` is large and may cause errors, consider decreasing `NFourier` to 64 and it probably should be even less. By default `NFourier` equals `NQuad`.")
|
|
260
261
|
# Not strictly necessary but there will be tremendous inaccuracies if this is violated
|
|
261
262
|
if not NLeg <= NQuad:
|
|
262
263
|
raise ValueError("There should be more streams than the number of phase function Legendre coefficients used.")
|
|
@@ -316,24 +317,31 @@ def pydisort(
|
|
|
316
317
|
scale_tau = 1 - omega_arr * f_arr
|
|
317
318
|
scaled_thickness_arr = scale_tau * thickness_arr
|
|
318
319
|
scaled_tau_arr_with_0 = np.insert(np.cumsum(scaled_thickness_arr), 0, 0)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
)[None, :]
|
|
320
|
+
scaled_Leg_coeffs = (Leg_coeffs - f_arr[:, None]) / (1 - f_arr[:, None])
|
|
321
|
+
weighted_scaled_Leg_coeffs = scaled_Leg_coeffs * (2 * np.arange(NLeg) + 1)[None, :]
|
|
322
322
|
scaled_omega_arr = (1 - f_arr) / scale_tau * omega_arr
|
|
323
323
|
|
|
324
324
|
translations = scaled_tau_arr_with_0[:-1] - scale_tau * np.insert(tau_arr[:-1], 0, 0)
|
|
325
325
|
scaled_s_poly_coeffs = (
|
|
326
326
|
subroutines.affine_transform_poly_coeffs(s_poly_coeffs, scale_tau, translations)
|
|
327
327
|
/ scale_tau[:, None] # Divide by d\tau^* / d\tau
|
|
328
|
-
)
|
|
328
|
+
) * (1 - omega_arr[:, None]) # Enforce Kirchoff's Law of Thermal Radiation: absorptivity equals emissivity
|
|
329
329
|
|
|
330
330
|
else:
|
|
331
331
|
# This is a shortcut to the same results
|
|
332
332
|
scale_tau = np.ones(NLayers)
|
|
333
333
|
scaled_tau_arr_with_0 = np.insert(tau_arr, 0, 0)
|
|
334
|
-
|
|
334
|
+
scaled_Leg_coeffs = Leg_coeffs
|
|
335
|
+
weighted_scaled_Leg_coeffs = scaled_Leg_coeffs * (2 * np.arange(NLeg) + 1)[None, :]
|
|
335
336
|
scaled_omega_arr = omega_arr
|
|
336
|
-
scaled_s_poly_coeffs = s_poly_coeffs
|
|
337
|
+
scaled_s_poly_coeffs = s_poly_coeffs * (1 - omega_arr[:, None]) # Enforce Kirchoff's Law of Thermal Radiation: absorptivity equals emissivity
|
|
338
|
+
|
|
339
|
+
if np.any(scaled_omega_arr > 1 - 1e-6):
|
|
340
|
+
warnings.warn("Some delta-scaled single-scattering albedos are very close to 1 which may cause numerical instability.")
|
|
341
|
+
if (np.any(-0.95 > scaled_Leg_coeffs[:, 1:]) and np.any(scaled_Leg_coeffs[:, 1:] > 0.95)):
|
|
342
|
+
warnings.warn("Some delta-scaled phase function Legendre coefficients have a magnitude that is very close to 1" +
|
|
343
|
+
" (this excludes the zeroth index coefficient which must be 1) and this may cause numerical instability.")
|
|
344
|
+
|
|
337
345
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
338
346
|
|
|
339
347
|
# Rescale of sources
|
|
@@ -361,7 +369,7 @@ def pydisort(
|
|
|
361
369
|
b_neg = (b_neg / rescale_factor).copy()
|
|
362
370
|
# --------------------------------------------------------------------------------------------------------------------------
|
|
363
371
|
|
|
364
|
-
if NT_cor and not only_flux and there_is_beam_source and np.any(f_arr > 0) and NLeg < NLeg_all:
|
|
372
|
+
if NT_cor and not only_flux and there_is_beam_source and np.any(f_arr > 0) and NLeg < NLeg_all and np.any(omega_arr > 0):
|
|
365
373
|
|
|
366
374
|
############################### Perform NT corrections on the intensity but not the flux ###############################
|
|
367
375
|
############################### Refer to section 3.7.2 of the Comprehensive Documentation ##############################
|
|
@@ -384,12 +384,11 @@ def linear_spline_coefficients(x, y, check_inputs=True):
|
|
|
384
384
|
|
|
385
385
|
|
|
386
386
|
|
|
387
|
-
def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI,
|
|
387
|
+
def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, **kwargs):
|
|
388
388
|
"""Generate DISORT-equivalent ``s_poly_coeffs`` for input into ``pydisort``.
|
|
389
389
|
This convenience function is provided to help match the inputs for Stamnes' DISORT to those for PythonicDISORT.
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
Otherwise, the emissivities will be set to 1 and users can multiply their own emissivity factors.
|
|
390
|
+
Note that the coefficients will be multiplied by emissivity factors equal to ``1 - omega_arr`` inside
|
|
391
|
+
`pydisort`, i.e. Kirchoff's law of thermal radiation is enforced, just like in Stamnes' DISORT.
|
|
393
392
|
|
|
394
393
|
Parameters
|
|
395
394
|
----------
|
|
@@ -402,8 +401,6 @@ def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, omega_arr=None, **kw
|
|
|
402
401
|
Lower bound of wavenumber interval with units m^-1. This variable is identically named in Stamnes' DISORT.
|
|
403
402
|
WVNMHI : float
|
|
404
403
|
Upper bound of wavenumber interval with units m^-1. This variable is identically named in Stamnes' DISORT.
|
|
405
|
-
omega_arr : optional, array or float
|
|
406
|
-
Single-scattering albedo of each atmospheric layer which is required to compute the emissivity of each layer.
|
|
407
404
|
**kwargs
|
|
408
405
|
Keyword arguments to pass to ``scipy.integrate.quad_vec``.
|
|
409
406
|
|
|
@@ -426,26 +423,10 @@ def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, omega_arr=None, **kw
|
|
|
426
423
|
lambda WVNM: Planck(TEMPER, WVNM), WVNMLO, WVNMHI, **kwargs
|
|
427
424
|
)[0]
|
|
428
425
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
)
|
|
434
|
-
)
|
|
435
|
-
elif np.isscalar(omega_arr):
|
|
436
|
-
return (
|
|
437
|
-
linear_spline_coefficients(
|
|
438
|
-
tau_arr_with_0, blackbody_emission_at_each_boundary, check_inputs=False
|
|
439
|
-
)
|
|
440
|
-
* (1 - omega_arr)
|
|
441
|
-
)
|
|
442
|
-
else:
|
|
443
|
-
return (
|
|
444
|
-
linear_spline_coefficients(
|
|
445
|
-
tau_arr_with_0, blackbody_emission_at_each_boundary, check_inputs=False
|
|
446
|
-
)
|
|
447
|
-
* (1 - omega_arr)[:, None]
|
|
448
|
-
)
|
|
426
|
+
return linear_spline_coefficients(
|
|
427
|
+
tau_arr_with_0, blackbody_emission_at_each_boundary, check_inputs=False
|
|
428
|
+
)
|
|
429
|
+
|
|
449
430
|
|
|
450
431
|
|
|
451
432
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PythonicDISORT
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: Discrete Ordinates Solver for the (1D) Radiative Transfer Equation in a single or multi-layer atmosphere.
|
|
5
5
|
Author-email: Dion HO Jia Xu <dh3065@columbia.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/LDEO-CREW/Pythonic-DISORT
|
|
@@ -67,8 +67,8 @@ These notebooks double up as examples of how to use PythonicDISORT. The tests wh
|
|
|
67
67
|
* Test Problem 5: Cloud C.1 Scattering, Beam Source
|
|
68
68
|
* Test Problem 6: No Scattering, Increasingly Complex Sources (relevant for modeling longwave radiation)
|
|
69
69
|
* Test Problem 7: Absorption + Scattering + All Possible Sources, Lambertian and Hapke Surface Reflectivities (one layer)
|
|
70
|
-
* Test Problem 8: Absorbing / Isotropic-Scattering Medium
|
|
71
|
-
* Test Problem 9: General Emitting / Absorbing / Scattering Medium
|
|
70
|
+
* Test Problem 8: Absorbing / Isotropic-Scattering Medium (multiple layers)
|
|
71
|
+
* Test Problem 9: General Emitting / Absorbing / Scattering Medium (multiple layers)
|
|
72
72
|
* Test Problem 11: Single-Layer vs. Multiple Layers (no corresponding Jupyter Notebook)
|
|
73
73
|
|
|
74
74
|
# Installation
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|