PythonicDISORT 0.8.0__tar.gz → 0.9.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: 0.8.0
3
+ Version: 0.9.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
@@ -28,8 +28,10 @@ On the other hand, PythonicDISORT should be easier to install, use, and modify t
28
28
 
29
29
  PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8]) and has its main features: multi-layer solver,
30
30
  delta-M scaling, Nakajima-Tanaka (NT) corrections, only flux option, direct beam source, isotropic internal source (blackbody emission),
31
- Dirichlet boundary conditions (diffuse flux boundary sources), Bi-Directional Reflectance Function (BDRF) for surface reflection, and more.
32
- In addition, subroutine to calculate actinic fluxes was added to satisfy a user request and further feature requests as well as feedback are welcome.
31
+ Dirichlet boundary conditions (diffuse flux boundary sources), Bi-Directional Reflectance Function (BDRF) for surface reflection,
32
+ interpolation with respect to polar angle and more.
33
+ In addition, we added a subroutine to calculate actinic fluxes to satisfy a user request, and integration with respect to tau was also added.
34
+ Further feature requests as well as feedback are welcome.
33
35
 
34
36
  You may contact me, Dion, through dh3065@columbia.edu.
35
37
 
@@ -50,8 +52,8 @@ Not only are there verification tests in `Pythonic-DISORT.ipynb`,
50
52
  most of the test problems in Stamnes' `disotest.f90` (download DISORT 4.0.99 from http://www.rtatmocn.com/disort/) have also been recreated.
51
53
  In these tests, the solutions from PythonicDISORT are compared against solutions
52
54
  from a F2PY-wrapped Stamnes' DISORT (version 4.0.99; wrapper inspired by https://github.com/kconnour/pyRT_DISORT). With PyTest installed, execute the console command `pytest`
53
- in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks that correspond to each test
54
- to show how it was implemented. These notebooks double up as examples of how to use PythonicDISORT.
55
+ in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks to show the implementation of each test.
56
+ These notebooks double up as examples of how to use PythonicDISORT.
55
57
 
56
58
  # Installation
57
59
 
@@ -7,8 +7,10 @@ On the other hand, PythonicDISORT should be easier to install, use, and modify t
7
7
 
8
8
  PythonicDISORT is based on Stamnes' FORTRAN DISORT (see References, in particular [2, 3, 8]) and has its main features: multi-layer solver,
9
9
  delta-M scaling, Nakajima-Tanaka (NT) corrections, only flux option, direct beam source, isotropic internal source (blackbody emission),
10
- Dirichlet boundary conditions (diffuse flux boundary sources), Bi-Directional Reflectance Function (BDRF) for surface reflection, and more.
11
- In addition, subroutine to calculate actinic fluxes was added to satisfy a user request and further feature requests as well as feedback are welcome.
10
+ Dirichlet boundary conditions (diffuse flux boundary sources), Bi-Directional Reflectance Function (BDRF) for surface reflection,
11
+ interpolation with respect to polar angle and more.
12
+ In addition, we added a subroutine to calculate actinic fluxes to satisfy a user request, and integration with respect to tau was also added.
13
+ Further feature requests as well as feedback are welcome.
12
14
 
13
15
  You may contact me, Dion, through dh3065@columbia.edu.
14
16
 
@@ -29,8 +31,8 @@ Not only are there verification tests in `Pythonic-DISORT.ipynb`,
29
31
  most of the test problems in Stamnes' `disotest.f90` (download DISORT 4.0.99 from http://www.rtatmocn.com/disort/) have also been recreated.
30
32
  In these tests, the solutions from PythonicDISORT are compared against solutions
31
33
  from a F2PY-wrapped Stamnes' DISORT (version 4.0.99; wrapper inspired by https://github.com/kconnour/pyRT_DISORT). With PyTest installed, execute the console command `pytest`
32
- in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks that correspond to each test
33
- to show how it was implemented. These notebooks double up as examples of how to use PythonicDISORT.
34
+ in the `pydisotest` directory to run these tests. The `pydisotest` directory also contains Jupyter Notebooks to show the implementation of each test.
35
+ These notebooks double up as examples of how to use PythonicDISORT.
34
36
 
35
37
  # Installation
36
38
 
