Source code for sasktran2.constituent.vmrabsorber2d

from __future__ import annotations

import numpy as np

import sasktran2 as sk
from sasktran2._core_rust import PyVMRAbsorber2D
from sasktran2.atmosphere import Atmosphere
from sasktran2.optical.base import OpticalProperty

from .base import Constituent


[docs] class VMRAbsorber2D(Constituent): """An absorber specified by VMR on the native Geometry2D grid. The VMR is stored in ``(horizontal, altitude)`` order and must match the :class:`~sasktran2.Geometry2D` used by the atmosphere. No spatial interpolation or horizontal broadcasting is performed. Parameters ---------- optical_property : OpticalProperty Property that supplies absorption cross sections. vmr : numpy.ndarray Volume mixing ratio with shape ``(horizontal, altitude)``. """ _constituent: PyVMRAbsorber2D
[docs] def __init__(self, optical_property: OpticalProperty, vmr: np.ndarray) -> None: super().__init__() vmr = np.asarray(vmr, dtype=np.float64) if vmr.ndim != 2: msg = f"vmr must have shape (horizontal, altitude); got {vmr.shape}" raise ValueError(msg) if 0 in vmr.shape: msg = "vmr horizontal and altitude dimensions must both be non-empty" raise ValueError(msg) self._volume_shape = vmr.shape self._constituent = PyVMRAbsorber2D( optical_property, np.ascontiguousarray(vmr).reshape(-1) ) self._optical_property = optical_property
@property def volume_spatial_mode(self) -> str: return "native_2d" @property def vmr(self) -> np.ndarray: """Volume mixing ratio in ``(horizontal, altitude)`` order.""" return self._constituent.vmr.reshape(self._volume_shape) @vmr.setter def vmr(self, vmr: np.ndarray) -> None: vmr = np.asarray(vmr, dtype=np.float64) if vmr.shape != self._volume_shape: msg = f"vmr must retain shape {self._volume_shape}; got {vmr.shape}" raise ValueError(msg) self._constituent.vmr = np.ascontiguousarray(vmr).reshape(-1) def _validate_atmosphere(self, atmo: Atmosphere) -> None: if not isinstance(atmo.model_geometry, sk.Geometry2D): msg = "VMRAbsorber2D requires an atmosphere using Geometry2D" raise TypeError(msg) if tuple(atmo.volume_shape) != self._volume_shape: msg = ( "VMRAbsorber2D shape does not match the atmosphere: " f"{self._volume_shape} != {atmo.volume_shape}" ) raise ValueError(msg) def add_to_atmosphere(self, atmo: Atmosphere) -> None: self._validate_atmosphere(atmo) self._constituent.add_to_atmosphere(atmo) def register_derivative(self, atmo: Atmosphere, name: str) -> None: self._validate_atmosphere(atmo) self._constituent.register_derivative(atmo, name)