[1]:
%reload_ext eradiate.notebook.tutorials
Last updated: 2023-01-26 09:25 (eradiate v0.22.6.dev24+ge8e434d7.d20221118)
Particle layer basics#
Overview
In this tutorial, we introduce Eradiate’s particle layer simulation features. We also introduce the general heterogeneous atmosphere container.
Prerequisites
Ability to run and visualise 1D simulations (see First steps with Eradiate).
(Optional but recommended) Ability to configure a molecular atmosphere (see Molecular atmosphere basics).aeronet_sahara_spherical_RAMIA_GENERIC_extrapolated.json
What you will learn
How to add one or several aerosol layers to a molecular atmosphere.
The particle layer interface#
We start by activating the IPython extension and importing and aliasing a few useful components. We also select the CKD mode.
[2]:
%load_ext eradiate
import eradiate
import numpy as np
import matplotlib.pyplot as plt
from eradiate.notebook.tutorials import plot_sigma_t
eradiate.set_mode("ckd")
We alias the eradiate.scenes
module and the unit registry for convenience:
[3]:
import eradiate.scenes as ertsc
from eradiate import unit_registry as ureg
The ParticleLayer
class is used to configure particle layers. It is parametrised by:
an extent, defined by its bottom and top parameters;
a number of layers (n_layers) the particle layer is discretised;
a particle spatial density distribution (e.g. uniform or gaussian)
an optical thickness value (tau_ref) at a reference wavelength set by its w_ref parameter (by default 550 nm).
We start by creating a particle layer with default parameters:
[4]:
particle_layer_default = ertsc.atmosphere.ParticleLayer()
particle_layer_default
ParticleLayer( id='atmosphere', geometry=PlaneParallelGeometry(width=1000000.0 km), scale=None, _bottom=0.0 km, _top=1.0 km, distribution=UniformParticleDistribution(bounds=array([0., 1.])), w_ref=550 nm, tau_ref=0.2, n_layers=16, dataset=<xarray.Dataset> Dimensions: (w: 20, mu: 181, i: 1, j: 1) Coordinates: * w (w) float64 350.0 400.0 412.0 443.0 ... 1.95e+03 2.25e+03 3.75e+03 * i (i) int64 0 * j (j) int64 0 * mu (mu) float64 1.0 1.0 1.0 1.0 1.0 ... -0.9983 -0.9993 -0.9998 -1.0 Data variables: sigma_t (w) float64 0.05645 0.04158 0.03882 ... 0.00578 0.005448 0.005256 albedo (w) float64 0.9808 0.9798 0.9795 0.9784 ... 0.9129 0.9011 0.8525 phase (w, mu, i, j) float64 178.4 173.6 161.7 ... 0.2595 0.2662 0.2686 Attributes: convention: CF-1.8 title: Particles radiative properties history: 2021-11-03 15:29:43 - data set creation - kata, version 0.0.2, _phase=TabulatedPhaseFunction( id='phase_atmosphere', data=<xarray.DataArray 'phase' (w: 20, mu: 181, i: 1, j: 1)> array([[[[2.1040000e-01]], [[2.1451000e-01]], [[2.2864000e-01]], ..., [[1.6167650e+02]], [[1.7361663e+02]], [[1.7842933e+02]]], [[[2.1991000e-01]], [[2.1822000e-01]], [[2.2853000e-01]], ... [[6.7030030e+01]], [[6.7152350e+01]], [[6.7193330e+01]]], [[[2.6860000e-01]], [[2.6621000e-01]], [[2.5953000e-01]], ..., [[3.0493370e+01]], [[3.0512870e+01]], [[3.0519370e+01]]]]) Coordinates: * w (w) float64 350.0 400.0 412.0 443.0 ... 1.95e+03 2.25e+03 3.75e+03 * i (i) int64 0 * j (j) int64 0 * mu (mu) float64 -1.0 -0.9998 -0.9993 -0.9983 ... 1.0 1.0 1.0 1.0 Attributes: standard_name: scattering_phase_matrix long_name: scattering phase matrix units: str^-1 ), _zgrid=ZGrid( levels=[0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.5625 0.625 0.6875 0.75 0.8125 0.875 0.9375 1.0] km, _layers=[0.03125 0.09375 0.15625 0.21875 0.28125 0.34375 0.40625 0.46875 0.53125 0.59375 0.65625 0.71875 0.78125 0.84375 0.90625 0.96875] km, _layer_height=0.0625 km ) )
We see that our particle_layer_default
variable represents an aerosol layer with:
a vertical extent from 0 to 1 km;
a uniform particle density distribution within that extent;
a division into 16 uniform cells;
an optical thickness at 550 nm equal to 0.2.
We can now visualise the extinction coefficient using the following convenience function:
[5]:
plot_sigma_t(particle_layer_default)

Now, let’s define a particle layer with a different optical thickness:
[6]:
particle_layer_thick = ertsc.atmosphere.ParticleLayer(tau_ref=1.0)
plot_sigma_t(
particle_layer_default,
particle_layer_thick,
labels=["default", "thick"],
)

We can also modify the extent of our particle using the bottom and top parameters:
[7]:
particle_layer_high = ertsc.atmosphere.ParticleLayer(tau_ref=0.5, bottom=1 * ureg.km, top=2 * ureg.km)
plot_sigma_t(
particle_layer_default,
particle_layer_thick,
particle_layer_high,
labels=["default", "thick", "high"],
)