@@ -8,7 +8,7 @@ dependencies = [
8
8
  "scipy>=1.8.0",
9
9
  ]
10
10
  name = "PythonicDISORT"
11
- version = "0.8.0"
11
+ version = "0.9.1"
12
12
  authors = [
13
13
  { name="Dion HO Jia Xu", email="dh3065@columbia.edu" },
14
14
  ]
@@ -20,12 +20,12 @@ def _assemble_intensity_and_fluxes(
20
20
  there_is_beam_source, # Is there a beam source?
21
21
  b_pos, b_neg, # Dirichlet BCs
22
22
  b_pos_is_scalar, b_neg_is_scalar, # Is each Dirichlet BCs scalar (isotropic)?
23
- s_poly_coeffs, # Polynomial coefficients of isotropic source
24
23
  Nscoeffs, # Number of isotropic source polynomial coefficients
24
+ s_poly_coeffs, # Polynomial coefficients of isotropic source
25
25
  there_is_iso_source, # Is there an isotropic source?
26
26
  scale_tau, # Delta-scale factor for tau
27
27
  only_flux, # Only compute fluxes?
28
- NLayers_to_use_sparse, # Number of layers above or equal which to use sparse matrices
28
+ use_banded_solver_NLayers, # Number of layers above or equal which to use `scipy.linalg.solve_banded`
29
29
  _autograd_compatible, # Should the output functions be compatible with autograd?
30
30
  ):
