smartg 2.0.2__tar.gz → 2.0.4__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.
Files changed (134) hide show
  1. {smartg-2.0.2/smartg.egg-info → smartg-2.0.4}/PKG-INFO +6 -1
  2. {smartg-2.0.2 → smartg-2.0.4}/README.md +5 -0
  3. {smartg-2.0.2 → smartg-2.0.4}/pyproject.toml +1 -1
  4. {smartg-2.0.2 → smartg-2.0.4}/smartg/smartg.py +173 -22
  5. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/communs.h +0 -4
  6. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/device.cu +92 -96
  7. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/shapes.h +120 -40
  8. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/transform.h +0 -6
  9. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_iprt_phase3.py +23 -6
  10. smartg-2.0.4/smartg/tests/test_jax_context.py +46 -0
  11. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_objects3d.py +7 -3
  12. smartg-2.0.4/smartg/tests/test_shapes.py +326 -0
  13. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_smartg.py +173 -1
  14. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_smartg_host.py +12 -0
  15. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_smartg_jax.py +28 -0
  16. smartg-2.0.4/smartg/tests/test_surface.py +328 -0
  17. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_view.py +20 -0
  18. {smartg-2.0.2 → smartg-2.0.4}/smartg/view.py +7 -0
  19. {smartg-2.0.2 → smartg-2.0.4/smartg.egg-info}/PKG-INFO +6 -1
  20. {smartg-2.0.2 → smartg-2.0.4}/smartg.egg-info/SOURCES.txt +2 -0
  21. smartg-2.0.2/smartg/tests/test_surface.py +0 -47
  22. {smartg-2.0.2 → smartg-2.0.4}/LICENSE.TXT +0 -0
  23. {smartg-2.0.2 → smartg-2.0.4}/LICENSE_RNG.TXT +0 -0
  24. {smartg-2.0.2 → smartg-2.0.4}/MANIFEST.in +0 -0
  25. {smartg-2.0.2 → smartg-2.0.4}/setup.cfg +0 -0
  26. {smartg-2.0.2 → smartg-2.0.4}/smartg/__init__.py +0 -0
  27. {smartg-2.0.2 → smartg-2.0.4}/smartg/albedo.py +0 -0
  28. {smartg-2.0.2 → smartg-2.0.4}/smartg/atmosphere.py +0 -0
  29. {smartg-2.0.2 → smartg-2.0.4}/smartg/auxdata.py +0 -0
  30. {smartg-2.0.2 → smartg-2.0.4}/smartg/bandset.py +0 -0
  31. {smartg-2.0.2 → smartg-2.0.4}/smartg/cdf.py +0 -0
  32. {smartg-2.0.2 → smartg-2.0.4}/smartg/config.py +0 -0
  33. {smartg-2.0.2 → smartg-2.0.4}/smartg/conftest.py +0 -0
  34. {smartg-2.0.2 → smartg-2.0.4}/smartg/diff.py +0 -0
  35. {smartg-2.0.2 → smartg-2.0.4}/smartg/environ.py +0 -0
  36. {smartg-2.0.2 → smartg-2.0.4}/smartg/grid3d.py +0 -0
  37. {smartg-2.0.2 → smartg-2.0.4}/smartg/histories.py +0 -0
  38. {smartg-2.0.2 → smartg-2.0.4}/smartg/interp.py +0 -0
  39. {smartg-2.0.2 → smartg-2.0.4}/smartg/iprt/__init__.py +0 -0
  40. {smartg-2.0.2 → smartg-2.0.4}/smartg/iprt/common.py +0 -0
  41. {smartg-2.0.2 → smartg-2.0.4}/smartg/iprt/phase3.py +0 -0
  42. {smartg-2.0.2 → smartg-2.0.4}/smartg/iprt/phase_a.py +0 -0
  43. {smartg-2.0.2 → smartg-2.0.4}/smartg/iprt/phase_b.py +0 -0
  44. {smartg-2.0.2 → smartg-2.0.4}/smartg/kdis.py +0 -0
  45. {smartg-2.0.2 → smartg-2.0.4}/smartg/notebooks/demo_notebook.py +0 -0
  46. {smartg-2.0.2 → smartg-2.0.4}/smartg/notebooks/demo_notebook_objects.py +0 -0
  47. {smartg-2.0.2 → smartg-2.0.4}/smartg/notebooks/demo_notebook_photons_histories.py +0 -0
  48. {smartg-2.0.2 → smartg-2.0.4}/smartg/notebooks/validation_smartg_iprt_phase3.py +0 -0
  49. {smartg-2.0.2 → smartg-2.0.4}/smartg/notebooks/validation_smartg_iprt_phase_a.py +0 -0
  50. {smartg-2.0.2 → smartg-2.0.4}/smartg/notebooks/validation_smartg_iprt_phase_b_c2.py +0 -0
  51. {smartg-2.0.2 → smartg-2.0.4}/smartg/objects3d.py +0 -0
  52. {smartg-2.0.2 → smartg-2.0.4}/smartg/phase.py +0 -0
  53. {smartg-2.0.2 → smartg-2.0.4}/smartg/postprocess.py +0 -0
  54. {smartg-2.0.2 → smartg-2.0.4}/smartg/progress.py +0 -0
  55. {smartg-2.0.2 → smartg-2.0.4}/smartg/reptran.py +0 -0
  56. {smartg-2.0.2 → smartg-2.0.4}/smartg/rrs.py +0 -0
  57. {smartg-2.0.2 → smartg-2.0.4}/smartg/sensor.py +0 -0
  58. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/device.h +0 -0
  59. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/geometry.h +0 -0
  60. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/helper_math.h +0 -0
  61. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/MicroURNG.hpp +0 -0
  62. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/ReinterpretCtr.hpp +0 -0
  63. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/aes.h +0 -0
  64. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/array.h +0 -0
  65. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/ars.h +0 -0
  66. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/conventional/Engine.hpp +0 -0
  67. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/conventional/gsl_cbrng.h +0 -0
  68. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/clangfeatures.h +0 -0
  69. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/compilerfeatures.h +0 -0
  70. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/gccfeatures.h +0 -0
  71. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/iccfeatures.h +0 -0
  72. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/msvcfeatures.h +0 -0
  73. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/nvccfeatures.h +0 -0
  74. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/open64features.h +0 -0
  75. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/openclfeatures.h +0 -0
  76. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/pgccfeatures.h +0 -0
  77. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/sse.h +0 -0
  78. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/sunprofeatures.h +0 -0
  79. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/features/xlcfeatures.h +0 -0
  80. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/gsl_microrng.h +0 -0
  81. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/philox.h +0 -0
  82. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/incRNGs/Random123/threefry.h +0 -0
  83. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/kernel2.cu +0 -0
  84. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/kernel2.h +0 -0
  85. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/math_constants.h +0 -0
  86. {smartg-2.0.2 → smartg-2.0.4}/smartg/src/phase_grid.h +0 -0
  87. {smartg-2.0.2 → smartg-2.0.4}/smartg/surface.py +0 -0
  88. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/__init__.py +0 -0
  89. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/conftest.py +0 -0
  90. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/iprt_checks.py +0 -0
  91. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/logs/.gitkeep +0 -0
  92. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_aerosol.py +0 -0
  93. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_atm3d.py +0 -0
  94. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_auxdata.py +0 -0
  95. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_bandset.py +0 -0
  96. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_cdf.py +0 -0
  97. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_chowdhary_aos.py +0 -0
  98. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_conftest.py +0 -0
  99. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_cuda.py +0 -0
  100. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_environ.py +0 -0
  101. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_histories.py +0 -0
  102. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_iprt_checks.py +0 -0
  103. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_iprt_phase_b_c2.py +0 -0
  104. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_iprt_phase_b_c3.py +0 -0
  105. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_kdis.py +0 -0
  106. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_kokhanovsky_natraj.py +0 -0
  107. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_notebook.py +0 -0
  108. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_objects3d_geometry.py +0 -0
  109. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_opac.py +0 -0
  110. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_phase_grid.py +0 -0
  111. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_phase_grid_ice.py +0 -0
  112. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_phase_mix.py +0 -0
  113. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_phase_readers.py +0 -0
  114. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_postprocess.py +0 -0
  115. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_profile.py +0 -0
  116. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_progress.py +0 -0
  117. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_quick_iprt_phase_a.py +0 -0
  118. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_reptran.py +0 -0
  119. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_rrs.py +0 -0
  120. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_self_consistency.py +0 -0
  121. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_thermal.py +0 -0
  122. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_truncation.py +0 -0
  123. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_truncation_components.py +0 -0
  124. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_vrs.py +0 -0
  125. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_water.py +0 -0
  126. {smartg-2.0.2 → smartg-2.0.4}/smartg/tests/test_xarray.py +0 -0
  127. {smartg-2.0.2 → smartg-2.0.4}/smartg/truncation.py +0 -0
  128. {smartg-2.0.2 → smartg-2.0.4}/smartg/typing.py +0 -0
  129. {smartg-2.0.2 → smartg-2.0.4}/smartg/vrs.py +0 -0
  130. {smartg-2.0.2 → smartg-2.0.4}/smartg/water.py +0 -0
  131. {smartg-2.0.2 → smartg-2.0.4}/smartg/xarray.py +0 -0
  132. {smartg-2.0.2 → smartg-2.0.4}/smartg.egg-info/dependency_links.txt +0 -0
  133. {smartg-2.0.2 → smartg-2.0.4}/smartg.egg-info/requires.txt +0 -0
  134. {smartg-2.0.2 → smartg-2.0.4}/smartg.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smartg
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: SMART-G (Speed-up Monte Carlo Advanced Radiative Transfer Code using GPU) is a radiative transfer code using a Monte-Carlo technique to simulate the propagation of the polarized light in the atmosphere and/or ocean, and using GPU acceleration.
5
5
  Author-email: Mustapha Moulana <mm@hygeos.com>, Didier Ramon <dr@hygeos.com>, François Steinmetz <fs@hygeos.com>, Dominique Jolivet <dj@hygeos.com>, Mathieu Compiègne <mc@hygeos.com>
