Source code for pyforestry.base.helpers.primitives.diameter_cm

"""Diameter primitives and conversion helpers.

This module defines the ``Diameter_cm`` value object and helper functions for
converting between diameter and basal-area representations. The formulas mirror
the standard geometric relationships used in forestry growth calculations.

Source:
    Standard basal-area geometry for diameter/basal-area conversion
    workflows in ``pyforestry``.
"""

from __future__ import annotations

from math import pi, sqrt


[docs] class Diameter_cm(float): """ A diameter measurement in centimeters, with metadata. This class subclasses `float` to store a diameter value (cm) while also carrying: Attributes: over_bark (bool): Whether the diameter is measured over bark. measurement_height_m (float): Height at which the diameter was measured (in meters). """ # Declare the new instance attributes in __slots__ __slots__ = ("over_bark", "measurement_height_m") def __new__(cls, value: float, over_bark: bool = True, measurement_height_m: float = 1.3): """ Create a new `Diameter_cm` instance. Args: value (float): Diameter value in centimeters. Must be non-negative. over_bark (bool, optional): Whether the diameter is measured over bark. Defaults to True. measurement_height_m (float, optional): Height at which the diameter is measured, in meters. Must be non-negative. Defaults to 1.3. Raises: ValueError: If `value` is negative. ValueError: If `measurement_height_m` is negative. Returns: Diameter_cm: A new instance with the specified value and metadata. """ if value < 0: raise ValueError("Diameter must be non-negative.") if measurement_height_m < 0: raise ValueError("measurement_height_m must be >= 0 m!") # Create the instance using the parent's __new__ obj = super().__new__(cls, value) # Now, these assignments are valid and understood by linters obj.over_bark = over_bark obj.measurement_height_m = measurement_height_m return obj @property def value(self) -> float: """ Return the raw diameter value as a float. Returns: float: The diameter in centimeters. """ return float(self) def __repr__(self): """ Return the canonical string representation. Returns: str: String in the format "Diameter_cm(value, over_bark=..., measurement_height_m=...)". """ return ( f"Diameter_cm({float(self)}, over_bark={self.over_bark}, " f"measurement_height_m={self.measurement_height_m})" )
[docs] def diameter_to_basal_area_cm2(diameter_cm: float) -> float: """Compute basal area (cm²) from diameter (cm). Source: Standard basal-area geometry (area = pi * d^2 / 4). Args: diameter_cm (float): Diameter at breast height in centimeters. Returns: float: Basal area in cm². Raises: ValueError: If ``diameter_cm`` is negative. """ if diameter_cm < 0: raise ValueError("diameter_cm must be non-negative.") return (pi / 4.0) * (diameter_cm**2)
[docs] def basal_area_cm2_to_diameter_cm(basal_area_cm2: float) -> float: """Compute diameter (cm) from basal area (cm²). Source: Standard basal-area geometry (area = pi * d^2 / 4). Args: basal_area_cm2 (float): Basal area in cm². Returns: float: Diameter in centimeters. Raises: ValueError: If ``basal_area_cm2`` is negative. """ if basal_area_cm2 < 0: raise ValueError("basal_area_cm2 must be non-negative.") return sqrt(4.0 * basal_area_cm2 / pi)
[docs] def diameter_growth_to_basal_area_growth_cm2( diameter_cm: float, diameter_growth_cm: float ) -> float: """Convert diameter growth (cm) to basal area growth (cm²). Source: Standard basal-area geometry (area = pi * d^2 / 4). Args: diameter_cm (float): Current diameter (cm). diameter_growth_cm (float): Diameter increment (cm). Returns: float: Basal area growth (cm²). Raises: ValueError: If inputs are negative. """ if diameter_cm < 0: raise ValueError("diameter_cm must be non-negative.") if diameter_growth_cm < 0: raise ValueError("diameter_growth_cm must be non-negative.") return (pi / 4.0) * ((diameter_cm + diameter_growth_cm) ** 2 - diameter_cm**2)
[docs] def basal_area_growth_cm2_to_diameter_growth_cm( diameter_cm: float, basal_area_growth_cm2: float ) -> float: """Convert basal area growth (cm²) to diameter growth (cm). Source: Standard basal-area geometry (d = sqrt(4 * area / pi)). Args: diameter_cm (float): Current diameter (cm). basal_area_growth_cm2 (float): Basal area growth (cm²). Returns: float: Diameter increment (cm). Raises: ValueError: If inputs are negative. """ if diameter_cm < 0: raise ValueError("diameter_cm must be non-negative.") if basal_area_growth_cm2 < 0: raise ValueError("basal_area_growth_cm2 must be non-negative.") return sqrt((4.0 * basal_area_growth_cm2 / pi) + diameter_cm**2) - diameter_cm
__all__ = [ "Diameter_cm", "diameter_to_basal_area_cm2", "basal_area_cm2_to_diameter_cm", "diameter_growth_to_basal_area_growth_cm2", "basal_area_growth_cm2_to_diameter_growth_cm", ]