31
31
  """Assembles the solution functions: intensity `u`, upward flux `flux_up`, downward flux `flux_down`
@@ -66,13 +66,13 @@ def _assemble_intensity_and_fluxes(
66
66
  | `b_pos` | `NQuad/2 x NFourier` or scalar |
67
67
  | `b_neg` | `NQuad/2 x NFourier` or scalar |
68
68
  | `b_pos_is_scalar` | boolean |
69
- | `b_neg_is_scalar` | boolean |
69
+ | `b_neg_is_scalar` | boolean |
70
+ | `Nscoeffs` | scalar |
70
71
  | `s_poly_coeffs` | `NLayers x Nscoeffs` or `Nscoeffs` |
71
- | `Nscoeffs` | scalar |
72
72
  | `there_is_iso_source` | boolean |
73
73
  | `scale_tau` | `NLayers` |
74
74
  | `only_flux` | boolean |
75
- | `NLayers_to_use_sparse` | scalar |
75
+ | `use_banded_solver_NLayers` | scalar |
76
76
  | `_autograd_compatible` | boolean |
77
77
 
78
78
  Notable internal variables of _assemble_intensity_and_fluxes
@@ -146,10 +146,10 @@ def _assemble_intensity_and_fluxes(
146
146
  there_is_beam_source,
147
147
  b_pos, b_neg,
148
148
  b_pos_is_scalar, b_neg_is_scalar,
149
- s_poly_coeffs,
150
149
  Nscoeffs,
150
+ s_poly_coeffs,
151
151
  there_is_iso_source,
152
- NLayers_to_use_sparse,
152
+ use_banded_solver_NLayers,
153
153
  )
154
154
 
155
155
  G_collect_0 = G_collect[0, :, :, :]
@@ -198,26 +198,28 @@ def _assemble_intensity_and_fluxes(
198
198
 
199
199
  if is_antiderivative_wrt_tau:
200
200
  if there_is_beam_source:
201
- um = np.einsum(
202
- "mtij, mtj -> mti",
203
- GC_collect[:, l, :, :],
204
- np.exp(exponent) / (scale_tau[None, l, None] * K_collect[:, l, :]),
205
- optimize=True,
206
- ) + B_collect[:, l, :] * np.exp(-scaled_tau[None, :, None] / mu0) / (
207
- -scale_tau[None, l, None] / mu0
201
+ um = (
202
+ np.einsum(
203
+ "mtij, mtj -> mti",
204
+ GC_collect[:, l, :, :],
205
+ np.exp(exponent) / (scale_tau[None, :, None] * K_collect)[:, l, :],
206
+ optimize=True,
207
+ )
208
+ + (B_collect / (-scale_tau / mu0)[None, :, None])[:, l, :]
209
+ * np.exp(-scaled_tau / mu0)[None, :, None]
208
210
  )
209
211
  else:
210
212
  um = np.einsum(
211
213
  "mtij, mtj -> mti",
212
214
  GC_collect[:, l, :, :],
213
- np.exp(exponent) / (scale_tau[None, l, None] * K_collect[:, l, :]),
215
+ np.exp(exponent) / (scale_tau[None, l, None] * K_collect)[:, l, :],
214
216
  optimize=True,
215
217
  )
216
218
  else:
217
219
  if there_is_beam_source:
218
220
  um = np.einsum(
219
221
  "mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
220
- ) + B_collect[:, l, :] * np.exp(-scaled_tau[None, :, None] / mu0)
222
+ ) + B_collect[:, l, :] * np.exp(-scaled_tau / mu0)[None, :, None]
221
223
  else:
222
224
  um = np.einsum(
223
225
  "mtij, mtj -> mti", GC_collect[:, l, :, :], np.exp(exponent), optimize=True
@@ -225,20 +227,23 @@ def _assemble_intensity_and_fluxes(
225
227
 
226
228
  # Contribution from particular solution for isotropic internal sources
227
229
  if there_is_iso_source:
230
+ l_uniq, l_inv = np.unique(l, return_inverse=True)
228
231
  _mathscr_v_contribution = _mathscr_v(
229
- tau, l,
230
- s_poly_coeffs,
231
- Nscoeffs,
232
- G_collect_0,
233
- K_collect_0,
234
- G_inv_collect_0,
232
+ tau, l_inv, Nscoeffs,
233
+ s_poly_coeffs[l_uniq],
234
+ G_collect_0[l_uniq],
235
+ K_collect_0[l_uniq],
236
+ G_inv_collect_0[l_uniq],
235
237
  mu_arr,
236
- _autograd_compatible,
237
- is_antiderivative_wrt_tau
238
+ is_antiderivative_wrt_tau,
239
+ _autograd_compatible
238
240
  )
239
241
 
240
242
  if _autograd_compatible:
241
- um = um + np.concatenate((_mathscr_v_contribution.T, np.zeros((NFourier - 1, len(tau), NQuad))))
243
+ if NFourier == 1:
244
+ um = um + _mathscr_v_contribution.T
245
+ else:
246
+ um = um + np.concatenate((_mathscr_v_contribution.T, np.zeros((NFourier - 1, len(tau), NQuad))))
242
247
  else:
243
248
  um[0, :, :] += _mathscr_v_contribution.T
244
249
 
@@ -259,26 +264,28 @@ def _assemble_intensity_and_fluxes(
259
264
  )
260
265
  if is_antiderivative_wrt_tau:
261
266
  if there_is_beam_source:
262
- ulast = np.einsum(
263
- "tij, tj -> it",
264
- GC_collect[-1, l, :, :],
265
- np.exp(exponent) / (scale_tau[l, None] * K_collect[-1, l, :]),
266
- optimize=True,
267
- ) + B_collect[-1, l, :].T * np.exp(-scaled_tau[None, :] / mu0) / (
268
- -scale_tau[None, l] / mu0
267
+ ulast = (
268
+ np.einsum(
269
+ "tij, tj -> it",
270
+ GC_collect[-1, l, :, :],
271
+ np.exp(exponent) / (scale_tau[:, None] * K_collect[-1, :, :])[l, :],
272
+ optimize=True,
273
+ )
274
+ + (B_collect.T[-1, :, :] / (-scale_tau / mu0)[None, :])[:, l]
275
+ * np.exp(-scaled_tau / mu0)[None, :]
269
276
  )
270
277
  else:
271
278
  ulast = np.einsum(
272
279
  "tij, tj -> it",
273
280
  GC_collect[-1, l, :, :],
274
- np.exp(exponent) / (scale_tau[l, None] * K_collect[-1, l, :]),
281
+ np.exp(exponent) / (scale_tau[:, None] * K_collect[-1, :, :])[l, :],
275
282
  optimize=True,
276
283
  )
277
284
  else:
278
285
  if there_is_beam_source:
279
286
  ulast = np.einsum(
280
287
  "tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
281
- ) + B_collect[-1, l, :].T * np.exp(-scaled_tau[None, :] / mu0)
288
+ ) + B_collect.T[-1, :, l] * np.exp(-scaled_tau / mu0)[None, :]
282
289
  else:
283
290
  ulast = np.einsum(
284
291
  "tij, tj -> it", GC_collect[-1, l, :, :], np.exp(exponent), optimize=True
@@ -330,7 +337,10 @@ def _assemble_intensity_and_fluxes(
330
337
  # in performing the reclassification of delta-scaled actinic flux
331
338
  if _return_act_dscale_for_reclass:
332
339
  if is_antiderivative_wrt_tau:
333
- act_dscale_reclassification = I0 * np.exp(-scaled_tau / mu0) / (-scale_tau[l] / mu0) - I0 * np.exp(-tau / mu0) * -mu0
340
+ act_dscale_reclassification = (
341
+ I0 * np.exp(-scaled_tau / mu0) / (-scale_tau[l] / mu0)
342
+ - I0 * np.exp(-tau / mu0) * -mu0
343
+ )
334
344
  else:
335
345
  act_dscale_reclassification = I0 * np.exp(-scaled_tau / mu0) - I0 * np.exp(-tau / mu0)
336
346
  else:
@@ -348,26 +358,28 @@ def _assemble_intensity_and_fluxes(
348
358
 
349
359
  if is_antiderivative_wrt_tau:
350
360
  if there_is_beam_source:
351
- u0 = np.einsum(
352
- "tij, tj -> it",
353
- GC_collect_0[l, :, :],
354
- np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
355
- optimize=True,
356
- ) + B_collect_0[l, :].T * np.exp(-scaled_tau[None, :] / mu0) / (
357
- -scale_tau[None, l] / mu0
361
+ u0 = (
362
+ np.einsum(
363
+ "tij, tj -> it",
364
+ GC_collect_0[l, :, :],
365
+ np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :],
366
+ optimize=True,
367
+ )
368
+ + (B_collect_0.T / (-scale_tau / mu0)[None, :])[:, l]
369
+ * np.exp(-scaled_tau / mu0)[None, :]
358
370
  )
359
371
  else:
360
372
  u0 = np.einsum(
361
373
  "tij, tj -> it",
362
374
  GC_collect_0[l, :, :],
363
- np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
375
+ np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :],
364
376
  optimize=True,
365
377
  )
366
378
  else:
367
379
  if there_is_beam_source:
368
380
  u0 = np.einsum(
369
381
  "tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
370
- ) + B_collect_0[l, :].T * np.exp(-scaled_tau[None, :] / mu0)
382
+ ) + B_collect_0.T[:, l] * np.exp(-scaled_tau / mu0)[None, :]
371
383
  else:
372
384
  u0 = np.einsum(
373
385
  "tij, tj -> it", GC_collect_0[l, :, :], np.exp(exponent), optimize=True
@@ -375,16 +387,16 @@ def _assemble_intensity_and_fluxes(
375
387
 
376
388
  # Contribution from particular solution for isotropic internal sources
377
389
  if there_is_iso_source:
390
+ l_uniq, l_inv = np.unique(l, return_inverse=True)
378
391
  _mathscr_v_contribution = _mathscr_v(
379
- tau, l,
380
- s_poly_coeffs,
381
- Nscoeffs,
382
- G_collect_0,
383
- K_collect_0,
384
- G_inv_collect_0,
392
+ tau, l_inv, Nscoeffs,
393
+ s_poly_coeffs[l_uniq],
394
+ G_collect_0[l_uniq],
395
+ K_collect_0[l_uniq],
396
+ G_inv_collect_0[l_uniq],
385
397
  mu_arr,
386
- _autograd_compatible,
387
- is_antiderivative_wrt_tau
398
+ is_antiderivative_wrt_tau,
399
+ _autograd_compatible
388
400
  )
389
401
  u0 = u0 + _mathscr_v_contribution
390
402
 
@@ -428,27 +440,27 @@ def _assemble_intensity_and_fluxes(
428
440
  scaled_tau = tau
429
441
 
430
442
  if there_is_iso_source:
443
+ l_uniq, l_inv = np.unique(l, return_inverse=True)
431
444
  _mathscr_v_contribution = _mathscr_v(
432
- tau, l,
433
- s_poly_coeffs,
434
- Nscoeffs,
435
- G_collect_0,
436
- K_collect_0,
437
- G_inv_collect_0,
445
+ tau, l_inv, Nscoeffs,
446
+ s_poly_coeffs[l_uniq],
447
+ G_collect_0[l_uniq, :N, :],
448
+ K_collect_0[l_uniq],
449
+ G_inv_collect_0[l_uniq],
438
450
  mu_arr,
439
- _autograd_compatible,
440
- is_antiderivative_wrt_tau
441
- )[:N, :]
451
+ is_antiderivative_wrt_tau,
452
+ _autograd_compatible
453
+ )
442
454
  else:
443
455
  _mathscr_v_contribution = 0
444
456
 
445
457
  if there_is_beam_source:
446
458
  if is_antiderivative_wrt_tau:
447
459
  direct_beam_contribution = (
448
- B_pos[:, l] * np.exp(-scaled_tau[None, :] / mu0) / (-scale_tau[None, l] / mu0)
460
+ (B_pos / (-scale_tau / mu0)[None, :])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
449
461
  )
450
462
  else:
451
- direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau[None, :] / mu0)
463
+ direct_beam_contribution = B_pos[:, l] * np.exp(-scaled_tau / mu0)[None, :]
452
464
  else:
453
465
  direct_beam_contribution = 0
454
466
 
@@ -465,7 +477,7 @@ def _assemble_intensity_and_fluxes(
465
477
  np.einsum(
466
478
  "tij, tj -> it",
467
479
  GC_pos[l, :, :],
468
- np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]),
480
+ np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :],
469
481
  )
470
482
  + direct_beam_contribution
471
483
  + _mathscr_v_contribution
@@ -507,27 +519,27 @@ def _assemble_intensity_and_fluxes(
507
519
  scaled_tau = tau
508
520
 
509
521
  if there_is_iso_source:
522
+ l_uniq, l_inv = np.unique(l, return_inverse=True)
510
523
  _mathscr_v_contribution = _mathscr_v(
511
- tau, l,
512
- s_poly_coeffs,
513
- Nscoeffs,
514
- G_collect_0,
515
- K_collect_0,
516
- G_inv_collect_0,
524
+ tau, l_inv, Nscoeffs,
525
+ s_poly_coeffs[l_uniq],
526
+ G_collect_0[l_uniq, N:, :],
527
+ K_collect_0[l_uniq],
528
+ G_inv_collect_0[l_uniq],
517
529
  mu_arr,
518
- _autograd_compatible,
519
- is_antiderivative_wrt_tau
520
- )[N:, :]
530
+ is_antiderivative_wrt_tau,
531
+ _autograd_compatible
532
+ )
521
533
  else:
522
534
  _mathscr_v_contribution = 0
523
535
 
524
536
  if there_is_beam_source:
525
537
  if is_antiderivative_wrt_tau:
526
- direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau[None, :] / mu0) / (-scale_tau[None, l] / mu0)
538
+ direct_beam_contribution = (B_neg / (-scale_tau / mu0)[None, l])[:, l] * np.exp(-scaled_tau / mu0)[None, :]
527
539
  direct_beam = I0 * mu0 * np.exp(-tau / mu0) * -mu0
528
- direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau[None, l] / mu0)
540
+ direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0) / (-scale_tau / mu0)[None, l]
529
541
  else:
530
- direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau[None, :] / mu0)
542
+ direct_beam_contribution = B_neg[:, l] * np.exp(-scaled_tau / mu0)[None, :]
531
543
  direct_beam = I0 * mu0 * np.exp(-tau / mu0)
532
544
  direct_beam_scaled = I0 * mu0 * np.exp(-scaled_tau / mu0)
533
545
  else:
@@ -545,7 +557,7 @@ def _assemble_intensity_and_fluxes(
545
557
 
546
558
  if is_antiderivative_wrt_tau:
547
559
  u0_neg = (
548
- np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent) / (scale_tau[l, None] * K_collect_0[l, :]))
560
+ np.einsum("tij, tj -> it", GC_neg[l, :, :], np.exp(exponent) / (scale_tau[:, None] * K_collect_0)[l, :])
549
561
  + direct_beam_contribution
550
562
  + _mathscr_v_contribution
551
563
  )