6
6
  Project-URL: Source, https://github.com/hygeos/smartg
@@ -326,6 +326,11 @@ environment. Without them it skips itself with a message rather than
326
326
  failing, so run it with `pixi run -e extra pytest
327
327
  smartg/tests/test_smartg_jax.py`.
328
328
 
329
+ `test_jax_context.py` needs JAX on a GPU, in the same environment, and
330
+ a pytest process of its own: `test_smartg_jax.py` keeps JAX on the CPU
331
+ for the whole process, and the test then skips itself. Run it with
332
+ `pixi run -e extra pytest smartg/tests/test_jax_context.py`.
333
+
329
334
  To avoid repeating some pytest arguments, a `pytest.ini` file can be created at the root of the project. The following is an example of the contents of such a file:
330
335
 
331
336
  ```
@@ -275,6 +275,11 @@ environment. Without them it skips itself with a message rather than
275
275
  failing, so run it with `pixi run -e extra pytest
276
276
  smartg/tests/test_smartg_jax.py`.
277
277
 
278
+ `test_jax_context.py` needs JAX on a GPU, in the same environment, and
279
+ a pytest process of its own: `test_smartg_jax.py` keeps JAX on the CPU
280
+ for the whole process, and the test then skips itself. Run it with
281
+ `pixi run -e extra pytest smartg/tests/test_jax_context.py`.
282
+
278
283
  To avoid repeating some pytest arguments, a `pytest.ini` file can be created at the root of the project. The following is an example of the contents of such a file:
279
284
 
280
285
  ```
