Skip to content

Commit f2f59de

Browse files
committed
Upgraded subroutines.generate_s_poly_coeffs to be able to compute emissivities if omega_arr is supplied.
Slight improvements to docs and input checks.
1 parent fd1d450 commit f2f59de

File tree

6 files changed

+72
-41
lines changed

6 files changed

+72
-41
lines changed

docs/Pythonic-DISORT.ipynb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@
10351035
"\n",
10361036
"$$B(T(\\tau)) = a_{l0} + a_{l1} \\tau$$\n",
10371037
"\n",
1038-
"for constants $a_{l0}, a_{l1}$ to be specified. The convenience function `PythonicDISORT.subroutines.generate_s_poly_coeffs` is provided to generate `s_poly_coeffs` such that the outputs of PythonicDISORT will match those of Stamnes' DISORT given the same inputs. PythonicDISORT allows much more flexibility in choosing blackbody emission profiles than DISORT."
1038+
"for constants $a_{l0}, a_{l1}$ to be specified. The convenience function `PythonicDISORT.subroutines.generate_s_poly_coeffs` is provided to generate `s_poly_coeffs` such that the outputs of PythonicDISORT will match those of Stamnes' DISORT given the same inputs. PythonicDISORT allows much more flexibility in choosing blackbody emission profiles than DISORT though."
10391039
]
10401040
},
10411041
{
@@ -1056,7 +1056,8 @@
10561056
"\n",
10571057
"##################################################################\n",
10581058
"\n",
1059-
"emissivity = 1 # This should actually be determined using Kirchoff's law of thermal radiation"
1059+
"emissivity = 1 # In practice this should be determined using Kirchoff's law\n",
1060+
" # of thermal radiation, and Kirchoff's law is enforced in Stamnes' DISORT"
10601061
]
10611062
},
10621063
{
@@ -1071,7 +1072,7 @@
10711072
"# This is the coefficient matrix \\mathscr{a}\n",
10721073
"s_poly_coeffs = PythonicDISORT.subroutines.generate_s_poly_coeffs(\n",
10731074
" tau_arr, TEMPER, WVNMLO, WVNMHI\n",
1074-
") * emissivity\n",
1075+
") * emissivity[:, None]\n",
10751076
"\n",
10761077
"#####################################################################################################\n",
10771078
"\n",
@@ -5996,7 +5997,7 @@
59965997
"name": "python",
59975998
"nbconvert_exporter": "python",
59985999
"pygments_lexer": "ipython3",
5999-
"version": "3.13.2"
6000+
"version": "3.11.8"
60006001
}
60016002
},
60026003
"nbformat": 4,

pydisotest/8_test.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@
307307
"\n",
308308
"Upward (diffuse) fluxes\n",
309309
"Difference = 1.8563882056565895e-08\n",
310-
"Difference ratio = 3.5919432662810775e-07\n",
310+
"Difference ratio = 0.9999999953171316\n",
311311
"\n",
312312
"Downward (diffuse) fluxes\n",
313313
"Difference = 6.096862970039751e-08\n",

pydisotest/9_test.ipynb

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -656,12 +656,12 @@
656656
"Max pointwise differences\n",
657657
"\n",
658658
"Upward (diffuse) fluxes\n",
659-
"Difference = 2.5765318011072846e-08\n",
660-
"Difference ratio = 6.125620222015996e-07\n",
659+
"Difference = 2.576531828168971e-08\n",
660+
"Difference ratio = 6.125620247828119e-07\n",
661661
"\n",
662662
"Downward (diffuse) fluxes\n",
663-
"Difference = 1.1920928955078125e-07\n",
664-
"Difference ratio = 3.967778597939065e-07\n",
663+
"Difference = 1.1920928977282585e-07\n",
664+
"Difference ratio = 3.967778612553841e-07\n",
665665
"\n",
666666
"Direct (downward) fluxes\n",
667667
"Difference = 0.0\n",
@@ -672,7 +672,7 @@
672672
"At tau = 0.0\n",
673673
"Max pointwise difference = 5.53211412651633e-08\n",
674674
"At tau = 1.05\n",
675-
"Max pointwise difference ratio = 9.347970166516183e-07\n",
675+
"Max pointwise difference ratio = 9.347970250378194e-07\n",
676676
"\n"
677677
]
678678
}
@@ -1036,9 +1036,9 @@
10361036
"Intensities\n",
10371037
"\n",
10381038
"At tau = 21.0\n",
1039-
"Max pointwise difference = 0.0027373875326694996\n",
1039+
"Max pointwise difference = 0.0027373888669937063\n",
10401040
"At tau = 21.0\n",
1041-
"Max pointwise difference ratio = 0.0017543192767219193\n",
1041+
"Max pointwise difference ratio = 0.0017543201318550158\n",
10421042
"\n"
10431043
]
10441044
}
@@ -1129,7 +1129,7 @@
11291129
{
11301130
"data": {
11311131
"text/plain": [
1132-
"<matplotlib.legend.Legend at 0x2406e552810>"
1132+
"<matplotlib.legend.Legend at 0x2a1c21ed810>"
11331133
]
11341134
},
11351135
"execution_count": 31,
@@ -1452,9 +1452,9 @@
14521452
"Intensities\n",
14531453
"\n",
14541454
"At tau = 0.0\n",
1455-
"Max pointwise difference = 0.06855659986274398\n",
1455+
"Max pointwise difference = 0.06911426569533097\n",
14561456
"At tau = 0.0\n",
1457-
"Max pointwise difference ratio = 0.03692288128517093\n",
1457+
"Max pointwise difference ratio = 0.03919935155907838\n",
14581458
"\n"
14591459
]
14601460
}
@@ -1487,8 +1487,8 @@
14871487
"Max pointwise differences\n",
14881488
"\n",
14891489
"Upward (diffuse) fluxes\n",
1490-
"Difference = 0.004794243247684626\n",
1491-
"Difference ratio = 0.0009782676032947248\n",
1490+
"Difference = 0.004794243247683738\n",
1491+
"Difference ratio = 0.0009782676032945436\n",
14921492
"\n",
14931493
"Downward (diffuse) fluxes\n",
14941494
"Difference = 0.00870651337438666\n",
@@ -1501,9 +1501,9 @@
15011501
"Intensities\n",
15021502
"\n",
15031503
"At tau = 2.1\n",
1504-
"Max pointwise difference = 0.012109081699047897\n",
1504+
"Max pointwise difference = 0.010067514209913586\n",
15051505
"At tau = 2.1\n",
1506-
"Max pointwise difference ratio = 0.007287281829096514\n",
1506+
"Max pointwise difference ratio = 0.005664629397768609\n",
15071507
"\n"
15081508
]
15091509
}