We can vary the particle density distribution within the defined bounds:
[8]:
particle_layer_gaussian = ertsc.atmosphere.ParticleLayer(
tau_ref=0.5,
bottom=0.5 * ureg.km,
top=1.5 * ureg.km,
distribution="gaussian",
n_layers=31,
)
plot_sigma_t(
particle_layer_default,
particle_layer_thick,
particle_layer_high,
particle_layer_gaussian,
labels=["default", "thick", "high", "gaussian"],
)

Adding a particle layer to a molecular atmosphere#
Although Eradiate can simulate radiative transfer in a single particle layer, realistic use cases rather involve adding a particle layer to a molecular atmosphere. For this, we use the HeterogeneousAtmosphere
class. This class acts as a container which can mix a MolecularAtmosphere
and an arbitrary number of ParticleLayer
s.
[9]:
exponential = ertsc.atmosphere.ParticleLayer(
tau_ref=0.05,
bottom=0 * ureg.km,
top=6 * ureg.km,
distribution="exponential",
)
gaussian = ertsc.atmosphere.ParticleLayer(
tau_ref=0.02,
bottom=1 * ureg.km,
top=2 * ureg.km,
distribution="gaussian",
)
atmosphere = ertsc.atmosphere.HeterogeneousAtmosphere(
molecular_atmosphere={"type": "molecular"},
particle_layers=[exponential, gaussian]
)
We can display the extinction coefficient for the full atmospheric profile and all its components as follows (note that we restrict the view range to the [0, 10] km extent for clarity
[10]:
plot_sigma_t(
atmosphere,
atmosphere.molecular_atmosphere,
exponential,
gaussian,
labels=["atmosphere", "molecular", "exponential", "gaussian"],
altitude_extent=(0, 10),
)

We see on this plot that the various components of the HeterogeneousAtmosphere
container are stacked as expected. We also see that the components are all assumed piecewise-constant on their own grid, then regridded on a fine mesh prior to merge.
Running the simulation#
We can now use this atmosphere definition to run a 1D simulation and compute the top-of-atmosphere BRF:
[11]:
# Show only spectral loop progress
eradiate.config.progress = "spectral_loop"
exp = eradiate.experiments.AtmosphereExperiment(
surface={"type": "lambertian", "reflectance": 1.0},
atmosphere=atmosphere,
illumination={"type": "directional", "zenith": 30.0, "azimuth": 0.0},
measures={
"type": "mdistant",
"construct": "hplane",
"zeniths": np.arange(-75, 76, 5),
"azimuth": 0.0,
"spectral_cfg": {"bins": ["550"]}, # Run the simulation for the 550 nm bin
"spp": 10000,
},
)
result = eradiate.run(exp)
result
[11]:
<xarray.Dataset> Dimensions: (sza: 1, saa: 1, w: 1, y_index: 1, x_index: 31, srf_w: 1) Coordinates: (12/13) * sza (sza) float64 30.0 * saa (saa) float64 0.0 * y_index (y_index) int64 0 y (y_index) float64 0.0 * x_index (x_index) int64 0 1 2 3 4 5 6 7 ... 23 24 25 26 27 28 29 30 x (x_index) float64 0.0 0.03333 0.06667 ... 0.9333 0.9667 1.0 ... ... * w (w) float64 550.0 bin_wmin (w) float64 545.0 bin_wmax (w) float64 555.0 vza (x_index, y_index) int64 -75 -70 -65 -60 -55 ... 60 65 70 75 vaa (x_index, y_index) int64 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 * srf_w (srf_w) float64 550.0 Data variables: radiance (sza, saa, w, y_index, x_index) float64 0.4191 ... 0.4234 spp (sza, saa, w) float64 1e+04 irradiance (sza, saa, w) float64 1.646 srf (srf_w) float64 1.0 brdf (sza, saa, w, y_index, x_index) float64 0.2546 ... 0.2572 brf (sza, saa, w, y_index, x_index) float64 0.7999 ... 0.8081 radiance_srf (sza, saa, y_index, x_index) float64 0.4191 ... 0.4234 irradiance_srf (sza, saa) float64 1.646 brdf_srf (sza, saa, y_index, x_index) float64 0.2546 ... 0.2572 brf_srf (sza, saa, y_index, x_index) float64 0.7999 ... 0.8081 Attributes: convention: CF-1.8 source: eradiate, version 0.22.6.dev24+ge8e434d7.d20221118 history: 2023-01-26 09:25:30 - data creation - AtmosphereExperiment.p... references: title: Top-of-atmosphere simulation results
And we can plot the computed TOA BRF:
[12]:
with plt.rc_context({"lines.marker": ".", "lines.linestyle": ":"}):
result.brf.squeeze(drop=True).plot(x="vza");

Final words#
This quick introduction provides just an overview of how particle layers are parametrised. In particular, particle radiative property datasets (the dataset
parameter of the ParticleLayer
constructor) are not (yet) covered by this tutorial. Until this is done, you can have a look in the resources/data/spectra/particles
and explore those datasets.
Further reading#
List of available particle density distributions:
particle_distribution_factory
Particle optical property data guide: Particle radiative properties