@@ -4,7 +4,7 @@ requires = ["setuptools>=61"]
4
4
 
5
5
  [project]
6
6
  name = "smartg"
7
- version = "2.0.2"
7
+ version = "2.0.4"
8
8
  description = """
9
9
  SMART-G (Speed-up Monte Carlo Advanced Radiative Transfer Code using GPU) is a radiative \
10
10
  transfer code using a Monte-Carlo technique to simulate the propagation of the polarized \
@@ -32,6 +32,8 @@ reduce_diff
32
32
  Post-process ALIS finite-difference runs into sensitivities.
33
33
  """
34
34
 
35
+ import atexit
36
+ import functools
35
37
  import os
36
38
  import subprocess
37
39
  import tempfile
@@ -402,7 +404,7 @@ class Alis:
402
404
  """
403
405
  The options of the ALIS method, for Smartg.run.
404
406
 
405
- ALIS (Emde et al. 2010) computes the spectrum of a whole band from
407
+ ALIS (Emde et al. 2011) computes the spectrum of a whole band from
406
408
  a single set of photon paths, by weighting each path with the
407
409
  absorption of every wavelength. The Smartg object must have been
408
410
  compiled with alis=True, together with alt_pp=True or pp=False.
@@ -562,7 +564,10 @@ def _check_alis_kernel(
562
564
 
563
565
 
564
566
  def _check_objects_kernel(
565
- my_objects: list | None, alis: bool, pp: bool, opt3d: bool
567
+ my_objects: Sequence[Entity | GroupE] | None,
568
+ alis: bool,
569
+ pp: bool,
570
+ opt3d: bool,
566
571
  ) -> None:
567
572
  """