src/PythonicDISORT/_assemble_intensity_and_fluxes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,6 @@ def flux_down(tau, is_antiderivative_wrt_tau=False, return_tau_arr=False):
592592
# --------------------------------------------------------------------------------------------------------------------------
593593

594594
if only_flux:
595-
return flux_up, flux_down, u0#, GC_collect_0, K_collect_0, B_collect_0
595+
return flux_up, flux_down, u0#, GC_collect_0, K_collect_0#, B_collect_0
596596
else:
597597
return flux_up, flux_down, u0, u

src/PythonicDISORT/pydisort.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,6 @@ def pydisort(
230230
# The user must supply at least as many phase function Legendre coefficients as intended for use
231231
if not NLeg > 0:
232232
raise ValueError("The number of phase function Legendre coefficients must be positive.")
233-
if not np.all(omega_arr * Leg_coeffs_all[:, 0] == omega_arr):
234-
raise ValueError("The first phase function Legendre coefficient must equal 1.")
235-
if not (np.all(-1 <= Leg_coeffs_all) and np.all(Leg_coeffs_all <= 1)):
236-
raise ValueError("The phase function Legendre coefficients must all be between -1 and 1.")
237233
if not NLeg <= NLeg_all:
238234
raise ValueError("`NLeg` cannot be larger than the number of phase function Legendre coefficients provided.")
239235
# Ensure that the first dimension of the following inputs corresponds to the number of layers
@@ -245,6 +241,11 @@ def pydisort(
245241
raise ValueError("The length of `f_arr` does not match the number of layers as deduced from the length of `tau_arr`.")
246242
if there_is_iso_source and not np.shape(s_poly_coeffs)[0] == NLayers:
247243
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`.")
244+
# Value checks on the phase function Legendre coefficients
245+
if not np.all(omega_arr * Leg_coeffs_all[:, 0] == omega_arr):
246+
raise ValueError("The first phase function Legendre coefficient must equal 1.")
247+
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.")
248249
# Conditions on the number of quadrature angles (NQuad), Legendre coefficients (NLeg) and loops (NFourier)
249250
if not NQuad >= 2:
250251
raise ValueError("There must be at least two streams.")
@@ -353,9 +354,10 @@ def pydisort(
353354
scaled_s_poly_coeffs = (scaled_s_poly_coeffs / rescale_factor).copy()
354355
else:
355356
rescale_factor = np.max((I0, np.max(b_pos), np.max(b_neg)))
356-
I0 = (I0 / rescale_factor).copy()
357-
b_pos = (b_pos / rescale_factor).copy()
358-
b_neg = (b_neg / rescale_factor).copy()
357+
if rescale_factor != 0:
358+
I0 = (I0 / rescale_factor).copy()
359+
b_pos = (b_pos / rescale_factor).copy()
360+
b_neg = (b_neg / rescale_factor).copy()
359361
# --------------------------------------------------------------------------------------------------------------------------
360362

361363
if NT_cor and not only_flux and there_is_beam_source and np.any(f_arr > 0) and NLeg < NLeg_all:

src/PythonicDISORT/subroutines.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,12 @@ def linear_spline_coefficients(x, y, check_inputs=True):
384384

385385

386386

387-
def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, **kwargs):
387+
def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, omega_arr=None, **kwargs):
388388
"""Generate DISORT-equivalent ``s_poly_coeffs`` for input into ``pydisort``.
389389
This convenience function is provided to help match the inputs for Stamnes' DISORT to those for PythonicDISORT.
390-
Users will have to manually adjust emissivities, however, and they should note that PythonicDISORT allows much
391-
more flexibility in choosing blackbody emission profiles than DISORT.
390+
If ``omega_arr`` is specified, the coefficients will be multiplied by emissivity factors equal to ``1 - omega_arr``
391+
per Kirchoff's law of thermal radiation, and Kirchoff's law is enforced in Stamnes' DISORT.
392+
Otherwise, the emissivities will be set to 1 and users can multiply their own emissivity factors.
392393
393394
Parameters
394395
----------
@@ -401,6 +402,8 @@ def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, **kwargs):
401402
Lower bound of wavenumber interval with units m^-1. This variable is identically named in Stamnes' DISORT.
402403
WVNMHI : scalar
403404
Upper bound of wavenumber interval with units m^-1. This variable is identically named in Stamnes' DISORT.
405+
omega_arr : optional, array or scalar
406+
Single-scattering albedo of each atmospheric layer.
404407
**kwargs
405408
Keyword arguments to pass to ``scipy.integrate.quad_vec``.
406409
@@ -414,12 +417,35 @@ def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, **kwargs):
414417
"""
415418
tau_arr = np.atleast_1d(tau_arr)
416419
if not len(TEMPER) == len(tau_arr) + 1:
417-
raise ValueError("Missing temperature specification at some boundaries / interfaces.")
418-
420+
raise ValueError(
421+
"Missing temperature specification at some boundaries / interfaces."
422+
)
423+
419424
tau_arr_with_0 = np.insert(tau_arr, 0, 0)
420-
blackbody_emission_at_each_boundary = sc.integrate.quad_vec(lambda WVNM: Planck(TEMPER, WVNM), WVNMLO, WVNMHI, **kwargs)[0]
421-
422-
return linear_spline_coefficients(tau_arr_with_0, blackbody_emission_at_each_boundary, check_inputs=False)
425+
blackbody_emission_at_each_boundary = sc.integrate.quad_vec(
426+
lambda WVNM: Planck(TEMPER, WVNM), WVNMLO, WVNMHI, **kwargs
427+
)[0]
428+
429+
if omega_arr == None:
430+
return (
431+
linear_spline_coefficients(
432+
tau_arr_with_0, blackbody_emission_at_each_boundary, check_inputs=False
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+
)
423449

424450

425451

@@ -448,7 +474,9 @@ def generate_emissivity_from_BDRF(N, zeroth_BDRF_Fourier_mode):
448474
return 1 - zeroth_BDRF_Fourier_mode
449475
else:
450476
mu_arr_pos, W = Gauss_Legendre_quad(N)
451-
return 1 - 2 * zeroth_BDRF_Fourier_mode(mu_arr_pos, mu_arr_pos) * mu_arr_pos[None, :] @ W
477+
return (
478+
1 - 2 * zeroth_BDRF_Fourier_mode(mu_arr_pos, mu_arr_pos) * mu_arr_pos[None, :] @ W
479+
)
452480

453481

454482

@@ -778,7 +806,7 @@ def mathscr_b(i):
778806

779807

780808

781-
def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
809+
def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None, div_threshold=1e-15):
782810
"""Performs pointwise comparisons between results from Stamnes' DISORT,
783811
which are stored in ``.npz`` files, against results from PythonicDISORT. Used in our PyTests.
784812
@@ -807,7 +835,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
807835
diff_flux_up,
808836
flup,
809837
out=np.zeros_like(diff_flux_up),
810-
where=flup > 1e-8,
838+
where=flup > div_threshold,
811839
)
812840
print("Difference =", np.max(diff_flux_up))
813841
print("Difference ratio =", np.max(ratio_flux_up))
@@ -820,7 +848,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
820848
diff_flux_down_diffuse,
821849
rfldn,
822850
out=np.zeros_like(diff_flux_down_diffuse),
823-
where=rfldn > 1e-8,
851+
where=rfldn > div_threshold,
824852
)
825853
print("Difference =", np.max(diff_flux_down_diffuse))
826854
print(
@@ -836,7 +864,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
836864
diff_flux_down_direct,
837865
rfldir,
838866
out=np.zeros_like(diff_flux_down_direct),
839-
where=rfldir > 1e-8,
867+
where=rfldir > div_threshold,
840868
)
841869
print("Difference =", np.max(diff_flux_down_direct))
842870
print(
@@ -852,7 +880,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
852880
diff,
853881
uu[mu_to_compare],
854882
out=np.zeros_like(diff),
855-
where=uu[mu_to_compare] > 1e-8,
883+
where=uu[mu_to_compare] > div_threshold,
856884
)
857885
max_diff_tau_index = np.argmax(np.max(np.max(diff, axis=0), axis=1))
858886
max_ratio_tau_index = np.argmax(np.max(np.max(diff_ratio, axis=0), axis=1))

0 commit comments

Comments
 (0)