PythonicDISORT 1.0.2__py3-none-any.whl → 1.1__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.
@@ -100,6 +100,7 @@ def _assemble_intensity_and_fluxes(
100
100
  else:
101
101
  import numpy as np
102
102
 
103
+
103
104
  ################################## Assemble uncorrected intensity and flux functions #######################################
104
105
 
105
106
  # Compute all the necessary quantities
@@ -172,7 +173,7 @@ def _assemble_intensity_and_fluxes(
172
173
  Pass `return_Fourier_error = True` (defaults to `False`) to return the
173
174
  Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
174
175
  Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
175
- to switch to an antiderivative of the function with respect to `tau`.
176
+ to switch to an antiderivative of this function with respect to `tau`.
176
177
  """
177
178
  tau = np.atleast_1d(tau)
178
179
  phi = np.atleast_1d(phi)
@@ -238,7 +239,7 @@ def _assemble_intensity_and_fluxes(
238
239
  G_inv_collect_0[l_uniq],
239
240
  mu_arr,
240
241
  is_antiderivative_wrt_tau,
241
- autograd_compatible
242
+ autograd_compatible,
242
243
  )
243
244
 
244
245
  if NFourier == 1:
@@ -323,7 +324,7 @@ def _assemble_intensity_and_fluxes(
323
324
  but reclassification of delta-scaled flux and other corrections must be done manually
324
325
  (for actinic flux `generate_diff_act_flux_funcs` will automatically perform the reclassification).
325
326
  Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
326
- to switch to an antiderivative of the function with respect to `tau`.
327
+ to switch to an antiderivative of this function with respect to `tau`.
327
328
  """
328
329
  tau = np.atleast_1d(tau)
329
330
  if np.any(tau < 0) or np.any(tau > tau_arr[-1]):
@@ -400,7 +401,7 @@ def _assemble_intensity_and_fluxes(
400
401
  G_inv_collect_0[l_uniq],
401
402
  mu_arr,
402
403
  is_antiderivative_wrt_tau,
403
- autograd_compatible
404
+ autograd_compatible,
404
405
  )
405
406
  u0 = u0 + _mathscr_v_contribution
406
407
 
@@ -428,7 +429,7 @@ def _assemble_intensity_and_fluxes(
428
429
  (Energetic) Flux function with argument `tau` (type: array or float) for positive (upward) `mu` values.
429
430
  Returns the diffuse flux magnitudes (same type and size as `tau`).
430
431
  Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
431
- to switch to an antiderivative of the function with respect to `tau`.
432
+ to switch to an antiderivative of this function with respect to `tau`.
432
433
  """
433
434
  tau = np.atleast_1d(tau)
434
435
  if np.any(tau < 0) or np.any(tau > tau_arr[-1]):
@@ -457,7 +458,7 @@ def _assemble_intensity_and_fluxes(
457
458
  G_inv_collect_0[l_uniq],
458
459
  mu_arr,
459
460
  is_antiderivative_wrt_tau,
460
- autograd_compatible
461
+ autograd_compatible,
461
462
  )
462
463
  else:
463
464
  _mathscr_v_contribution = 0
@@ -509,7 +510,7 @@ def _assemble_intensity_and_fluxes(
509
510
  Returns a tuple of the diffuse and direct flux magnitudes respectively where each entry is of the
510
511
  same type and size as `tau`.
511
512
  Pass `is_antiderivative_wrt_tau = True` (defaults to `False`)
512
- to switch to an antiderivative of the function with respect to `tau`.
513
+ to switch to an antiderivative of this function with respect to `tau`.
513
514
  """
514
515
  tau = np.atleast_1d(tau)
515
516
  if np.any(tau < 0) or np.any(tau > tau_arr[-1]):
@@ -538,7 +539,7 @@ def _assemble_intensity_and_fluxes(
538
539
  G_inv_collect_0[l_uniq],
539
540
  mu_arr,
540
541
  is_antiderivative_wrt_tau,
541
- autograd_compatible
542
+ autograd_compatible,
542
543
  )
543
544
  else:
544
545
  _mathscr_v_contribution = 0
@@ -592,6 +593,6 @@ def _assemble_intensity_and_fluxes(
592
593
  # --------------------------------------------------------------------------------------------------------------------------
593
594
 
594
595
  if only_flux:
595
- return flux_up, flux_down, u0#, GC_collect_0, K_collect_0, B_collect_0
596
+ return flux_up, flux_down, u0#, GC_collect_0, K_collect_0#, B_collect_0
596
597
  else:
597
598
  return flux_up, flux_down, u0, u
@@ -101,16 +101,17 @@ def _solve_for_gen_and_part_sols(
101
101
  # Loop over NLayers atmospheric layers
102
102
  # --------------------------------------------------------------------------------------------------------------------------
103
103
  for l in range(NLayers):
104
- weighted_asso_Leg_coeffs_l = weighted_scaled_Leg_coeffs[l, :][ells] * fac
104
+ weighted_scaled_Leg_coeffs_l = weighted_scaled_Leg_coeffs[l, :][ells]
105
+ scaled_omega_l = scaled_omega_arr[l]
106
+ omega_times_Leg_coeffs = (scaled_omega_l / 2) * weighted_scaled_Leg_coeffs_l
105
107
 
106
- if np.any(weighted_asso_Leg_coeffs_l > 0): # There are shortcuts if the coefficients are all zero
107
- scaled_omega_l = scaled_omega_arr[l]
108
+ if np.any(np.abs(omega_times_Leg_coeffs) > 1e-8): # There are shortcuts if multiple scattering is insignificant
108
109
 
109
110
  # Generate D
110
111
  # --------------------------------------------------------------------------------------------------------------------------
111
- D_temp = weighted_asso_Leg_coeffs_l[None, :] * asso_leg_term_pos.T
112
- D_pos = (scaled_omega_l / 2) * D_temp @ asso_leg_term_pos
113
- D_neg = (scaled_omega_l / 2) * D_temp @ asso_leg_term_neg
112
+ D_temp = omega_times_Leg_coeffs[None, :] * (fac[None, :] * asso_leg_term_pos.T)
113
+ D_pos = D_temp @ asso_leg_term_pos
114
+ D_neg = D_temp @ asso_leg_term_neg
114
115
 
115
116
  # --------------------------------------------------------------------------------------------------------------------------
116
117
 
@@ -130,8 +131,8 @@ def _solve_for_gen_and_part_sols(
130
131
  # Generate X
131
132
  X_temp = (
132
133
  (scaled_omega_l * I0 * (2 - (m == 0)) / (4 * pi))
133
- * weighted_asso_Leg_coeffs_l
134
- * asso_leg_term_mu0
134
+ * weighted_scaled_Leg_coeffs_l
135
+ * (fac * asso_leg_term_mu0)
135
136
  )
136
137
  X_pos = X_temp @ asso_leg_term_pos
137
138
  X_neg = X_temp @ asso_leg_term_neg
@@ -149,7 +150,6 @@ def _solve_for_gen_and_part_sols(
149
150
 
150
151
  # --------------------------------------------------------------------------------------------------------------------------
151
152
 
152
-
153
153
  alpha_arr[ind, :, :] = alpha
154
154
  beta_arr[ind, :, :] = beta
155
155
  no_shortcut_indices.append(ind)
@@ -173,15 +173,15 @@ def _solve_for_gen_and_part_sols(
173
173
 
174
174
  # Diagonalization of coefficient matrix (refer to section 3.4.2 of the Comprehensive Documentation)
175
175
  # --------------------------------------------------------------------------------------------------------------------------
176
- alpha_arr = alpha_arr[: len(no_shortcut_indices), :, :]
177
- beta_arr = beta_arr[: len(no_shortcut_indices), :, :]
176
+ alpha_arr = alpha_arr[no_shortcut_indices, :, :]
177
+ beta_arr = beta_arr[no_shortcut_indices, :, :]
178
178
 
179
179
  K_squared_arr, eigenvecs_GpG_arr = np.linalg.eig(
180
180
  np.einsum(
181
181
  "lij, ljk -> lik", alpha_arr - beta_arr, alpha_arr + beta_arr, optimize=True
182
182
  ),
183
183
  )
184
-
184
+
185
185
  # Eigenvalues arranged negative then positive, from largest to smallest magnitude
186
186
  K_arr = np.concatenate((-np.sqrt(K_squared_arr), np.sqrt(K_squared_arr)), axis=1)
187
187
  eigenvecs_GpG_arr = np.concatenate((eigenvecs_GpG_arr, eigenvecs_GpG_arr), axis=2)
@@ -205,7 +205,7 @@ def _solve_for_gen_and_part_sols(
205
205
  K_collect[no_shortcut_indices, :] = K_arr
206
206
  if len(no_shortcut_indices_0) > 0: # If there is no isotropic source this list will be empty
207
207
  G_inv_collect_0[no_shortcut_indices_0, :, :] = np.linalg.inv(
208
- G_collect[: len(no_shortcut_indices_0), :, :]
208
+ G_collect[no_shortcut_indices_0, :, :]
209
209
  )
210
210
 
211
211
  # --------------------------------------------------------------------------------------------------------------------------
@@ -76,7 +76,7 @@ def pydisort(
76
76
  NT_cor : optional, bool
77
77
  Perform Nakajima-Tanaka intensity corrections?
78
78
  BDRF_Fourier_modes : optional, list of functions
79
- BDRF Fourier modes, each a scalar, or a function with arguments ``mu, -mu_p`` of type array
79
+ BDRF Fourier modes, each a float, or a function with arguments ``mu, -mu_p`` of type array
80
80
  which output has the same dimensions as the outer product of the two arrays.
81
81
  s_poly_coeffs : optional, array
82
82
  Polynomial coefficients of isotropic internal sources.
@@ -86,17 +86,17 @@ def pydisort(
86
86
  At or above how many atmospheric layers should ``scipy.linalg.solve_banded`` be used?
87
87
  autograd_compatible : optional, bool
88
88
  If ``True``, the autograd package: https://github.com/HIPS/autograd can be used to compute
89
- the ``tau``-derivatives of the output functions but ``pydisort`` will be less efficient.
89
+ the ``tau``-derivatives of the output functions but ``pydisort`` will be less efficient.
90
90
 
91
91
  Returns
92
92
  -------
93
- mu_arr : array or float
93
+ mu_arr : array
94
94
  All ``mu`` (cosine of polar angle) quadrature nodes.
95
95
  Fp(tau) : function
96
96
  (Energetic) Flux function with argument ``tau`` (type: array or float) for positive (upward) ``mu`` values.
97
97
  Returns the diffuse flux magnitudes (same type and size as ``tau``).
98
98
  Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
99
- to switch to an antiderivative of the function with respect to ``tau``.
99
+ to switch to an antiderivative of this function with respect to ``tau``.
100
100
  For example, ``Fp(tau_arr, is_antiderivative_wrt_tau = True) - Fp(np.insert(tau_arr[:-1] + 1e-15, 0, 0), is_antiderivative_wrt_tau = True)``
101
101
  will produce an array of the tau-integral over each layer.
102
102
  Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
@@ -105,7 +105,7 @@ def pydisort(
105
105
  Returns a tuple of the diffuse and direct flux magnitudes respectively where each entry is of the
106
106
  same type and size as ``tau``.
107
107
  Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
108
- to switch to an antiderivative of the function with respect to ``tau``.
108
+ to switch to an antiderivative of this function with respect to ``tau``.
109
109
  Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
110
110
  u0(tau) : function
111
111
  Zeroth Fourier mode of the intensity with argument ``tau`` (type: array or float).
@@ -114,13 +114,13 @@ def pydisort(
114
114
  but reclassification of delta-scaled flux and other corrections must be done manually
115
115
  (for actinic flux ``subroutines.generate_diff_act_flux_funcs`` will automatically perform the reclassification).
116
116
  Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
117
- to switch to an antiderivative of the function with respect to ``tau``.
117
+ to switch to an antiderivative of this function with respect to ``tau``.
118
118
  Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
119
119
  u(tau, phi) : function, optional
120
120
  Intensity function with arguments ``(tau, phi)`` each of type array or float.
121
121
  Returns an ndarray with axes corresponding to variation with ``mu, tau, phi`` respectively.
122
122
  Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
123
- to switch to an antiderivative of the function with respect to ``tau``.
123
+ to switch to an antiderivative of this function with respect to ``tau``.
124
124
  Pass ``return_Fourier_error = True`` (defaults to ``False``) to return the
125
125
  Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
126
126
  Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
@@ -172,13 +172,13 @@ def pydisort(
172
172
  | `Leg_coeffs_residue_avg` | `NLeg_all` |
173
173
  | `scaled_mu0` | scalar |
174
174
  """
175
-
175
+
176
176
  if autograd_compatible:
177
177
  import autograd.numpy as np
178
178
  else:
179
179
  import numpy as np
180
180
 
181
- # Turn scalars into arrays
181
+ # Turn floats into arrays
182
182
  # --------------------------------------------------------------------------------------------------------------------------
183
183
  tau_arr = np.atleast_1d(tau_arr)
184
184
  omega_arr = np.atleast_1d(omega_arr)
@@ -222,7 +222,7 @@ def pydisort(
222
222
  if not np.all(tau_arr > 0):
223
223
  raise ValueError("tau values cannot be non-positive.")
224
224
  if not np.all(thickness_arr > 0):
225
- raise ValueError("Layer thickness cannot be non-positive.")
225
+ raise ValueError("Layer thicknesses cannot be non-positive.")
226
226
  # Single-scattering albedo must be between 0 and 1, excluding 1
227
227
  if not (np.all(omega_arr >= 0) and np.all(omega_arr < 1)):
228
228
  raise ValueError("Single-scattering albedo must be between 0 and 1, excluding 1.")
@@ -230,21 +230,22 @@ def pydisort(
230
230
  # The user must supply at least as many phase function Legendre coefficients as intended for use
231
231
  if not NLeg > 0:
232
232
  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.")
237
233
  if not NLeg <= NLeg_all:
238
234
  raise ValueError("`NLeg` cannot be larger than the number of phase function Legendre coefficients provided.")
239
235
  # Ensure that the first dimension of the following inputs corresponds to the number of layers
240
236
  if not np.shape(Leg_coeffs_all)[0] == NLayers:
241
- raise ValueError("The zeroth dimension of the shape of `Leg_coeffs_all` does not match the number of layers as deduced from the length of `tau_arr`.")
237
+ raise ValueError("The zeroth dimension of the shape of `Leg_coeffs_all` does not match the number of layers which is deduced from the length of `tau_arr`.")
242
238
  if not len(omega_arr) == NLayers:
243
- raise ValueError("The zeroth dimension of the shape of `omega_arr` does not match the number of layers as deduced from the length of `tau_arr`.")
239
+ raise ValueError("The zeroth dimension of the shape of `omega_arr` does not match the number of layers which is deduced from the length of `tau_arr`.")
244
240
  if np.any(f_arr != 0) and not len(f_arr) == NLayers:
245
- raise ValueError("The length of `f_arr` does not match the number of layers as deduced from the length of `tau_arr`.")
241
+ raise ValueError("The length of `f_arr` does not match the number of layers which is deduced from the length of `tau_arr`.")
246
242
  if there_is_iso_source and not np.shape(s_poly_coeffs)[0] == NLayers:
247
- 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`.")
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
+ # 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.")
248
249
  # Conditions on the number of quadrature angles (NQuad), Legendre coefficients (NLeg) and loops (NFourier)
249
250
  if not NQuad >= 2:
250
251
  raise ValueError("There must be at least two streams.")
@@ -261,7 +262,7 @@ def pydisort(
261
262
  raise ValueError("There should be more streams than the number of phase function Legendre coefficients used.")
262
263
  # We require principal angles and a downward incident beam
263
264
  if I0 < 0:
264
- raise ValueError("The incident beam must have positive intensity.")
265
+ raise ValueError("The intensity of the incident beam cannot be negative.")
265
266
  if there_is_beam_source:
266
267
  if not (0 < mu0 and mu0 <= 1):
267
268
  raise ValueError("The cosine of the polar angle of the incident beam must be between 0 and 1, excluding 0.")
@@ -345,6 +346,7 @@ def pydisort(
345
346
  np.max(b_pos),
346
347
  np.max(b_neg),
347
348
  scaled_s_poly_coeffs[0, 0],
349
+ scaled_s_poly_coeffs[-1, :] @ (scaled_tau_arr_with_0[-1] ** np.arange(Nscoeffs))
348
350
  )
349
351
  )
350
352
  I0 = (I0 / rescale_factor).copy()
@@ -353,9 +355,10 @@ def pydisort(
353
355
  scaled_s_poly_coeffs = (scaled_s_poly_coeffs / rescale_factor).copy()
354
356
  else:
355
357
  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()
358
+ if rescale_factor != 0:
359
+ I0 = (I0 / rescale_factor).copy()
360
+ b_pos = (b_pos / rescale_factor).copy()
361
+ b_neg = (b_neg / rescale_factor).copy()
359
362
  # --------------------------------------------------------------------------------------------------------------------------
360
363
 
361
364
  if NT_cor and not only_flux and there_is_beam_source and np.any(f_arr > 0) and NLeg < NLeg_all:
@@ -667,7 +670,7 @@ def pydisort(
667
670
  tau = np.atleast_1d(tau)
668
671
  phi = np.atleast_1d(phi)
669
672
  NT_corrections = TMS_correction(tau, phi, is_antiderivative_wrt_tau)
670
-
673
+
671
674
  if autograd_compatible:
672
675
  NT_corrections = NT_corrections + np.concatenate(
673
676
  [np.zeros((N, len(tau), len(phi))), IMS_correction(tau, phi, is_antiderivative_wrt_tau)], axis=0
@@ -12,13 +12,13 @@ def transform_interval(arr, c, d, a, b):
12
12
  arr : array
13
13
  The 1D array to transform.
14
14
  c : float
15
- The beginning of interval [c, d].
15
+ Lower bound of interval [c, d].
16
16
  d : float
17
- The end of interval [c, d].
17
+ Upper bound of interval [c, d].
18
18
  a : float, optional
19
- The beginning of interval [a, b].
19
+ Lower bound of interval [a, b].
20
20
  b : float, optional
21
- The end of interval [a, b].
21
+ Upper bound of interval [a, b].
22
22
 
23
23
  Returns
24
24
  -------
@@ -38,13 +38,13 @@ def transform_weights(weights, c, d, a, b):
38
38
  weights : array
39
39
  The weights to transform.
40
40
  c : float
41
- The beginning of interval [c, d].
41
+ Lower bound of interval [c, d].
42
42
  d : float
43
- The end of interval [c, d].
43
+ Upper bound of interval [c, d].
44
44
  a : float, optional
45
- The beginning of interval [a, b].
45
+ Lower bound of interval [a, b].
46
46
  b : float, optional
47
- The end of interval [a, b].
47
+ Upper bound of interval [a, b].
48
48
 
49
49
  Returns
50
50
  -------
@@ -95,9 +95,9 @@ def Gauss_Legendre_quad(N, c=0, d=1):
95
95
  N : int (will be converted to int)
96
96
  Number of quadrature nodes.
97
97
  c : float, optional
98
- Start of integration interval.
98
+ Lower bound of integration interval.
99
99
  d : float, optional
100
- End of integration interval.
100
+ Upper bound of integration interval.
101
101
 
102
102
  Returns
103
103
  -------
@@ -248,18 +248,18 @@ def generate_diff_act_flux_funcs(u0):
248
248
  Actinic flux function with argument ``tau`` (type: array) for positive (upward) ``mu`` values.
249
249
  Returns the diffuse flux magnitudes (type: array).
250
250
  Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
251
- to switch to an antiderivative of the function with respect to ``tau``.
251
+ to switch to an antiderivative of this function with respect to ``tau``.
252
252
  Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
253
253
  Fm_act(tau) : function
254
254
  Actinic flux function with argument ``tau`` (type: array) for negative (downward) ``mu`` values.
255
255
  Returns the diffuse flux magnitudes (type: array).
256
256
  Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
257
- to switch to an antiderivative of the function with respect to ``tau``.
257
+ to switch to an antiderivative of this function with respect to ``tau``.
258
258
  Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
259
259
 
260
260
  """
261
261
  N = len(u0(0)) // 2
262
- GL_weights = Gauss_Legendre_quad(N, 0, 1)[1]
262
+ GL_weights = Gauss_Legendre_quad(N)[1]
263
263
 
264
264
  # Note that the zeroth axis of the array u0(tau) captures variation with mu
265
265
  def flux_act_up(tau, is_antiderivative_wrt_tau=False, return_tau_arr=False):
@@ -314,11 +314,12 @@ def Planck(T, WVNM):
314
314
  not_zeros_ind = T != 0
315
315
 
316
316
  results = np.zeros(len(T))
317
- results[~not_zeros_ind] = 0
318
-
319
- # Coded in this way to prevent overflow
320
- expterm = np.exp(-100 * sc.constants.h * sc.constants.c * WVNM / (sc.constants.k * T[not_zeros_ind]))
321
- results[not_zeros_ind] = (2e8 * sc.constants.h * sc.constants.c**2 * WVNM**3 * expterm) / (1 - expterm)
317
+ if np.sum(not_zeros_ind) > 0:
318
+ results[~not_zeros_ind] = 0
319
+
320
+ # Coded in this way to prevent overflow
321
+ expterm = np.exp(-100 * sc.constants.h * sc.constants.c * WVNM / (sc.constants.k * T[not_zeros_ind]))
322
+ results[not_zeros_ind] = (2e8 * sc.constants.h * sc.constants.c**2 * WVNM**3 * expterm) / (1 - expterm)
322
323
 
323
324
  return np.squeeze(results)[()]
324
325
 
@@ -383,11 +384,12 @@ def linear_spline_coefficients(x, y, check_inputs=True):
383
384
 
384
385
 
385
386
 
386
- 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):
387
388
  """Generate DISORT-equivalent ``s_poly_coeffs`` for input into ``pydisort``.
388
389
  This convenience function is provided to help match the inputs for Stamnes' DISORT to those for PythonicDISORT.
389
- Users will have to manually adjust emissivities, however, and they should note that PythonicDISORT allows much
390
- 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.
391
393
 
392
394
  Parameters
393
395
  ----------
@@ -400,6 +402,8 @@ def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, **kwargs):
400
402
  Lower bound of wavenumber interval with units m^-1. This variable is identically named in Stamnes' DISORT.
401
403
  WVNMHI : float
402
404
  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.
403
407
  **kwargs
404
408
  Keyword arguments to pass to ``scipy.integrate.quad_vec``.
405
409
 
@@ -413,12 +417,35 @@ def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, **kwargs):
413
417
  """
414
418
  tau_arr = np.atleast_1d(tau_arr)
415
419
  if not len(TEMPER) == len(tau_arr) + 1:
416
- raise ValueError("Missing temperature specification at some boundaries / interfaces.")
417
-
420
+ raise ValueError(
421
+ "Missing temperature specification at some boundaries / interfaces."
422
+ )
423
+
418
424
  tau_arr_with_0 = np.insert(tau_arr, 0, 0)
419
- blackbody_emission_at_each_boundary = sc.integrate.quad_vec(lambda WVNM: Planck(TEMPER, WVNM), WVNMLO, WVNMHI, **kwargs)[0]
420
-
421
- 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 is 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
+ )
422
449
 
423
450
 
424
451
 
@@ -432,17 +459,24 @@ def generate_emissivity_from_BDRF(N, zeroth_BDRF_Fourier_mode):
432
459
  ----------
433
460
  N : int
434
461
  Number of upper hemisphere quadrature nodes. Equal to ``NQuad // 2``.
435
- zeroth_BDRF_Fourier_mode : function
462
+ zeroth_BDRF_Fourier_mode : function or float
436
463
  Zeroth BDRF Fourier mode with arguments ``mu, -mu_p`` of type array
437
464
  and which output has the same dimensions as the outer product of the two arrays.
465
+ A scalar input represents a constant function and in that case the output
466
+ is simply one minus the scalar input.
438
467
 
439
468
  Returns
440
469
  -------
441
- array
470
+ array or float
442
471
  Emissivity for the blackbody contribution to the lower boundary source ``b_neg``.
443
472
  """
444
- mu_arr_pos, W = Gauss_Legendre_quad(N)
445
- return 1 - 2 * zeroth_BDRF_Fourier_mode(mu_arr_pos, mu_arr_pos) * mu_arr_pos[None, :] @ W
473
+ if np.isscalar(zeroth_BDRF_Fourier_mode):
474
+ return 1 - zeroth_BDRF_Fourier_mode
475
+ else:
476
+ mu_arr_pos, W = Gauss_Legendre_quad(N)
477
+ return (
478
+ 1 - 2 * zeroth_BDRF_Fourier_mode(mu_arr_pos, mu_arr_pos) * mu_arr_pos[None, :] @ W
479
+ )
446
480
 
447
481
 
448
482
 
@@ -457,8 +491,9 @@ def cache_BDRF_Fourier_modes(N, mu0, BDRF_Fourier_modes):
457
491
  Number of upper hemisphere quadrature nodes. Equal to ``NQuad // 2``.
458
492
  mu0 : float
459
493
  Cosine of polar angle of the incident beam.
460
- BDRF_Fourier_modes : list of functions
461
- BDRF Fourier modes, each a scalar, or a function with arguments ``mu, -mu_p`` of type array
494
+ BDRF_Fourier_modes : list of functions and scalars
495
+ BDRF Fourier modes, each a scalar representing a constant function,
496
+ or a function with arguments ``mu, -mu_p`` of type array
462
497
  which output has the same dimensions as the outer product of the two arrays.
463
498
 
464
499
  Returns
@@ -471,7 +506,7 @@ def cache_BDRF_Fourier_modes(N, mu0, BDRF_Fourier_modes):
471
506
  NBDRF = len(BDRF_Fourier_modes)
472
507
  mu_arr_pos = Gauss_Legendre_quad(N)[0]
473
508
 
474
- [
509
+ BDRF_Fourier_modes_evaluated = [
475
510
  (
476
511
  None
477
512
  if np.isscalar(BDRF_Fourier_modes[m])
@@ -551,15 +586,16 @@ def interpolate(u):
551
586
  -------
552
587
  u_interpol : function
553
588
  Intensity function with arguments ``(mu, tau, phi)`` (for ``u``)
554
- or ``(mu, tau)`` (for ``u0``) each of type array or float.
589
+ or ``(mu, tau)`` (for ``u0``) each an array or scalar.
555
590
  Returns an ndarray with axes corresponding to variation with each argument in the same order.
556
591
  Pass ``is_antiderivative_wrt_tau = True`` (defaults to ``False``)
557
- to switch to an antiderivative of the function with respect to ``tau``.
592
+ to switch to an antiderivative of this function with respect to ``tau``.
558
593
  Pass ``return_Fourier_error = True`` (defaults to ``False``) to return the
559
594
  Cauchy / Fourier convergence evaluation (type: float) for the last Fourier term.
560
595
  Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
561
596
 
562
597
  """
598
+
563
599
  N = len(u(0, 0)) // 2
564
600
  mu_arr_pos = Gauss_Legendre_quad(N)[0]
565
601
 
@@ -774,18 +810,19 @@ def _mathscr_v(tau, # Input optical depths
774
810
  def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
775
811
  """Performs pointwise comparisons between results from Stamnes' DISORT,
776
812
  which are stored in ``.npz`` files, against results from PythonicDISORT. Used in our PyTests.
777
-
813
+ See the ``*_test`` Jupyter Notebooks to see how this function is used and the arguments that go into it.
814
+
778
815
  """
779
816
  # Load saved results from Stamnes' DISORT
780
817
  flup = results["flup"]
781
818
  rfldn = results["rfldn"]
782
819
  rfldir = results["rfldir"]
783
- if u != None:
820
+ if u is not None:
784
821
  uu = results["uu"]
785
822
 
786
823
  # Load comparison points
787
824
  tau_test_arr = results["tau_test_arr"]
788
- if u != None:
825
+ if u is not None:
789
826
  phi_arr = results["phi_arr"]
790
827
 
791
828
  # Perform and print the comparisons
@@ -800,7 +837,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
800
837
  diff_flux_up,
801
838
  flup,
802
839
  out=np.zeros_like(diff_flux_up),
803
- where=flup > 1e-8,
840
+ where=(flup != 0),
804
841
  )
805
842
  print("Difference =", np.max(diff_flux_up))
806
843
  print("Difference ratio =", np.max(ratio_flux_up))
@@ -813,7 +850,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
813
850
  diff_flux_down_diffuse,
814
851
  rfldn,
815
852
  out=np.zeros_like(diff_flux_down_diffuse),
816
- where=rfldn > 1e-8,
853
+ where=(rfldn != 0),
817
854
  )
818
855
  print("Difference =", np.max(diff_flux_down_diffuse))
819
856
  print(
@@ -829,7 +866,7 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
829
866
  diff_flux_down_direct,
830
867
  rfldir,
831
868
  out=np.zeros_like(diff_flux_down_direct),
832
- where=rfldir > 1e-8,
869
+ where=(rfldir != 0),
833
870
  )
834
871
  print("Difference =", np.max(diff_flux_down_direct))
835
872
  print(
@@ -838,14 +875,15 @@ def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
838
875
  )
839
876
  print()
840
877
 
841
- if u != None:
878
+ if u is not None:
842
879
  # Intensity
843
- diff = np.abs(uu - u(tau_test_arr, phi_arr)[reorder_mu])[mu_to_compare]
880
+ u_cache = u(tau_test_arr, phi_arr)[reorder_mu].reshape(np.shape(uu))
881
+ diff = np.abs(uu - u_cache)[mu_to_compare]
844
882
  diff_ratio = np.divide(
845
883
  diff,
846
884
  uu[mu_to_compare],
847
885
  out=np.zeros_like(diff),
848
- where=uu[mu_to_compare] > 1e-8,
886
+ where=(uu[mu_to_compare] != 0),
849
887
  )
850
888
  max_diff_tau_index = np.argmax(np.max(np.max(diff, axis=0), axis=1))
851
889
  max_ratio_tau_index = np.argmax(np.max(np.max(diff_ratio, axis=0), axis=1))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PythonicDISORT
3
- Version: 1.0.2
3
+ Version: 1.1
4
4
  Summary: Discrete Ordinates Solver for the (1D) Radiative Transfer Equation in a single or multi-layer atmosphere.
5
5
  Author-email: Dion HO Jia Xu <dh3065@columbia.edu>
6
6
  Project-URL: Homepage, https://github.com/LDEO-CREW/Pythonic-DISORT
@@ -0,0 +1,11 @@
1
+ PythonicDISORT/__init__.py,sha256=V1obNzaFgqInxqYJ5h_r6JHbZRxx4VM5VgzCIS6EEzI,84
2
+ PythonicDISORT/_assemble_intensity_and_fluxes.py,sha256=H7XxaRBHNIxNZsB49onFcU1hAsBIFWCeTBJ4NhoCT4w,29926
3
+ PythonicDISORT/_solve_for_coeffs.py,sha256=Bb-C5OJt355x8_v8WFB2sJutBtSTAFNIX4OxLQJk9-0,16539
4
+ PythonicDISORT/_solve_for_gen_and_part_sols.py,sha256=NVAFve6FNIEiku4xqaXBAI4G2HYIbhjcLC_B0nhGXXM,13131
5
+ PythonicDISORT/pydisort.py,sha256=jiPMLw-zcwRoZQq2IjZhAxiuHlhC3yY72wvAxAepfgg,39113
6
+ PythonicDISORT/subroutines.py,sha256=rf36t8coXQdnMb0NrEfgrOWDWZHkkH6KDIE3COueEbQ,34034
7
+ pythonicdisort-1.1.dist-info/LICENSE.md,sha256=q1i1QVh0OLzK0fj6kaNRNWsM_KZca66JwWgWwbuCPlA,1092
8
+ pythonicdisort-1.1.dist-info/METADATA,sha256=boJu4nE6zzVbJrwuVJWv2M3PTQhJpoS87XeS9M8ki2U,8308
9
+ pythonicdisort-1.1.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
10
+ pythonicdisort-1.1.dist-info/top_level.txt,sha256=aX6kCeI1E7naZz3xfw4_mIbWh5KBh9dmF3FIvgP5m3Y,15
11
+ pythonicdisort-1.1.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- PythonicDISORT/__init__.py,sha256=V1obNzaFgqInxqYJ5h_r6JHbZRxx4VM5VgzCIS6EEzI,84
2
- PythonicDISORT/_assemble_intensity_and_fluxes.py,sha256=M9mS2tD-GvMgze2MnHZEIarv7bpJsVe32fVPUEn0YpE,29911
3
- PythonicDISORT/_solve_for_coeffs.py,sha256=Bb-C5OJt355x8_v8WFB2sJutBtSTAFNIX4OxLQJk9-0,16539
4
- PythonicDISORT/_solve_for_gen_and_part_sols.py,sha256=_sPHgBTn58nOO0u13WCmCiDa6Qbv4HEu-7F_auFUcG4,13089
5
- PythonicDISORT/pydisort.py,sha256=xeHeW_XcQwSwLYTwYr2tnBUPnu0RqSTrOppO2KEHiT8,38865
6
- PythonicDISORT/subroutines.py,sha256=BFd0RqtmqzYF8J_ZE8KZB7GE5cl6ARHzbxqy0ewulbg,32517
7
- pythonicdisort-1.0.2.dist-info/LICENSE.md,sha256=q1i1QVh0OLzK0fj6kaNRNWsM_KZca66JwWgWwbuCPlA,1092
8
- pythonicdisort-1.0.2.dist-info/METADATA,sha256=-AEj3RRB4SbPh8aydWQtNmBlORzSEhjQ-kEhTjy9WBk,8310
9
- pythonicdisort-1.0.2.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
10
- pythonicdisort-1.0.2.dist-info/top_level.txt,sha256=aX6kCeI1E7naZz3xfw4_mIbWh5KBh9dmF3FIvgP5m3Y,15
11
- pythonicdisort-1.0.2.dist-info/RECORD,,