568
573
  Refuse the 3D objects in a kernel whose move does not follow them.
@@ -850,6 +855,122 @@ class LocalEstimate:
850
855
  )
851
856
 
852
857
 
858
+ # The CUDA contexts that clear_context destroyed, kept referenced so
859
+ # that no new context takes the place of one of them. The Smartg objects
860
+ # of a process share the context of pycuda.autoinit: once one of them
861
+ # clears it, the others must not make it current again, which crashes
862
+ # the driver.
863
+ _RELEASED_CONTEXTS: list[Any] = []
864
+
865
+
866
+ def _released(ctx: Any) -> bool:
867
+ """Return whether ctx is a context that clear_context destroyed."""
868
+ return any(c is ctx for c in _RELEASED_CONTEXTS)
869
+
870
+
871
+ def _make_current(ctx: Any) -> bool:
872
+ """Make the CUDA context ctx the current one, if it is not.
873
+
874
+ Another library of the process (JAX for instance) may have made its
875
+ own context current, where the kernels of SMART-G are unknown
876
+ ('invalid resource handle'). Returns True if ctx was pushed, to be
877
+ popped by the caller. Raises a RuntimeError if ctx was destroyed by
878
+ the clear_context of another Smartg object that shared it.
879
+ """
880
+ if ctx is None:
881
+ return False
882
+ if _released(ctx):
883
+ raise RuntimeError(
884
+ 'The CUDA context of this Smartg object was destroyed by the '
885
+ 'clear_context of another Smartg object that shared it: '
886
+ 'create a new Smartg object.')
887
+ # PyCUDA only knows the contexts it pushed itself: ask the driver
888
+ if _driver_current_context() == ctx.handle:
889
+ return False
890
+ ctx.push()
891
+ return True
892
+
893
+
894
+ def _autoinit_current_at_exit(sg: 'Smartg') -> None:
895
+ """At exit, make the pycuda.autoinit context of sg current again.
896
+
897
+ The exit handler of pycuda.autoinit pops its context, which fails
898
+ ('cannot pop non-current context') if another library (JAX) made
899
+ its own context current in the meantime.
900
+ """
901
+ import sys
902
+ mod = sys.modules.get('pycuda.autoinit')
903
+ if (sg.ctx is None or _released(sg.ctx) or mod is None
904
+ or getattr(mod, 'context', None) is not sg.ctx):
905
+ return
906
+ # set it current at the driver level: pushing it would leave it
907
+ # twice on the stack of PyCUDA, which aborts at the module cleanup
908
+ if _driver_current_context() == sg.ctx.handle:
909
+ return
910
+ try:
911
+ import ctypes
912
+ for name in ('libcuda.so.1', 'libcuda.so', 'nvcuda.dll'):
913
+ try:
914
+ set_current = ctypes.CDLL(name).cuCtxSetCurrent
915
+ break
916
+ except (OSError, AttributeError):
917
+ continue
918
+ else:
919
+ return
920
+ set_current(ctypes.c_void_p(sg.ctx.handle))
921
+ # an exit handler must not raise: the one of pycuda.autoinit, which
922
+ # runs next, reports a context that is still not current
923
+ except Exception: # noqa: BLE001, S110
924
+ pass
925
+
926
+
927
+ def _driver_current_context() -> int | None:
928
+ """Handle of the current CUDA context of the driver, or None."""
929
+ global _CU_CTX_GET_CURRENT
930
+ if _CU_CTX_GET_CURRENT is None:
931
+ import ctypes
932
+ import ctypes.util
933
+ names = ('libcuda.so.1', 'libcuda.so', 'nvcuda.dll',
934
+ ctypes.util.find_library('cuda'))
935
+ for name in names:
936
+ if not name:
937
+ continue
938
+ try:
939
+ _CU_CTX_GET_CURRENT = ctypes.CDLL(name).cuCtxGetCurrent
940
+ break
941
+ except (OSError, AttributeError):
942
+ continue
943
+ else:
944
+ _CU_CTX_GET_CURRENT = False
945
+ if not _CU_CTX_GET_CURRENT:
946
+ return None
947
+ import ctypes
948
+ handle = ctypes.c_void_p()
949
+ if _CU_CTX_GET_CURRENT(ctypes.byref(handle)) != 0:
950
+ return None
951
+ return handle.value
952
+
953
+
954
+ _CU_CTX_GET_CURRENT = None
955
+
956
+
957
+ def _in_context(method: Callable[..., Any]) -> Callable[..., Any]:
958
+ """Run a method of Smartg with its CUDA context current."""
959
+
960
+ @functools.wraps(method)
961
+ def wrapper(self: 'Smartg', *args: Any, **kwargs: Any) -> Any:
962
+ pushed = _make_current(self.ctx)
963
+ try:
964
+ return method(self, *args, **kwargs)
965
+ finally:
966
+ # run may have popped and released the context itself
967
+ # (autoinit=False, keep_context=False)
968
+ if pushed and self.ctx is not None:
969
+ cuda.Context.pop() # pyright: ignore[reportAttributeAccessIssue]
970
+
971
+ return wrapper
972
+
973
+
853
974
  class Smartg:
854
975
  """Initialize the Smartg object.
855
976
 
@@ -872,7 +993,7 @@ class Smartg:
872
993
  double : bool, optional
873
994
  Accumulate photons table in double precision (default double).
874
995
  alis : bool, optional
875
- Use the ALIS method (Emde et al. 2010) for treating gaseous
996
+ Use the ALIS method (Emde et al. 2011) for treating gaseous
876
997
  absorption and perturbed profile. Needs alt_pp=True or
877
998
  pp=False. The 3D objects are not available with it.
878
999
  back : bool, optional
@@ -906,7 +1027,8 @@ class Smartg:
906
1027
  Include the Sun Induced Fluorescence, an experimental option.
907
1028
  Needs alis=True, together with alt_pp=True or pp=False.
908
1029
  thermal : bool, optional
909
- Still in dev...
1030
+ Still in dev... The forward mode (back=False) needs alt_pp=True
1031
+ in the plane parallel geometry.
910
1032
  rng : str, optional
911
1033
  The pseudo-random number generator to use, only 2 choice:
912
1034
  - PHILOX
@@ -1027,6 +1149,13 @@ class Smartg:
1027
1149
  "geometry (pp=True): the spherical move does not follow "
1028
1150
  "its cells"
1029
1151
  )
1152
+ if thermal and not back and pp and not alt_pp:
1153
+ raise ValueError(
1154
+ "The forward thermal emission (thermal=True, back=False) "
1155
+ "needs alt_pp=True in the plane parallel geometry: the "
1156
+ "fast move does not start the emitted photons at their "
1157
+ "altitude"
1158
+ )
1030
1159
 
1031
1160
  if device is not None:
1032
1161
  env_modif = {'CUDA_DEVICE': str(device)}
@@ -1091,6 +1220,10 @@ class Smartg:
1091
1220
  self.ctx = make_default_context()
1092
1221
 
1093
1222
  self.autoinit = autoinit
1223
+ if autoinit:
1224
+ # registered after the exit handler of pycuda.autoinit, so
1225
+ # run before it: its context must be current to be popped
1226
+ atexit.register(_autoinit_current_at_exit, self)
1094
1227
  self.pp = pp
1095
1228
  self.double = double
1096
1229
  self.alis = alis
@@ -1197,6 +1330,9 @@ class Smartg:
1197
1330
  # 3D Object mode
1198
1331
  options.append('-DOBJ3D')
1199
1332
  options.append('-D' + rng)
1333
+ # CUDA 13 deprecates double4 (warning 1444) for double4_16a and
1334
+ # double4_32a, which older toolkits lack
1335
+ options.append('-diag-suppress=1444')
1200
1336
  # options.append('-lineinfo')
1201
1337
 
1202
1338
  #
@@ -1208,23 +1344,29 @@ class Smartg:
1208
1344
  with open(SRC_DEVICE, encoding='ascii', errors='ignore') as f:
1209
1345
  src_device_content = f.read()
1210
1346
 
1211
- # kernel compilation
1212
- self.mod = SourceModule(
1213
- src_device_content,
1214
- nvcc='nvcc',
1215
- options=options,
1216
- no_extern_c=True,
1217
- cache_dir=str(cache_dir),
1218
- include_dirs=[
1219
- str(DIR_SRC),
1220
- str(DIR_SRC / 'incRNGs' / 'Random123'),
1221
- ],
1222
- )
1347
+ # kernel compilation, in the context of this object (another
1348
+ # library may have made its own context current)
1349
+ pushed = _make_current(self.ctx)
1350
+ try:
1351
+ self.mod = SourceModule(
1352
+ src_device_content,
1353
+ nvcc='nvcc',
1354
+ options=options,
1355
+ no_extern_c=True,
1356
+ cache_dir=str(cache_dir),
1357
+ include_dirs=[
1358
+ str(DIR_SRC),
1359
+ str(DIR_SRC / 'incRNGs' / 'Random123'),
1360
+ ],
1361
+ )
1223
1362
 
1224
- # load the kernel
1225
- self.kernel = self.mod.get_function('launchKernel')
1226
- # self.kernel2 = self.mod.get_function('launchKernel2')
1227
- self.kernel2 = self.mod.get_function('reduce_absorption_gpu')
1363
+ # load the kernel
1364
+ self.kernel = self.mod.get_function('launchKernel')
1365
+ # self.kernel2 = self.mod.get_function('launchKernel2')
1366
+ self.kernel2 = self.mod.get_function('reduce_absorption_gpu')
1367
+ finally:
1368
+ if pushed:
1369
+ cuda.Context.pop() # pyright: ignore[reportAttributeAccessIssue]
1228
1370
 
1229
1371
  #
1230
1372
  # common attributes
@@ -1280,25 +1422,34 @@ class Smartg:
1280
1422
  Notes
1281
1423
  -----
1282
1424
  Once this method has been called, the run method can no longer
1283
- be used: the Smartg object must be reinitialized.
1425
+ be used: the Smartg object must be reinitialized. The Smartg
1426
+ objects with autoinit share one context: once one of them has
1427
+ cleared it, the others can no longer run, and their own
1428
+ clear_context only prints that there is no context to clear.
1284
1429
  """
