Source code for eradiate.scenes.bsdfs._rtls

from __future__ import annotations

import attrs
import mitsuba as mi
import pint
import pinttr

from ._core import BSDF
from ..core import traverse
from ..spectra import Spectrum, spectrum_factory
from ... import validators
from ...attrs import documented, parse_docs
from ...kernel import TypeIdLookupStrategy, UpdateParameter
from ...units import unit_context_config as ucc


[docs] @parse_docs @attrs.define(eq=False, slots=False) class RTLSBSDF(BSDF): """ RTLS BSDF [``rtls``]. This class implements the RossThick-LiSparse (RTLS) BRDF as described by the MODIS BRDF/Albedo Product ATBD :cite:`BU-MODISBRDFAlbedoProductATBD1999`. See Also -------- :ref:`plugin-bsdf-rtls` """ f_iso: Spectrum = documented( attrs.field( default=0.209741, converter=spectrum_factory.converter("dimensionless"), validator=[ attrs.validators.instance_of(Spectrum), validators.has_quantity("dimensionless"), ], ), doc="Isotropic scattering kernel parameter. " r"Defaults to :math:`f_{iso} = 0.209741`.", type=".Spectrum", init_type=".Spectrum or dict or float, optional", default="0.209741", ) f_vol: Spectrum = documented( attrs.field( default=0.004140, converter=spectrum_factory.converter("dimensionless"), validator=[ attrs.validators.instance_of(Spectrum), validators.has_quantity("dimensionless"), ], ), doc="Volumetric scattering from horizontally homogeneous leaf canopies " r"kernel parameter. Defaults to :math:`f_{vol} = 0.004140`.", type=".Spectrum", init_type=".Spectrum or dict or float, optional", default="0.004140", ) f_geo: Spectrum | None = documented( attrs.field( default=0.081384, converter=attrs.converters.optional( spectrum_factory.converter("dimensionless") ), validator=attrs.validators.optional( [ attrs.validators.instance_of(Spectrum), validators.has_quantity("dimensionless"), ] ), ), doc="Geometric-optical surface scattering kernel parameter. " r"Defaults to :math:`f_{geo} = 0.081384`.", type="float", init_type="float, optional", default="0.081384", ) h: pint.Quantity = documented( pinttr.field( default=2.0, units=ucc.deferred("dimensionless"), ), doc="Height-to-center-of-crown. Must be dimensionless.", type="quantity", init_type="quantity or float", default="2.0", ) r: pint.Quantity = documented( pinttr.field( default=1.0, units=ucc.deferred("dimensionless"), ), doc="Crown horizontal radius. " "Must not be zero.", type="quantity", init_type="quantity or float", default="1.0", ) b: pint.Quantity = documented( pinttr.field( default=1.0, units=ucc.deferred("dimensionless"), ), doc="Crown vertical radius. " "Must not be zero.", type="quantity", init_type="quantity or float", default="1.0", ) @r.validator def _r_validator(self, attribute, value): assert value != 0.0 @b.validator def _b_validator(self, attribute, value): assert value != 0.0 @property def template(self) -> dict: # Inherit docstring objects = { "f_iso": traverse(self.f_iso)[0], "f_vol": traverse(self.f_vol)[0], "f_geo": traverse(self.f_geo)[0], } result = { "type": "rtls", "h": self.h.magnitude, "r": self.r.magnitude, "b": self.b.magnitude, } for obj_key, obj_values in objects.items(): for key, value in obj_values.items(): result[f"{obj_key}.{key}"] = value if self.id is not None: result["id"] = self.id return result @property def params(self) -> dict[str, UpdateParameter]: # Inherit docstring objects = { "f_iso": traverse(self.f_iso)[1], "f_vol": traverse(self.f_vol)[1], "f_geo": traverse(self.f_geo)[1], } result = {} for obj_key, obj_params in objects.items(): for key, param in obj_params.items(): result[f"{obj_key}.{key}"] = attrs.evolve( param, lookup_strategy=TypeIdLookupStrategy( node_type=mi.BSDF, node_id=self.id, parameter_relpath=f"{obj_key}.{key}", ) if self.id is not None else None, ) return result