Source code for pyforestry.sweden.siteindex.sis.tegnhammar_1992

"""Tegnhammar (1992) site index estimation for Norway spruce.

Source:
    Tegnhammar, L. (1992). *Om skattningen av ståndortsindex för gran.* Sveriges
    lantbruksuniversitet, institutionen för skogstaxering, Rapport nr 53, Umeå, 259 s.
"""

# Tegnhammar Site Index corrections for Hägglund Site Index for Swedish Spruce.
from __future__ import annotations

import warnings
from math import exp
from typing import cast

from pyforestry.base.contracts import FormulaDescriptor, SourceReference
from pyforestry.base.helpers import Age, SiteIndexValue, TreeSpecies, enum_code
from pyforestry.sweden.geo.humidity.eriksson_1986 import eriksson_1986_humidity
from pyforestry.sweden.site.enums import Sweden
from pyforestry.sweden.siteindex.hagglund_1970 import Hagglund_1970


[docs] def tegnhammar_1992_adjusted_spruce_si_by_stand_variables( latitude: float, longitude: float, altitude: float, vegetation: int | Sweden.FieldLayer, ground_layer: int | Sweden.BottomLayer, aspect_main: int, soil_moisture: int | Sweden.SoilMoistureEnum, soil_depth: int | Sweden.SoilDepth, soil_texture: int | Sweden.SoilTextureTill | Sweden.SoilTextureSediment, humidity: float | None = None, ditched: bool = False, lateral_water: int | Sweden.SoilWater = 1, peat_humification: int | Sweden.PeatHumification = Sweden.PeatHumification.MEDIUM, epsg: str = "EPSG:4326", ) -> SiteIndexValue: """ Calculate Tegnhammar's adjusted Site Index from stand variables. Source: Tegnhammar, L. (1992). "Om skattningen av ståndortsindex för gran". Report 53. Dept. of Forest Survey. Swedish University of Agricultural Sciences. Umeå. Args: latitude (float): Decimal degrees. Default WGS84, see parameter ``epsg``. longitude (float): Decimal degrees. Default WGS84, see parameter ``epsg``. altitude (float): Metres above sea level. vegetation (int | Sweden.FieldLayer): Vegetation type. ground_layer (int | Sweden.BottomLayer): Ground layer type. aspect_main (int): Aspect code. Use 0 if slope is below 5%. soil_moisture (int | Sweden.SoilMoistureEnum): Soil moisture 1-5. soil_depth (int | Sweden.SoilDepth): Soil depth class. soil_texture ( int | Sweden.SoilTextureTill | Sweden.SoilTextureSediment ): Soil texture code. humidity (float | None, optional): Humidity during the vegetation period in mm. If ``None`` it will be calculated. ditched (bool): True if affected by ditching. lateral_water (int | Sweden.SoilWater): Water availability code. peat_humification (int | Sweden.PeatHumification): Peat decomposition level. epsg (str): Coordinate system (default is WGS84: ``"EPSG:4326"``). Returns: SiteIndexValue: Tegnhammar's adjusted SI in metres. """ if humidity is None: humidity = eriksson_1986_humidity(longitude=longitude, latitude=latitude) if humidity < 0: humidity = 0 # convert enums to numeric codes if needed vegetation = cast(int, enum_code(vegetation)) ground_layer = cast(int, enum_code(ground_layer)) soil_moisture = cast(int, enum_code(soil_moisture)) soil_depth = cast(int, enum_code(soil_depth)) soil_texture = cast(int, enum_code(soil_texture)) lateral_water = cast(int, enum_code(lateral_water)) peat_humification = cast(int, enum_code(peat_humification)) # Determine if site is north or south of Limes Norrlandicus # (Placeholder logic; replace with actual spatial analysis if required.) limes_n = latitude > 60.0 # Example condition; adjust as needed. lat_n = limes_n lat_s = not limes_n altitude_s_south_sweden = altitude if lat_s else 0 altitude_n_north_sweden = altitude if lat_n else 0 humidity_south_sweden_dry = humidity if lat_s and soil_moisture in [1, 2] else 0 humidity_north_sweden = humidity if lat_n else 0 humidity_north_sweden_lat_n = humidity_north_sweden * lat_n shallow = 1 if soil_depth != 1 else 0 dry = 1 if soil_moisture == 1 else 0 moist = 1 if soil_moisture == 4 else 0 peat = 1 if soil_texture == 9 else 0 if ditched and peat == 0 and moist == 0: warnings.warn( "Ditched only defined for peat or moist soils! Setting ditched to 0.", stacklevel=2 ) ditched = 0 # SwedenSoilWater codes: SELDOM_NEVER=1, SHORTER_PERIODS=2, LONGER_PERIODS=3 # (site/enums.py). The longer-periods flag previously tested ==4 (unreachable -> # its 12.557257 term was dead) and the shorter-periods flag tested ==3 (which is # LONGER, mis-applying the shorter coefficient). Map each to its real enum code, # matching the sibling HL1979 convention (==3 is longer periods). lateral_water_longer_periods = 1 if lateral_water == 3 else 0 lateral_water_shorter_periods = 1 if lateral_water == 2 else 0 coarse = 1 if soil_texture in [1, 2, 3] else 0 fine = 1 if soil_texture in [7, 8] else 0 # Defaults peat_humification_low, peat_humification_medium, peat_humification_high = 0, 0, 0 if peat == 1: peat_humification_low = 1 if peat_humification == 1 else 0 peat_humification_medium = 1 if peat_humification == 2 else 0 peat_humification_high = 1 if peat_humification == 3 else 0 aspect_east_south_east_incline = 1 if aspect_main in [6, 7] else 0 extremely_cold = max(altitude + (130 * latitude) - 8900, 0) # Use pyforestry.Geo.Geo.RetrieveGeoCode.getDistanceToCoast from pyforestry.sweden.geo.geo import RetrieveGeoCode distance_to_swedish_coast = ( RetrieveGeoCode().getDistanceToCoast(longitude, latitude) / 1000 ) # m to km distance_to_swedish_coast_2 = ( exp(-distance_to_swedish_coast / 5) if soil_moisture in [1, 2] else 0 ) # Vegetation types rich_herb_no_shrub = 1 if vegetation == 1 else 0 low_herb_no_shrub = 1 if vegetation == 3 else 0 herb_with_shrub = 1 if vegetation in [2, 3, 5, 6] else 0 no_field_layer = 1 if vegetation == 7 else 0 broadleaved_grass = 1 if vegetation == 8 else 0 thinleaved_grass = 1 if vegetation == 9 else 0 carex_or_equisetum = 1 if vegetation in range(10, 13) else 0 lingonberry = 1 if vegetation == 14 else 0 crowberry_or_worse = 1 if vegetation >= 15 else 0 # Ground layer sphagnum = 1 if ground_layer == 4 else 0 polytrichum = 1 if ground_layer == 5 else 0 # Calculation sih_just = ( 1210.813605 - 14.969124 * lat_n - 15.444848 * lat_s - 1.308729 * altitude_n_north_sweden - 0.000062709 * (altitude_n_north_sweden**2) - 0.048787 * altitude_n_north_sweden * moist + 0.000099984 * (altitude_n_north_sweden**2) * moist - 2.396754 * altitude_s_south_sweden - 0.000326 * (altitude_s_south_sweden**2) + 0.019672 * lat_n * altitude_n_north_sweden + 0.042405 * lat_s * altitude_s_south_sweden + 4.084904 * humidity_north_sweden - 0.064359 * humidity_north_sweden_lat_n - 0.085390 * humidity_north_sweden * moist + 0.026421 * humidity_south_sweden_dry - 22.492215 * distance_to_swedish_coast_2 - 6.494468 * dry + 3.284251 * moist + 8.004164 * lateral_water_shorter_periods + 12.557257 * lateral_water_longer_periods + 7.162013 * ditched - 2.241642 * fine - 3.944443 * coarse - 22.450707 * peat_humification_low - 10.735284 * peat_humification_medium - 2.872332 * peat_humification_high - 12.206813 * shallow + 5.792854 * shallow * moist + 36.421502 * rich_herb_no_shrub + 31.110677 * low_herb_no_shrub + 20.906555 * herb_with_shrub + 22.879371 * no_field_layer + 11.623366 * broadleaved_grass + 13.542164 * thinleaved_grass - 6.635013 * carex_or_equisetum - 10.954260 * lingonberry - 19.853882 * crowberry_or_worse - 15.615067 * sphagnum - 5.950684 * polytrichum - 0.062692 * extremely_cold + 5.299670 * aspect_east_south_east_incline ) SIS = sih_just / 100 fn = ( Hagglund_1970.height_trajectory.picea_abies.northern_sweden if latitude >= 60 else Hagglund_1970.height_trajectory.picea_abies.southern_sweden ) return SiteIndexValue( SIS, reference_age=Age.TOTAL(100), species={TreeSpecies.Sweden.picea_abies}, fn=fn, )
[docs] def tegnhammar_1992_adjusted_si_spruce(sih, dominant_age, latitude): """ Tegnhammar's correction for SI 1992. Args: sih (float): Site index in metres. dominant_age (float): Arithmetic mean of the age at breast height of the dominant trees. latitude (float): Latitude in degrees. Returns: float: Adjusted SIH in metres. """ return ((sih * 10) + (3.89 - 0.0498 * latitude) * (dominant_age - 15)) / 10
DESCRIPTOR = FormulaDescriptor( component_id="tegnhammar_1992_siteindex", source=SourceReference( author="Tegnhammar, L.", year=1992, title="Om skattningen av ståndortsindex för gran", note=( "Sveriges lantbruksuniversitet, institutionen för skogstaxering, " "Rapport nr 53, Umeå, 259 s." ), ), species_groups={"spruce": frozenset({"Picea abies"})}, units={}, kernel_names=( "tegnhammar_1992_adjusted_spruce_si_by_stand_variables", "tegnhammar_1992_adjusted_si_spruce", ), )