Aerosol Models

pyRadtran supports multiple aerosol model types through a unified interface.

OPAC Presets

Use predefined OPAC aerosol types:

from pyradtran import run_with_opac_preset, OpacPreset, OpacPresetName

# Using convenience function
result = run_with_opac_preset(
    preset="maritime_clean",
    sza=45.0,
    wl_min=400,
    wl_max=800,
)

# Using Scene builder
from pyradtran import Scene

scene = Scene().set_aerosol(
    OpacPreset(preset=OpacPresetName.MARITIME_CLEAN)
)

Available presets include MARITIME_CLEAN, MARITIME_POLLUTED, MARITIME_TROPICAL, URBAN, DESERT, etc.

Custom OPAC

Mix individual OPAC species with custom concentrations:

from pyradtran import OpacCustom

aerosol = OpacCustom(
    species={
        "INSO": 1000.0,   # Insoluble
        "WASO": 5000.0,   # Water soluble
        "SSAM": 200.0,    # Sea salt (accumulation)
    }
)
scene = Scene().set_aerosol(aerosol)

Pre-computed layer files (DirectLayerOpticsBlock)

For a pre-computed explicit aerosol file (a .master / .LAYER set), wrap it in a DirectLayerOpticsBlock and mix it into a CompositeAerosol:

from pyradtran.models.aerosol_composite import CompositeAerosol
from pyradtran.models.blocks import DirectLayerOpticsBlock

aerosol = CompositeAerosol(
    pieces=[DirectLayerOpticsBlock(master_path="my_aerosol.master", name="ext")],
    wavelength_grid_um=[0.50, 0.55, 0.60],   # must match the file's grid
    altitude_grid_km=[8.0, 6.0, 4.0, 2.0, 0.0],
    n_legendre=32,
    output_dir=".",
)
scene = Scene().set_aerosol(aerosol)

Note

DirectLayerOpticsBlock performs no wavelength resampling — the grids passed to CompositeAerosol must match the file’s grid exactly.

Composite Aerosol with Mie Scattering

For advanced users, pyRadtran supports composite aerosols built from LEGO “blocks”. Each block is a mass-normalized species (here a Mie species); a vertical profile places it in the column; PlacedBlock binds the two. Any number of blocks are externally mixed into one CompositeAerosol.

Note

The composite needs the wavelength grid (µm), the altitude grid (km, strictly descending), and a writable output_dir for the explicit .master / .LAYER file set it writes.

from pyradtran import Scene, Runner
from pyradtran.models.aerosol_composite import (
    CompositeAerosol, IntegrationConfig, MieSpecies, RefractiveIndex, SizeDistribution,
)
from pyradtran.models.blocks import PlacedBlock, od_to_mass_profile

altitude_km = [8.0, 6.0, 4.0, 2.0, 0.0]


def _species(n_real, k_imag, r_g_um, sigma_g, density, name):
    ri = RefractiveIndex(wavelength_um=[0.40, 0.55, 0.70], n_real=[n_real] * 3, k_imag=[k_imag] * 3)
    sd = SizeDistribution(kind="lognormal", params={"r_g_um": r_g_um, "sigma_g": sigma_g})
    return MieSpecies(
        refractive_index=ri, size_distribution=sd,
        particle_density_kg_m3=density, integration_config=IntegrationConfig(), name=name,
    )


dust = _species(1.53, 0.008, 0.50, 2.2, 2600.0, "dust")
soot = _species(1.75, 0.44, 0.05, 1.8, 1800.0, "soot")
pieces = [
    PlacedBlock(block=dust, profile=od_to_mass_profile(
        dust, tau_ref=0.20, ref_nm=550.0, altitude_km=altitude_km, scale_height_km=3.0)),
    PlacedBlock(block=soot, profile=od_to_mass_profile(
        soot, tau_ref=0.05, ref_nm=550.0, altitude_km=altitude_km, scale_height_km=1.5)),
]
aerosol = CompositeAerosol(
    pieces=pieces,
    wavelength_grid_um=[0.50, 0.55, 0.60],
    altitude_grid_km=altitude_km,
    n_legendre=32,
    output_dir=".",
)

scene = (
    Scene().set_atmosphere(profile="us")
    .set_source_solar(sza=30.0)
    .set_wavelength(500.0, 600.0)
    .set_solver(method="disort", streams=16, disort_intcor="moments")
    .set_output(quantities=["lambda", "edir", "edn", "eup"], format="ascii", zout=[0, 2, "toa"])
    .set_aerosol(aerosol)
)
result = Runner.execute(scene, data_path=None)
print(result.edir.shape)

Aerosol Modification

After setting an aerosol, scale or set a column property. variable is one of tau, ssa, gg, tau550; action is scale or set. Multiple calls accumulate (each adds one directive):

scene = scene.set_aerosol_modify(variable="tau", action="scale", value=0.5)

Bulk Aerosol from Aerosol3D

To use pre-computed bulk optics (e.g. from Aerosol3D’s BulkAerosolOpticsData), wrap them in BulkSpecies (duck-typed; pyRadtran does not import Aerosol3D itself). For a real Mie phase function instead of Henyey-Greenstein, build a Mie species with phase_function="mie".

import numpy as np
from pyradtran.models.aerosol_composite import BulkSpecies, CompositeAerosol
from pyradtran.models.blocks import PlacedBlock, MassProfile

# A tiny bulk object built inline so this snippet is self-contained. In practice
# pass an Aerosol3D BulkAerosolOpticsData here.
wl = np.array([0.50, 0.55, 0.60])


class _TinyBulk:
    size_distribution = None
    effective_density_kg_m3 = 2600.0

    def to_dataset(self, n_legendre=32):
        import xarray as xr
        nlayer = 4
        mom = np.zeros((wl.size, nlayer, n_legendre))
        mom[:, :, 0] = 1.0
        return xr.Dataset(
            {
                "beta_ext_per_mass": (("wavelength", "layer"), np.full((wl.size, nlayer), 0.5)),
                "ssa": (("wavelength", "layer"), np.full((wl.size, nlayer), 0.9)),
                "g": (("wavelength", "layer"), np.full((wl.size, nlayer), 0.3)),
                "legendre_moments": (("wavelength", "layer", "n_legendre"), mom),
            },
            coords={"wavelength": wl},
        )


block = BulkSpecies(bulk=_TinyBulk(), name="dust")
piece = PlacedBlock(block=block, profile=MassProfile(kg_m3_per_layer=(1e-7,) * 4))
aerosol = CompositeAerosol(
    pieces=[piece], wavelength_grid_um=list(wl),
    altitude_grid_km=[8.0, 6.0, 4.0, 2.0, 0.0], n_legendre=32, output_dir=".",
)
print("pieces:", [p.name for p in aerosol.pieces])

# Real Mie phase function (instead of Henyey-Greenstein):
# MieSpecies(..., phase_function="mie")