Source code for eradiate.spectral.ckd_quad
from __future__ import annotations
import enum
import warnings
import attrs
import pint
import xarray as xr
from ..attrs import documented, frozen
from ..quad import Quad, QuadType
from ..radprops import CKDAbsorptionDatabase
[docs]
class CKDQuadPolicy(enum.Enum):
"""
This enumeration defines flags mapping to policies defining the number of
CKD spectral quadrature points for a given spectral bin.
The ``"fixed"`` policy uses a constant number of quadrature points,
regardless of the spectral bin. The other policies will determine a number
of quadrature point based on molecular absorption data and an error
criterion, seeking to minimize or achieve an error on the total column
transmittance w.r.t. absorption for each spectral bin.
"""
FIXED = "fixed" #: Fixed number of quadrature points.
MINIMIZE_ERROR = "minimize_error"
"""Number of quadrature points that minimizes the error in each spectral bin
(not implemented yet)."""
ERROR_THRESHOLD = "error_threshold"
"""Optimal number of quadrature points that achieves the error threshold
(not implemented yet)."""
[docs]
@frozen
class CKDQuadConfig:
"""
This class holds configuration parameters for a CKD quadrature rule. Once
the quadrature definition is set, it can query an absorption database to
generate a quadrature rule for a specified spectral bin using its
:meth:`.get_quad` method.
"""
type: QuadType = documented(
attrs.field(default="gauss_legendre", converter=QuadType),
doc="Quadrature type.",
type=".QuadType",
init_type=".QuadType or str",
default='"gauss_legendre"',
)
ng_max: int = documented(
attrs.field(default=1, converter=int),
doc="Maximum number of quadrature points.",
type="int",
default="1",
)
policy: CKDQuadPolicy = documented(
attrs.field(default="fixed", converter=CKDQuadPolicy),
doc="Quadrature definition policy.",
type=".CKDQuadPolicy",
init_type=".CKDQuadPolicy or str",
default="fixed",
)
[docs]
@classmethod
def convert(self, value) -> CKDQuadConfig:
"""
Convert a value to a :class:`.CKDQuadConfig`. If ``value`` is a
dictionary, its values are passed to the constructor as keyword
arguments. Otherwise, ``value`` is returned unchanged.
"""
if isinstance(value, dict):
return CKDQuadConfig(**value)
else:
return value
[docs]
def get_quad(
self,
abs_db: CKDAbsorptionDatabase | None = None,
wcenter: pint.Quantity | float | None = None,
):
"""
Generate a quadrature rule for a specific bin, using information from a
specified molecular absorption database.
Parameters
----------
abs_db : .CKDAbsorptionDatabase, optional
An absorption database with a transmittance error variable.
wcenter : quantity or float, optional
The central wavelength of the spectral bin for which the quadrature
rule is requested. Unitless values are interpreted in default
wavelength units (generally nm).
Returns
-------
.CKDQuad
"""
if abs_db is None:
# If no spectral information is passed, use default policy
ng = self.ng_max
else:
if self.policy is not CKDQuadPolicy.FIXED:
# TODO: Temporary, implement advanced quadrature point count
# policies when sample count allocation is refactored
warnings.warn(
"An advanced spectral quadrature configuration was defined, "
"but this feature is currently disabled. Falling back to a "
f"fixed quadrature point policy with {self.ng_max} g-points."
)
ng = self.ng_max
return Quad.new(type=self.type, n=ng)
# TODO: Refactor to use new absorption database format
def ng_minimum(error: xr.DataArray, ng_max: int | None = None):
"""
Find the number of quadrature points that minimizes the error.
Parameters
----------
error : DataArray
Error data.
ng_max : int, optional
Maximum number of quadrature points. If not provided, it will be
inferred from the error data.
Returns
-------
int
Number of quadrature points that minimizes the error.
"""
if ng_max is None:
ng_max = int(error.ng.max())
error_w0 = error.isel(w=0)
ng_min = int(error.ng.where(error_w0 == error_w0.min(), drop=True)[0])
return ng_max if ng_min > ng_max else ng_min
# TODO: Refactor to use new absorption database format
def ng_threshold(error: xr.DataArray, threshold: float, ng_max: int | None = None):
"""
Find the number of quadrature points so that the error is (strictly) below
a specified threshold value.
Parameters
----------
error : DataArray
Error data.
threshold : float
Error threshold.
ng_max : int, optional
Maximum number of quadrature points. If not provided, it will be
inferred from the error data.
Returns
-------
int
Number of quadrature points so that the error is below the threshold.
"""
if ng_max is None:
ng_max = int(error.ng.max())
error_w0 = error.isel(w=0)
ng = error.ng.where(error_w0 < threshold, drop=True)
if ng.size == 0:
return ng_max
else:
ng = int(ng[0])
return ng_max if ng > ng_max else ng