Spectral discretization¶
In this guide, we give an overview of how Eradiate handles the spectral dimension during simulations.
Note
This section assumes familiarity with Experiment objects. For an introduction, see First steps with Eradiate and Molecular atmosphere basics.
Upon calling the eradiate.run() function, Eradiate runs successive simulations as part of a so-called spectral loop. Each iteration of that loop covers a subset of the spectral space, and the chunking of that space depends on three factors:
the current active mode, which determines the general spectral discretization method and the underlying Python class that represents it;
scene and user basic constraints, which define a background spectral grid;
the radiometric instrument or measure that is being simulated, which will apply filters to the background spectral grid to optimize it.
Data types¶
Spectral grid types and their mode dependence¶
In monochromatic modes, the spectral grid is discretized into a list of wavelengths. The corresponding type is MonoSpectralGrid and is simply initialized with the list of wavelengths:[1] [2]
[2]:
MonoSpectralGrid(wavelengths=np.arange(500, 601, 1))
[2]:
In CKD modes, the spectral grid is discretized into a set of spectral bins defined by their lower and upper bounds:[3] [4]
If passed a unitless value, the parameters of the CKDSpectralGrid are interpreted in default wavelength units (usually nm).
On the graphical representation, each bin on the mesh is displayed as a rectangle. The central wavelength is displayed as a line.
[3]:
CKDSpectralGrid(np.arange(495, 596, 10), np.arange(505, 606, 10))
[3]:
Both grid types can be created with a convenience SpectralGrid.arange() attached to their common SpectralGrid type. In that case, the correct type is dynamically selected depending on the currently active mode:
[4]:
eradiate.set_mode("mono")
SpectralGrid.arange(500, 601, 1) # Returns a MonoSpectralGrid
[4]:
[5]:
eradiate.set_mode("ckd")
SpectralGrid.arange(500, 601, 1) # Returns a CKDSpectralGrid
[5]:
Spectral responses functions (SRF)¶
Measures are assigned a spectral response function (SRF) described by the SpectralResponseFunction type and its child classes. The three types are:
BandSRF: a piecewise-linear SRF covering an arbitrary interval that can be initialized from a NetCDF dataset holding SRF data characterizing a radiometric instrument
[6]:
BandSRF.from_id("sentinel_2a-msi-3")
[6]:
UniformSRF: a constant SRF covering an arbitrary interval
[7]:
UniformSRF(wmin=500, wmax=600, value=1.0)
[7]:
DeltaSRF: a combination of one or several Dirac delta distributions
[8]:
DeltaSRF(wavelengths=[440, 550, 660])
[8]:
The background grid¶
Each Experiment instance holds a background grid that serves as the starting point to the spectral grid assembly. When the experiment defines an atmosphere, this background grid is imposed by the available atmospheric molecular absorption database (if it exists). Otherwise, it can be specified using the optional background_spectral_grid parameter.
As an example, this is the background spectral grid for an atmosphere-free experiment in monochromatic mode:
[9]:
eradiate.set_mode("mono")
exp = eradiate.experiments.AtmosphereExperiment(atmosphere=None)
display(exp.background_spectral_grid)
pprint(exp.background_spectral_grid)
MonoSpectralGrid(_wavelengths=[280. 281. 282. 283. ... 2397. 2398. 2399. 2400.] nm)
The background spectral grid for the default atmosphere setup in monochromatic mode, inherited from the komodo dataset has variable spectral density: [5]
You can zoom in to the [300, 301] nm interval by calling spectral_grid.select({"type": "uniform", "wmin": 300, "wmax": 301})
[10]:
eradiate.set_mode("mono")
exp = eradiate.experiments.AtmosphereExperiment(atmosphere={"type": "molecular"})
display(exp.background_spectral_grid)
pprint(exp.background_spectral_grid)
MonoSpectralGrid( │ _wavelengths=[250.0021 250.00835 250.0146 250.02086 ... 3122.073 3123.0483 3124.0237 3125. ] nm )
In CKD mode, the default absorption database has constant bin size in the wavenumber space, which results in bin increasing with wavelength:
[11]:
eradiate.set_mode("ckd")
exp = eradiate.experiments.AtmosphereExperiment(atmosphere={"type": "molecular"})
display(exp.background_spectral_grid)
pprint(exp.background_spectral_grid)
CKDSpectralGrid( │ wmins=[250. 250.62656642 251.25628141 251.88916877 ... 2777.77777778 2857.14285714 2941.17647059 3030.3030303 ] nm, │ wmaxs=[250.62656642 251.25628141 251.88916877 252.52525253 ... 2857.14285714 2941.17647059 3030.3030303 3125. ] nm, │ wcenters=[250.3129 250.94102 251.57233 252.2068 ... 2816.9016 2898.5505 2985.0745 3076.923 ] nm )
Setting an SRF for your measure (CKD mode)¶
When we specify a measure and its SRF, it is used to select the relevant wavelengths or spectral bins in the background spectral grid. To our previous experiment definition, we add a measure specification that uses the Sentinel-2A band 11 spectral response (this is done by simply specifying the SRF dataset ID, chosen from the SRF database):
[12]:
eradiate.set_mode("ckd")
exp = eradiate.experiments.AtmosphereExperiment(
atmosphere={"type": "molecular"},
measures={
"type": "mdistant",
"srf": "sentinel_2a-msi-11",
},
)
We can plot the background spectral grid and our measure SRF:
[13]:
fig, ax = plt.subplots(1, 1, figsize=(6, 2))
exp.background_spectral_grid.plot(ax, alpha=0.25)
exp.measures[0].srf.plot(ax, alpha=0)
ax.set_xlabel("Wavelength [nm]")
plt.show()
plt.close()
Now, if we inspect the spectral grid associated with the defined measure, we can see that the bins out of the spectral range covered by the instrument are removed:
[14]:
fig, ax = plt.subplots(1, 1, figsize=(6, 2))
exp.spectral_grids[0].plot(ax, alpha=0.25)
exp.measures[0].srf.plot(ax, alpha=0)
ax.set_xlabel("Wavelength [nm]")
plt.show()
plt.close()
We can use the other SRF types.
A UniformSRF will keep the bins that intersect the interval it covers:
[15]:
eradiate.set_mode("ckd")
exp = eradiate.experiments.AtmosphereExperiment(
atmosphere={"type": "molecular"},
measures={
"type": "mdistant",
"srf": {"type": "uniform", "wmin": 500, "wmax":600}
}
)
fig, ax = plt.subplots(1, 1, figsize=(6, 2))
exp.spectral_grids[0].plot(ax, alpha=0.25)
exp.measures[0].srf.plot(ax, alpha=0)
ax.set_xlabel("Wavelength [nm]")
plt.show()
plt.close()
A:class:.DeltaSRF will keep the bins that contain one of the wavelengths of its delta functions:
[16]:
eradiate.set_mode("ckd")
exp = eradiate.experiments.AtmosphereExperiment(
atmosphere={"type": "molecular"},
measures={
"type": "mdistant",
"srf": {"type": "delta", "wavelengths": [440, 550, 660]}
}
)
fig, ax = plt.subplots(1, 1, figsize=(6, 2))
exp.spectral_grids[0].plot(ax, alpha=0.25)
exp.measures[0].srf.plot(ax)
ax.set_xlabel("Wavelength [nm]")
plt.show()
plt.close()
Setting an SRF for your measure (monochromatic mode)¶
Similarly, in monochromatic modes, a BandSRF will retain the wavelengths of the background spectral grid that are included in its support:
[17]:
eradiate.set_mode("mono")
exp = eradiate.experiments.AtmosphereExperiment(
measures={
"type": "mdistant",
"srf": "sentinel_2a-msi-4",
}
)
fig, ax = plt.subplots(1, 1, figsize=(6, 2))
exp.spectral_grids[0].plot(ax, alpha=0.5)
exp.measures[0].srf.plot(ax, alpha=0)
ax.set_xlabel("Wavelength [nm]")
plt.show()
plt.close()
A UniformSRF will retain also the wavelengths of the background spectral grid that are included in its support:
[18]:
eradiate.set_mode("mono")
exp = eradiate.experiments.AtmosphereExperiment(
measures={
"type": "mdistant",
"srf": {"type": "uniform", "wmin": 500, "wmax": 550}
}
)
fig, ax = plt.subplots(1, 1, figsize=(6, 2))
exp.spectral_grids[0].plot(ax, alpha=0.25)
exp.measures[0].srf.plot(ax, alpha=0)
ax.set_xlabel("Wavelength [nm]")
plt.show()
plt.close()
On the other hand, a DeltaSRF will always override the background grid:
[19]:
eradiate.set_mode("mono")
exp = eradiate.experiments.AtmosphereExperiment(
measures={
"type": "mdistant",
"srf": {"type": "delta", "wavelengths": [440, 550, 660]}
}
)
fig, ax = plt.subplots(1, 1, figsize=(6, 2))
exp.spectral_grids[0].plot(ax, alpha=0.25)
exp.measures[0].srf.plot(ax)
ax.set_xlabel("Wavelength [nm]")
plt.show()
plt.close()