Speed-up Monte Carlo Advanced Radiative Transfer Code using GPU
SMART-G 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.
Didier Ramon
Mustapha Moulana
François Steinmetz
Dominique Jolivet
Mathieu Compiègne
HYGEOS
- Polarized (I, Q, U, V) Monte-Carlo radiative transfer, accelerated on NVIDIA GPUs
- Coupled ocean-atmosphere system, or atmosphere only / ocean only
- 1D atmospheric profiles (AFGL standard atmospheres or user-provided), aerosols (OPAC or user-defined) and clouds
- 1D ocean: chlorophyll-driven bio-optical models (Park & Ruddick, or the vertical profile of Zhai et al.), or user-defined inherent optical properties and phase matrices, over a reflecting seafloor
- Truncation of the forward peak of the phase matrices (GT or Delta-M, with pytrunc), per component; the ocean hydrosols are truncated by default, as in SMART-G 1.x
- 3D atmospheres (
opt3d=True), validated against MYSTIC on the IPRT phase B benchmark - Spherical atmosphere (
pp=False), validated on the IPRT phase 3 benchmark - 3D objects and concentrated solar flux geometries (heliostat fields, solar towers)
- Flat, rough (Cox-Munk), Lambertian or BRDF (RTLS) surfaces, and horizontally inhomogeneous environments (albedo maps)
- Spectral integration with the k-distribution and REPTRAN parameterizations, and ALIS, which computes many wavelengths, and Jacobians, from the same photons
- Rotational (Ring effect) and vibrational Raman scattering
- Forward and backward modes, local estimate, sensors, and photon-history tracking
- Results returned as an
xarray.Dataset, with built-in visualization helpers - Validated against published benchmarks and other codes: the IPRT phases A, B and 3, the atmosphere-ocean testbed of Chowdhary et al. (2020), HydroLight, and the Rayleigh, aerosol and cloud benchmarks of Kokhanovsky et al. (2010) and Natraj & Hovenier (2012), see section 7
SMART-G supports Python 3.11 to 3.14 and runs on NVIDIA GPUs: pycuda
needs the NVIDIA driver and the CUDA toolkit, see
section 3.
Upgrading from 1.x: version 2.0 renames many classes, functions and
parameters to follow PEP 8 (e.g. AtmAFGL → Atm1D, NBPHOTONS →
n_photons). The breaking changes are listed in the
CHANGELOG,
whose Corrections section lists the fixes that change results as well:
the particle scattering of the chlorophyll ocean, for instance, which 1.x
halved by mistake, or the reflection of a rough sea at grazing sun, which
created energy.
To install SMART-G from PyPI:
pip install smartgTo include extra dependencies use instead:
pip install "smartg[extra]"Use the command:
conda install -c conda-forge smartgIf you need extra dependencies (jax with cuda) we recommend the installation with pip instead.
Click here
First clone the repository:
git clone https://github.com/hygeos/smartg.gitYou can now choose between Pixi or Conda for your development environment.
Pixi is recommended for its fast dependency resolution and robust environment management. Unlike Conda, which only considers Conda packages during conflict resolution, Pixi considers both Conda and pip package versions when solving dependencies.
To create and activate the environment, use the following command:
pixi shellTo consider all extra dependencies (e.g. jax), use instead:
pixi shell --environment extraWith Anaconda/Miniconda, use the following command:
conda env create -n smartg-env -f environment.yml
conda activate smartg-env
pip install --no-deps -e .For a full installation (extra dependencies), replace environment.yml by environment-extra.yml.
An installation guide is available in the nvidia website: installation-guide.
You can also install CUDA using conda:
conda install nvidia::cudaThe auxiliary data can be downloaded as follow:
>>> # Example to download all the data. See the docstrings for more details.
>>> from smartg.auxdata import download
>>> from pathlib import Path
>>> download(Path('dir/path/where/to/save/data/'), data_type='all')
>>> # Only some datasets, in the SMARTG_DIR_AUXDATA directory
>>> download(data_type=['aer', 'cld'])The datasets already on disk are skipped. Each download is recorded in
a .smartg_auxdata.json manifest inside the auxdata directory, which
allows checking and refreshing the data when it changes on the server:
>>> from smartg.auxdata import check_update, update
>>> check_update() # queries the remote versions, downloads nothing
Auxiliary data in /dir/path/where/to/save/data
dataset status downloaded (UTC) remote (UTC) source / note
aer up to date 2026-09-15 14:03 2026-04-27 14:37 hygeos
acs outdated 2026-09-15 14:03 2026-09-16 09:12 hygeos
kdis missing - 2024-05-02 14:13 hygeos
...
update() would download: acs, kdis
>>> update() # downloads only the missing, outdated or unrecorded datasets
>>> update(data_type='reptran', force=True) # download again whatever the statusThe manifest also records the checksum of every downloaded file, so
check_update() lists the files modified or deleted locally (status
modified). The remote data is the reference: restore() shows these
files and, after confirmation, replaces them by the remote copies
without downloading the whole dataset again. Extra files are kept.
>>> from smartg.auxdata import restore
>>> restore() # lists the modified files and asks before replacing them
>>> restore(data_type='aer', yes=True) # no question askedThe environment variable SMARTG_DIR_AUXDATA must be defined.
For example, in the .bashrc / .zshrc file the following can be added:
export SMARTG_DIR_AUXDATA="dir/path/where/to/save/data/"or (not recommended) in a .env file in the SMART-G root directory,
the parent of the smartg package, which only exists in a git clone:
SMARTG_DIR_AUXDATA=dir/path/where/to/save/data/
Once the auxiliary data are in place, a first simulation takes a few lines:
from smartg.smartg import Smartg
from smartg.atmosphere import Atm1D
from smartg.view import smartg_view
# 1e8 photons at 500 nm, tropical atmosphere, sun at a 30° zenith angle
ds = Smartg().run(wavelength=500., th_deg=30., n_photons=1e8,
atmosphere=Atm1D('afglt'))
# ds is an xarray.Dataset holding 'I_up (TOA)', 'Q_up (TOA)',
# 'U_up (TOA)', 'V_up (TOA)', ...
smartg_view(ds) # polar view of the reflectance and of the polarizationThe first call compiles the CUDA kernel; subsequent runs reuse it.
Sample notebooks are provided in the notebooks folder, and jupyter notebook has nice possibilities for interactive development and visualization, in particular if you are using a remote cuda computer. Good entry points are:
demo_notebook.py— general usage: atmosphere, ocean, surface, outputs and visualizationdemo_notebook_objects.py— simulations involving 3D objects, e.g., solar power towersdemo_notebook_photons_histories.py— tracking the photon paths (needs the extra dependencies, e.g.pixi shell --environment extra)validation_smartg_iprt_phase_a.py— the 1D cases of the IPRT phase A, compared with MYSTICvalidation_smartg_iprt_phase_b_c2.py— the cubic cloud (C2) of the IPRT phase B, in 3D mode, compared with MYSTICvalidation_smartg_iprt_phase3.py— the spherical cases D1 to E6 of the IPRT phase 3
The notebooks are stored as jupytext percent scripts: plain Python files in which # %% starts a code cell and # %% [markdown] a markdown cell. They hold neither outputs nor editor metadata, so their diffs and merges are readable. Jupyter magics are commented (# %%time) so that the scripts stay valid Python, and are restored when a script is opened as a notebook.
jupytext is installed with the other dependencies. Its configuration, in the [tool.jupytext] section of pyproject.toml, pairs each script with a .ipynb notebook of the same name, which keeps the outputs locally and is ignored by git.
-
JupyterLab / Jupyter Notebook (
pixi run jupyter lab): right-click a script, then Open With → Jupytext Notebook. Saving updates both the script and its paired notebook. Runpixi run jupytext-config set-default-viewer pythononce to open the scripts as notebooks with a double click. -
VS Code: the
# %%cells of a script run as is in the Interactive Window. To use the notebook editor, open the paired.ipynb(created by the task below) and either install the Jupytext Sync extension, which updates the script on save, or run the task before committing. -
Synchronization: after a
git pull, or after editing one file of a pair outside Jupyter, runpixi run sync-notebooks
The cells are taken from the most recently modified file of each pair, and the outputs from the notebook. Synchronize before pulling as well, so that edits made in a notebook are not overwritten by a newer script.
-
New notebook: save it as
smartg/notebooks/<name>.ipynbfrom Jupyter, which creates its script, or runpixi run jupytext --sync smartg/notebooks/<name>.ipynb. Only the.pyscript is committed.
To check that SMART-G is running correctly, run the following command at the root of the project:
pytest smartg/tests/test_cuda.py smartg/tests/test_profile.py \
smartg/tests/test_atm3d.py smartg/tests/test_smartg.py -s -vA full testing is recommended in dev:
pytest smartg/tests/ -s -vWith Pixi, both are available as tasks:
pixi run test-basic # the four files above
pixi run test-all # the whole test suitetest_smartg_jax.py needs the jax dependencies of the extra
environment. Without them it skips itself with a message rather than
failing, so run it with pixi run -e extra pytest smartg/tests/test_smartg_jax.py.
test_jax_context.py needs JAX on a GPU, in the same environment, and
a pytest process of its own: test_smartg_jax.py keeps JAX on the CPU
for the whole process, and the test then skips itself. Run it with
pixi run -e extra pytest smartg/tests/test_jax_context.py.
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:
[pytest]
addopts= --html=test_report.html --self-contained-html -s -v -rs
The arguments --html=test_report.html --self-contained-html generate an html report containing the results of the tests (sometimes with more details e.g. plots), named test_report.html, and -rs lists the reasons why tests have been skipped.
Four files compare SMART-G with the IPRT (International Polarized Radiative Transfer) model intercomparison: one for phase A, two for phase B and one for phase 3. The durations below were measured on a Ryzen 9 5950X with a GeForce RTX 5070 Ti.
Phase A — test_quick_iprt_phase_a.py runs the 1D cases A1 (a Rayleigh layer), A2 (a Rayleigh layer over a Lambertian surface), A3 and A4 (aerosol layers), A5 (a water cloud, in the principal plane and in the almucantar) and A6 (a Rayleigh layer over a glittering sea), with the realistic profiles B1, B2 and B3 of phase B, and compares them with MYSTIC. Ten tests, about 3 min 30, no slow tier.
Phase B — test_iprt_phase_b_c2.py (cubic cloud) and test_iprt_phase_b_c3.py (cumulus cloud with aerosols) check the 3D atmosphere mode (opt3d=True) against the MYSTIC reference. Reproducing the benchmark photon counts takes hours, so each of their tests exists in two tiers: a fast one, run by default, and a slow one selected with -m slow.
pytest smartg/tests/test_iprt_phase_b_c3.py # fast, ~3 min
pytest -m slow smartg/tests/test_iprt_phase_b_c3.py # slow, ~38 minBoth files together take 6 min in the fast tier and 1 h 23 in the slow one. C3 builds each of its atmospheres, one mixed phase matrix per cloudy cell, in a single threaded loop of about 15 s; its truncated cloud costs a few seconds more, being truncated once per distinct effective radius rather than once per cell.
The fast tier detects a 5 % error on the cloud optical properties, the slow one 1 %: run it before a release, or after a change to the 3D kernel, to the phase matrices or to the truncation.
Phase 3 — test_iprt_phase3.py checks the spherical geometry (pp=False) on the one-layer cases D1 to D6 and the vertically inhomogeneous cases E1 to E5, against saved SMART-G results computed with 1e8 photons per viewing direction. It has the same two tiers: the fast one uses 1e6 photons per direction and takes about 5 min for the file, the slow one reproduces the 1e8 of the references and takes about 6 h 40 for the 11 cases. E6, the camera at 300 000 km, is not covered yet.
pytest smartg/tests/test_iprt_phase3.py # fast, ~5 min
pytest -m slow smartg/tests/test_iprt_phase3.py # slow, ~6 h 40Because a GPU run is not reproducible bit for bit, the phase 3 comparison is statistical: the tolerances on the mean bias and on the fraction of directions beyond three combined standard deviations were measured per tier rather than taken from a normal distribution.
Four more files compare SMART-G with published benchmarks or with itself, with the same fast and slow tiers (-m slow) where the photon counts make it worthwhile. The durations are those of an RTX 5070 Ti.
Atmosphere-ocean testbed — test_chowdhary_aos.py compares the upwelling reflectances I, Q and U just above the sea and at the top of the atmosphere with the testbed of Chowdhary et al. (2020, JQSRT 242, 106717): a Rayleigh atmosphere over a rough sea (AOS-I, and AOS-I* in the geometry of Natraj et al. 2009), a rough sea over 100 m of pure sea water (AOS-II), both (AOS-III), and both with forward-peaked hydrosols (AOS-IV), at four wavelengths and two solar angles. About 3 min in the fast tier and 1 h 30 in the slow one.
Rayleigh, aerosol and cloud benchmarks — test_kokhanovsky_natraj.py compares the Stokes parameters of Rayleigh slabs of optical thickness 1 to 1024 (Natraj & Hovenier 2012) and of the Rayleigh, aerosol and water cloud layers of Kokhanovsky et al. (2010) with the benchmark values, the validations of the SMART-G paper. About 30 s of runs in the fast tier and 4 min in the slow one.
Self-consistency — test_self_consistency.py computes the same quantity in two ways, which must agree within the Monte Carlo noise: the equivalence theorem (the absorption as a weight along the path or at the collisions), ALIS against standard runs, with water, and the Jacobians of ALIS against finite differences. About 3 min 30 in the fast tier and 30 min in the slow one.
Ocean — test_water.py holds, among its unit tests, a comparison with HydroLight (the irradiance reflectance and the upwelling radiance just below the surface, pure sea water) and with the analytic radiance of a Lambertian seafloor under clear water, in the forward and backward modes.
Identifiers follow PEP 8: snake_case for modules, functions, variables and
parameters, PascalCase for classes, UPPER_SNAKE_CASE for constants. Lines
are at most 79 columns of code and 72 of docstrings and comments, and the
docstrings follow the numpy convention.
ruff and pyright check all of this, configured in the [tool.ruff] and
[tool.pyright] sections of pyproject.toml. Neither is a project
dependency, so install them separately; the configuration was written for
ruff 0.16 and pyright 1.1.411. Lint the tracked files with:
ruff check $(git ls-files '*.py')
pyrightPassing the tracked files explicitly keeps untracked scratch modules out of the report.
GeForce GTX 1070, GeForce TITAN V, GeForce RTX 2080 Ti, GeForce RTX 3070, GeForce RTX 3090, GeForce RTX 4090, GeForce RTX 5070 Ti, A100, RTX PRO 6000 Blackwell (Workstation Edition)
The use of GPUs before 10xx series (Pascal) is deprecated as of SMART-G 1.0.0
This software is available under the SMART-G license v1.0, available in the LICENSE.TXT file. It can be used for free for non-commercial purposes; for commercial use, please contact HYGEOS.
When acknowledging the use of SMART-G for scientific papers, reports etc please cite the following reference(s):
-
Ramon, D., Steinmetz, F., Jolivet, D., Compiègne, M., & Frouin, R. (2019). Modeling polarized radiative transfer in the ocean-atmosphere system with the GPU-accelerated SMART-G Monte Carlo code. Journal of Quantitative Spectroscopy and Radiative Transfer, 222, 89-107. https://doi.org/10.1016/j.jqsrt.2018.10.017
-
Moulana, M., Cornet, C., Elias, T., Ramon, D., Caliot, C., & Compiègne, M. (2024). Concentrated solar flux modeling in solar power towers with a 3D objects-atmosphere hybrid system to consider atmospheric and environmental gains. Solar Energy, 277, 112675. https://doi.org/10.1016/j.solener.2024.112675
- Changes between versions: CHANGELOG.md
- Bug reports and feature requests: github issues
- Anything else: contact HYGEOS