PythonicDISORT 1.0.0__tar.gz → 1.0.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PythonicDISORT
3
- Version: 1.0.0
3
+ Version: 1.0.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
@@ -8,7 +8,7 @@ dependencies = [
8
8
  "scipy>=1.8.0",
9
9
  ]
10
10
  name = "PythonicDISORT"
11
- version = "1.0.0"
11
+ version = "1.0.1"
12
12
  authors = [
13
13
  { name="Dion HO Jia Xu", email="dh3065@columbia.edu" },
14
14
  ]
@@ -592,6 +592,6 @@ def _assemble_intensity_and_fluxes(
592
592
  # --------------------------------------------------------------------------------------------------------------------------
593
593
 
594
594
  if only_flux:
595
- return flux_up, flux_down, u0#, GC_collect_0, K_collect_0
595
+ return flux_up, flux_down, u0#, GC_collect_0, K_collect_0, B_collect_0
596
596
  else:
597
597
  return flux_up, flux_down, u0, u
@@ -82,12 +82,12 @@ def _solve_for_coeffs(
82
82
  GC_collect = np.empty((NFourier, NLayers, NQuad, NQuad))
83
83
  use_banded_solver = (NLayers >= use_banded_solver_NLayers)
84
84
  if use_banded_solver:
85
- sym_offset = 3 * N - 1
85
+ Nsupsubdiags = 3 * N - 1 # The number of super-diagonals equals the number of sub-diagonals
86
86
 
87
87
  # The following loops can easily be parallelized, but the speed-up is unlikely to be worth the overhead
88
88
  for m in range(NFourier):
89
89
  m_equals_0 = (m == 0)
90
- BDRF_bool = (NBDRF > m)
90
+ there_is_BDRF_mode = (NBDRF > m)
91
91
 
92
92
  G_collect_m = G_collect[m, :, :, :]
93
93
  K_collect_m = K_collect[m, :, :]
@@ -97,7 +97,7 @@ def _solve_for_coeffs(
97
97
  # Generate mathscr_D and mathscr_X (BDRF terms)
98
98
  # Just for this part, refer to section 3.4.2 of the Comprehensive Documentation
99
99
  # --------------------------------------------------------------------------------------------------------------------------
100
- if BDRF_bool:
100
+ if there_is_BDRF_mode:
101
101
  mathscr_D_neg = (1 + m_equals_0 * 1) * BDRF_Fourier_modes[m](mu_arr_pos, mu_arr_pos)
102
102
  R = mathscr_D_neg * mu_arr_pos_times_W[None, :]
103
103
 
@@ -212,7 +212,7 @@ def _solve_for_coeffs(
212
212
  * np.exp(-scaled_tau_arr_with_0 / mu0)[l_range, None]
213
213
  ).ravel()
214
214
 
215
- if BDRF_bool:
215
+ if there_is_BDRF_mode:
216
216
  RHS = (
217
217
  np.concatenate(
218
218
  [
@@ -260,7 +260,7 @@ def _solve_for_coeffs(
260
260
  G_0_np
261
261
  * np.exp(K_collect_m[0, :N] * scaled_tau_arr_with_0[1])[None, :]
262
262
  )
263
- if BDRF_bool:
263
+ if there_is_BDRF_mode:
264
264
  LHS[-N:, -NQuad : -N] = (G_L_pn - R @ G_L_nn) * E_Lm1L[None, :]
265
265
  LHS[-N:, -N:] = G_L_pp - R @ G_L_np
266
266
  else:
@@ -298,8 +298,8 @@ def _solve_for_coeffs(
298
298
  # Solve the system
299
299
  if use_banded_solver:
300
300
  C_m = sc.linalg.solve_banded(
301
- (sym_offset, sym_offset),
302
- to_diag_ordered_form(LHS, sym_offset),
301
+ (Nsupsubdiags, Nsupsubdiags),
302
+ to_diag_ordered_form(LHS, Nsupsubdiags, Nsupsubdiags),
303
303
  RHS,
304
304
  True,
305
305
  True,
@@ -153,7 +153,7 @@ def _solve_for_gen_and_part_sols(
153
153
  alpha_arr[ind, :, :] = alpha
154
154
  beta_arr[ind, :, :] = beta
155
155
  no_shortcut_indices.append(ind)
156
- if m_equals_0 and there_is_iso_source:
156
+ if m_equals_0 and there_is_iso_source: # Keep the list empty if there is no isotropic source
157
157
  no_shortcut_indices_0.append(l)
158
158
 
159
159
  else:
@@ -192,7 +192,7 @@ def _solve_for_gen_and_part_sols(
192
192
  / K_arr[:, None, :]
193
193
  )
194
194
 
195
- # Eigenvector matrix
195
+ # Eigenvector matrices
196
196
  G_arr = np.concatenate(
197
197
  (
198
198
  (eigenvecs_GpG_arr - eigenvecs_GmG_arr) / 2,
@@ -203,7 +203,7 @@ def _solve_for_gen_and_part_sols(
203
203
 
204
204
  G_collect[no_shortcut_indices, :, :] = G_arr
205
205
  K_collect[no_shortcut_indices, :] = K_arr
206
- if len(no_shortcut_indices_0) > 0:
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
208
  G_collect[: len(no_shortcut_indices_0), :, :]
209
209
  )
@@ -44,7 +44,7 @@ def pydisort(
44
44
  omega_arr : array or float
45
45
  Single-scattering albedo of each atmospheric layer.
46
46
  NQuad : int
47
- Number of ``mu`` quadrature nodes.
47
+ Number of ``mu`` quadrature nodes, i.e. number of streams.
48
48
  Leg_coeffs_all : 2darray
49
49
  All available unweighted phase function Legendre coefficients.
50
50
  Each row pertains to an atmospheric layer (from top to bottom).
@@ -61,10 +61,10 @@ def pydisort(
61
61
  NFourier : optional, int
62
62
  Number of Fourier modes to use to construct the intensity function.
63
63
  b_pos : optional, 2darray or float
64
- Dirichlet boundary condition for the upward direction.
64
+ Dirichlet condition at the bottom boundary for the upward direction.
65
65
  Each column pertains to a Fourier mode (ascending order).
66
66
  b_neg : optional, 2darray or float
67
- Dirichlet boundary condition for the downward direction.
67
+ Dirichlet condition at the top boundary for the downward direction.
68
68
  Each column pertains to a Fourier mode (ascending order).
69
69
  only_flux : optional, bool
70
70
  Do NOT compute the intensity function?
@@ -86,7 +86,7 @@ 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
  -------
@@ -97,9 +97,7 @@ def pydisort(
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
99
  to switch to an antiderivative of the function with respect to ``tau``.
100
- For example, ``Fp(1, is_antiderivative_wrt_tau=True) - Fp(0, is_antiderivative_wrt_tau=True)``
101
- will produce the tau-integral over ``[1, 0]`` assuming both values belong to the same layer, and
102
- ``Fp(tau_arr, is_antiderivative_wrt_tau = True) - Fp(np.insert(tau_arr[:-1] + 1e-15, 0, 0), is_antiderivative_wrt_tau = True)``
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)``
103
101
  will produce an array of the tau-integral over each layer.
104
102
  Pass ``return_tau_arr`` to return ``tau_arr`` (defaults to ``False``).
105
103
  Fm(tau) : function
@@ -234,8 +232,8 @@ def pydisort(
234
232
  raise ValueError("The number of phase function Legendre coefficients must be positive.")
235
233
  if not np.all(omega_arr * Leg_coeffs_all[:, 0] == omega_arr):
236
234
  raise ValueError("The first phase function Legendre coefficient must equal 1.")
237
- if not (np.all(0 <= Leg_coeffs_all) and np.all(Leg_coeffs_all <= 1)):
238
- raise ValueError("The phase function Legendre coefficients must all be between 0 and 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.")
239
237
  if not NLeg <= NLeg_all:
240
238
  raise ValueError("`NLeg` cannot be larger than the number of phase function Legendre coefficients provided.")
241
239
  # Ensure that the first dimension of the following inputs corresponds to the number of layers
@@ -265,9 +263,9 @@ def pydisort(
265
263
  if I0 < 0:
266
264
  raise ValueError("The incident beam must have positive intensity.")
267
265
  if there_is_beam_source:
268
- if not 0 < mu0 and mu0 <= 1:
266
+ if not (0 < mu0 and mu0 <= 1):
269
267
  raise ValueError("The cosine of the polar angle of the incident beam must be between 0 and 1, excluding 0.")
270
- if not 0 <= phi0 and phi0 < 2 * pi:
268
+ if not (0 <= phi0 and phi0 < 2 * pi):
271
269
  raise ValueError("Provide the principal azimuthal angle for the incident beam (must be between 0 and 2pi, excluding 2pi).")
272
270
  # Ensure that the BC inputs are of the correct shape
273
271
  if len(np.atleast_1d(b_pos)) == 1:
@@ -299,14 +299,14 @@ def Planck(T, WVNM):
299
299
 
300
300
  Parameters
301
301
  ----------
302
- T : float or 1darray
302
+ T : float or array
303
303
  Temperatures in kelvin.
304
304
  WVNM : float
305
305
  Wavenumber with units m^-1.
306
306
 
307
307
  Returns
308
308
  -------
309
- float or 1darray
309
+ float or array
310
310
  Emitted power per unit area from a blackbody surface with units W / m^2.
311
311
 
312
312
  """
@@ -325,14 +325,15 @@ def Planck(T, WVNM):
325
325
 
326
326
 
327
327
  def blackbody_contrib_to_BCs(T, WVNMLO, WVNMHI, **kwargs):
328
- """Compute blackbody contribution to the BCs (``b_pos`` or ``b_neg``).
328
+ """Compute blackbody contribution to each BC (``b_pos`` or ``b_neg``),
329
+ i.e. the blackbody emission of that boundary, with units W / m^2.
329
330
  This convenience function is provided to help match the inputs for Stamnes' DISORT to those for PythonicDISORT.
330
331
  Users will have to manually adjust emissivities but ``PythonicDISORT.subroutines.generate_emissivity_from_BDRF``
331
332
  can help with that.
332
333
 
333
334
  Parameters
334
335
  ----------
335
- T : float or 1darray
336
+ T : float or array
336
337
  Temperatures in kelvin.
337
338
  WVNMLO : float
338
339
  Lower bound of wavenumber interval with units m^-1. This variable is identically named in Stamnes' DISORT.
@@ -341,6 +342,10 @@ def blackbody_contrib_to_BCs(T, WVNMLO, WVNMHI, **kwargs):
341
342
  **kwargs
342
343
  Keyword arguments to pass to ``scipy.integrate.quad_vec``.
343
344
 
345
+ Returns
346
+ -------
347
+ float or array
348
+ The blackbody emission of each boundary with units W / m^2.
344
349
  """
345
350
  return np.squeeze(sc.integrate.quad_vec(lambda WVNM: Planck(T, WVNM), WVNMLO, WVNMHI, **kwargs)[0])
346
351
 
@@ -351,15 +356,15 @@ def linear_spline_coefficients(x, y, check_inputs=True):
351
356
 
352
357
  Parameters
353
358
  ----------
354
- x : 1darray
359
+ x : array
355
360
  Array of `x` data points.
356
- y : 1darray
361
+ y : array
357
362
  Array of `y` data points.
358
363
 
359
364
  Returns
360
365
  -------
361
366
  2darray
362
- The coefficients with axes (segment, ascending polynomial order), as required by ``pydisort``.
367
+ The coefficients with axes (segment, ascending polynomial order) which is required by ``pydisort``.
363
368
 
364
369
  """
365
370
  # Input checks
@@ -379,15 +384,16 @@ def linear_spline_coefficients(x, y, check_inputs=True):
379
384
 
380
385
 
381
386
  def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, **kwargs):
382
- """Generate ``s_poly_coeffs`` as required by ``pydisort``.
383
- This convenience function is provided to help match the inputs for Stamnes' DISORT to those for PythonicDISORT,
384
- however, users will have to manually adjust emissivities.
387
+ """Generate DISORT-equivalent ``s_poly_coeffs`` for input into ``pydisort``.
388
+ 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.
385
391
 
386
392
  Parameters
387
393
  ----------
388
394
  tau_arr : array or float
389
395
  Optical depth of the lower boundary of each atmospheric layer.
390
- TEMPER : 1darray
396
+ TEMPER : array
391
397
  Temperature in kelvin at each boundary / interface from top to bottom.
392
398
  This variable is identically named in Stamnes' DISORT.
393
399
  WVNMLO : float
@@ -419,7 +425,7 @@ def generate_s_poly_coeffs(tau_arr, TEMPER, WVNMLO, WVNMHI, **kwargs):
419
425
  def generate_emissivity_from_BDRF(N, zeroth_BDRF_Fourier_mode):
420
426
  """Use Kirchoff's law of thermal radiation to determine the (directional) emissivity
421
427
  of the surface given the zeroth Fourier mode of the Bi-Directional Reflectance Function (BDRF).
422
- This computation is internal to Stamnes' DISORT.
428
+ This computation is automatic and internal to Stamnes' DISORT.
423
429
  This function supplements ``PythonicDISORT.subroutines.blackbody_contrib_to_BCs``.
424
430
 
425
431
  Parameters
@@ -432,8 +438,8 @@ def generate_emissivity_from_BDRF(N, zeroth_BDRF_Fourier_mode):
432
438
 
433
439
  Returns
434
440
  -------
435
- 1darray
436
- Emissivity for the blackbody contribution to the lower boundary source `b_neg``.
441
+ array
442
+ Emissivity for the blackbody contribution to the lower boundary source ``b_neg``.
437
443
  """
438
444
  mu_arr_pos, W = Gauss_Legendre_quad(N)
439
445
  return 1 - 2 * zeroth_BDRF_Fourier_mode(mu_arr_pos, mu_arr_pos) * mu_arr_pos[None, :] @ W
@@ -488,9 +494,9 @@ def affine_transform_poly_coeffs(poly_coeffs, a_arr, b_arr):
488
494
  poly_coeffs : 2darray
489
495
  Array with columns [C_0, C_1, ..., C_n].
490
496
  The rows correspond to different affine transformations.
491
- a_arr : 1darray
497
+ a_arr : array
492
498
  Scale factors.
493
- b_arr : 1darray
499
+ b_arr : array
494
500
  Translations.
495
501
 
496
502
  Returns
@@ -518,10 +524,11 @@ def affine_transform_poly_coeffs(poly_coeffs, a_arr, b_arr):
518
524
  return np.einsum("lij, lj -> li", T, poly_coeffs, optimize=True)
519
525
 
520
526
 
527
+
521
528
  def interpolate(u):
522
- """Polynomial (Barycentric) interpolation with respect to mu. The output
523
- is a function that is continuous and variable in all three arguments: mu, tau and phi.
524
- Discussed in sections 3.7 and 6.3 in the Comprehensive Documentation.
529
+ """Polynomial (Barycentric) interpolation with respect to ``mu``. The output
530
+ is a function that is continuous and variable in all three arguments: ``mu``, ``tau`` and ``phi``.
531
+ Discussed in sections 3.7 and 6.3 of the Comprehensive Documentation.
525
532
 
526
533
  Parameters
527
534
  ----------
@@ -611,22 +618,23 @@ def interpolate(u):
611
618
 
612
619
 
613
620
 
614
- def to_diag_ordered_form(A, sym_offset):
621
+ def to_diag_ordered_form(A, Nsuperdiags, Nsubdiags):
615
622
  """
616
- Convert a matrix A to the diagonal ordered form required by ``scipy.linalg.solve_banded``.
617
- We assume that the matrix has the same number of super- and sub-diagonals.
623
+ Convert some matrix A to the diagonal ordered form required by ``scipy.linalg.solve_banded``.
618
624
 
619
625
  Parameters
620
626
  ----------
621
627
  A : 2darray
622
628
  The square matrix to be converted.
623
- sym_offset : int
624
- The number of super- or sub-diagonals (assumed to be equal)
629
+ Nsuperdiags : int
630
+ The number of super-diagonals.
631
+ Nsubdiags : int
632
+ The number of sub-diagonals.
625
633
 
626
634
  Returns
627
635
  -------
628
636
  2darray
629
- The diagonal ordered form matrix as required by solve_banded.
637
+ The diagonal ordered form matrix as required by ``scipy.linalg.solve_banded``.
630
638
  """
631
639
  n = A.shape[0]
632
640
  indices = np.arange(n)
@@ -634,28 +642,29 @@ def to_diag_ordered_form(A, sym_offset):
634
642
  return np.concatenate(
635
643
  [
636
644
  A[
637
- indices - np.arange(sym_offset, -1, -1)[:, None],
645
+ indices - np.arange(Nsuperdiags, -1, -1)[:, None],
638
646
  indices[None, :],
639
647
  ],
640
648
  A[
641
- indices - np.arange(n - 1, n - sym_offset - 1, -1)[:, None],
649
+ indices - np.arange(n - 1, n - Nsubdiags - 1, -1)[:, None],
642
650
  indices[None, :],
643
651
  ],
644
652
  ],
645
653
  axis=0,
646
654
  )
647
655
 
656
+
648
657
 
649
- def _mathscr_v(tau, # Input optical depths
650
- l, # Layer index of each input optical depth
651
- Nscoeffs, # Number of isotropic source polynomial coefficients
652
- s_poly_coeffs, # Polynomial coefficients of isotropic source
653
- G, # Eigenvector matrices
654
- K, # Eigenvalues
655
- G_inv, # Inverse of eigenvector matrix
656
- mu_arr, # Quadrature nodes for both hemispheres
658
+ def _mathscr_v(tau, # Input optical depths
659
+ l, # Layer index of each input optical depth
660
+ Nscoeffs, # Number of isotropic source polynomial coefficients
661
+ s_poly_coeffs, # Polynomial coefficients of isotropic source
662
+ G, # Eigenvector matrices
663
+ K, # Eigenvalues
664
+ G_inv, # Inverse of eigenvector matrix
665
+ mu_arr, # Quadrature nodes for both hemispheres
657
666
  is_antiderivative_wrt_tau=False, # Switch to an antiderivative of the function?
658
- autograd_compatible=False, # Should the output functions be compatible with autograd?
667
+ autograd_compatible=False, # Should the output functions be compatible with autograd?
659
668
  ):
660
669
  """Particular solution for isotropic internal sources.
661
670
  Refer to section 3.6.1 of the Comprehensive Documentation.
@@ -664,22 +673,22 @@ def _mathscr_v(tau, # Input optical depths
664
673
  and ``_solve_for_coeffs`` functions which call it.
665
674
 
666
675
  Arguments of _mathscr_v
667
- | Variable | Shape |
668
- | --------------------------- | ------------------------------------- |
669
- | ``tau`` | ``Ntau`` |
670
- | ``l`` | ``Ntau`` |
671
- | ``Nscoeffs`` | scalar |
672
- | ``s_poly_coeffs`` | ``NLayers x Nscoeffs`` or ``Nscoeffs`` |
673
- | ``G`` | ``NLayers<= x NQuad x NQuad`` |
674
- | ``K`` | ``NLayers<= x NQuad`` |
676
+ | Variable | Shape |
677
+ | ----------------------------- | ----------------------------------------- |
678
+ | ``tau`` | ``Ntau`` |
679
+ | ``l`` | ``Ntau`` |
680
+ | ``Nscoeffs`` | scalar |
681
+ | ``s_poly_coeffs`` | ``NLayers x Nscoeffs`` |
682
+ | ``G`` | ``NLayers<= x NQuad x NQuad`` |
683
+ | ``K`` | ``NLayers<= x NQuad`` |
675
684
  | ``G_inv`` | ``NLayers<= x NQuad x NQuad`` or ``None`` |
676
- | ``mu_arr`` | ``NQuad`` |
677
- | ``is_antiderivative_wrt_tau`` | boolean |
678
- | ``autograd_compatible`` | boolean |
685
+ | ``mu_arr`` | ``NQuad`` |
686
+ | ``is_antiderivative_wrt_tau`` | boolean |
687
+ | ``autograd_compatible`` | boolean |
679
688
 
680
689
  Notable internal variables of _mathscr_v
681
- | Variable | Shape |
682
- | ---------------- | ----------------------------------- |
690
+ | Variable | Shape |
691
+ | ---------------- | ------------------------------------- |
683
692
  | i_arr | ``Nscoeffs`` |
684
693
  | i_arr_repeat | ``Nscoeffs*(Nscoeffs+1)/2`` | Using triangular number formula
685
694
  | j_arr | ``Nscoeffs*(Nscoeffs+1)/2`` | Using triangular number formula
@@ -694,8 +703,8 @@ def _mathscr_v(tau, # Input optical depths
694
703
  def mathscr_b(i):
695
704
  """
696
705
  Notable internal variables of mathscr_b
697
- | Variable | Shape |
698
- | ---------------- | ------------------------------------- |
706
+ | Variable | Shape |
707
+ | ---------------- | --------------------------------------- |
699
708
  | j_arr | ``i + 1`` |
700
709
  | s_poly_coeffs_nj | ``i + 1`` |
701
710
  | OUTPUT | ``Nscoeffs x (i + 1) x NQuad`` |
@@ -716,7 +725,7 @@ def _mathscr_v(tau, # Input optical depths
716
725
  shape = np.shape(K)
717
726
  i_arr = np.arange(Nscoeffs)
718
727
  i_arr_repeat = np.repeat(i_arr, i_arr + 1)
719
- j_arr = np.concatenate([np.arange(i + 1) for i in range(Nscoeffs)])
728
+ j_arr = np.concatenate([i_arr[:i] for i in range(1, Nscoeffs + 1)])
720
729
  s_poly_coeffs_nj = s_poly_coeffs[:, n - j_arr]
721
730
 
722
731
  mathscr_v_coeffs = np.zeros((Nscoeffs, shape[0], shape[1]))
@@ -752,7 +761,7 @@ def _mathscr_v(tau, # Input optical depths
752
761
 
753
762
  def _compare(results, mu_to_compare, reorder_mu, flux_up, flux_down, u=None):
754
763
  """Performs pointwise comparisons between results from Stamnes' DISORT,
755
- which are stored in .npz files, against results from PythonicDISORT. Used in our PyTests.
764
+ which are stored in ``.npz`` files, against results from PythonicDISORT. Used in our PyTests.
756
765
 
757
766
  """
758
767
  # Load saved results from Stamnes' DISORT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PythonicDISORT
3
- Version: 1.0.0
3
+ Version: 1.0.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
File without changes
File without changes