1285
1430
  try:
1286
1431
  assert self.ctx is not None
1432
+ # another library (JAX) may have made its context current
1433
+ _make_current(self.ctx)
1287
1434
  self.ctx.pop()
1288
1435
  self.ctx.detach()
1436
+ _RELEASED_CONTEXTS.append(self.ctx)
1289
1437
  self.ctx = None
1290
1438
  from pycuda.tools import clear_context_caches
1291
1439
 
1292
1440
  clear_context_caches()
1293
1441
  if self.autoinit:
1294
- # In case of autoinit delete pycuda.autoinit
1442
+ # With autoinit, delete pycuda.autoinit and its exit
1443
+ # handler, which would pop the destroyed context
1295
1444
  import pycuda.autoinit
1296
1445
 
1446
+ atexit.unregister(pycuda.autoinit._finish_up)
1297
1447
  del pycuda.autoinit
1298
1448
  # pycuda's own error classes again, nothing else to do here
1299
1449
  except Exception: # noqa: BLE001
1300
1450
  print("There is no current context to clear.")
1301
1451
 
1452
+ @_in_context
1302
1453
  def run(
1303
1454
  self,
1304
1455
  wavelength: NumericArrayLike | BandSet | Sequence[BandLike],
@@ -286,10 +286,6 @@ public:
286
286
  #endif
287
287
  /* ---------------------*/
288
288
 
289
- #ifdef SPHERIQUE
290
- float radius;
291
- #endif
292
-
293
289
  #ifdef BACK
294
290
  // Cumulative Mueller Matrix
295
291
  float4x4 M;