Source code for Munin.SiteIndex.sweden.SIS.HL1979_SIS

# Version 0.90 2024-06-17

#Estimate Site Index H100 for Spruce in Sweden by stand factors.

#Canonical Source:
#Corrected functions from Appendix II for estimation of site index
#by site factors according to Hägglund and Lundmark (1977), in
#Hägglund, B. (1979) Ett system för bonitering av skogsmark - analys,
#kontroll och diskussion inför praktisk tillämpning. Rapport 14, Projekt
#HUGIN. Skogsvetenskapliga fakulteten, Sveriges Lantbruksuniversitet.
#Umeå. Sweden.
#     
#Hägglund, B., Lundmark, J-E. (1977) Skattning av höjdboniteten med
#ståndortsfaktorer: Tall och gran i Sverige. Research Notes nr. 28. Dept.
#Forest Ecology & Forest Soils. Royal College of Forestry. Stockholm. Sweden.


# Author: Carl Vigren, Dept. Forest Resource Management, SLU Umeå.
# Written to match results to SIS NFI routine. 



import numpy as np
from pandas import isna as pdisna
from typing import Union
from math import exp

[docs] def NFI_SIS_SPRUCE( **kwargs ) -> float: """ Estimate Site Index H100 for Spruce in Sweden by stand factors. Source: Corrected functions from Appendix II for estimation of site index by site factors according to Hägglund and Lundmark (1977), in Hägglund, B. (1979) Ett system för bonitering av skogsmark - analys, kontroll och diskussion inför praktisk tillämpning. Rapport 14, Projekt HUGIN. Skogsvetenskapliga fakulteten, Sveriges Lantbruksuniversitet. Umeå. Sweden. Hägglund, B., Lundmark, J-E. (1977) Skattning av höjdboniteten med ståndortsfaktorer: Tall och gran i Sverige. Research Notes nr. 28. Dept. Forest Ecology & Forest Soils. Royal College of Forestry. Stockholm. Sweden. Details: Uses corrected functions from appendix II in Hägglund & Lundmark 1979, except for when used on peat soils, in which case functions f. 8.1 & f. 8.2. pages 138-139 from Hägglund & Lundmark (1977) is used. Parameters: vegetation (int): Integer 1-18 groundLayer (int): Integer 1-6 latitude (float): Decimal WGS84. altitude (float): Meters above sea level. aspect (int): Main aspect of the site. int 0-360. incline_percent (float): Incline of slope, in percent. Default == 0. soil_moisture (int): Integer 1-5. 1: Dry, 2: Mesic, 3: Mesic-Moist, 4: Moist, 5: Wet soil_texture (int): Integer 1-9. see [forester::Sweden_soil_types(type="texture")] soil_depth (int): Integer 1-5. 1: Deep >70 cm, 2: Rather shallow 20-70 cm, 3: Shallow < 20 cm, 4: Varying. 5: New nfi designation 'häll', en: 'outcrop'. lateral_water (int): Integer 1-3. 1: Seldom/never, 2: Shorter periods, 3: Longer periods. ditched (bool): FALSE (default) if plot affected by ditching. climate_code (str): One of: NA (default),"M1","M2","K1","K2","K3". coast (bool): FALSE (default) if plot is closer to coast than 50km. limes_norrlandicus (bool): FALSE (default) if plot is north of Sernanders Limes Norrlandicus (used for Peat soils only.) gotland (bool): FALSE (default) if plot is located in Gotland. nfi_adjustments (bool): TRUE (default) if adjustments based on NFI should be applied. N.B. This applies coastal correction n. of Limes norrlandicus always. dlan (int): Optional. None (default). 1-31 according to NFI ID. Returns: float: Site Index H100 (meters). Will return NA & issue a warning message if no method was found. """ latitude = kwargs.get('latitude') altitude = kwargs.get('altitude') soil_moisture = kwargs.get('soil_moisture') ground_layer = kwargs.get('ground_layer') vegetation = kwargs.get('vegetation') soil_texture = kwargs.get('soil_texture') climate_code = kwargs.get('climate_code') lateral_water = kwargs.get('lateral_water') soil_depth = kwargs.get('soil_depth') incline_percent = kwargs.get('incline_percent') aspect = kwargs.get('aspect') ditched = kwargs.get('ditched') peat = kwargs.get('peat') gotland = kwargs.get('gotland') dlan = kwargs.get('dlan') limes_norrlandicus = kwargs.get('limes_norrlandicus') coast = kwargs.get('coast') nfi_adjustments = kwargs.get('nfi_adjustments') Adjustment = 1.00 changeToPine = False #helpers deepsoil = 1 rshallow = 2 shallow = 3 varying = 4 New_SoilMoisture = soil_moisture New_GroundLayer = ground_layer New_Vegetation = vegetation New_SoilTexture = soil_texture New_LateralWater = lateral_water New_SoilDepth = soil_depth #nfi-code for missing soil (0). Replace with shallow, stony soil if not peat: if New_SoilTexture == 0: New_SoilDepth = shallow New_SoilTexture = 1 #if dry soil and swamp-mosses, set soil_moisture to mesic. if New_SoilMoisture == 1 and New_GroundLayer in [2, 4, 5]: New_SoilMoisture = 2 #Spruce cannot be placed on dry soil, switch to mesic. if New_SoilMoisture == 1: print('Warning: No coverage for Spruce on dry soils, switching to mesic.') New_SoilMoisture = 2 #moist-wet mineral soils with lichen-dominated ground layer: set vegetation to horsetail. ground-layer to swamp-mosses. if not peat and New_SoilMoisture in [3, 4] and New_GroundLayer == 1: New_Vegetation = 12 New_GroundLayer = 5 #If shallow or varying (3,4) or stony soil + not under longer periods groundwater feeding: set soil moisture to dry. #reduce coefficient 10 percent. #change to Pine. if not peat: if (New_SoilDepth >= shallow or New_SoilTexture == 1) and New_LateralWater < 3: New_SoilMoisture = 1 Adjustment = 0.90 changeToPine = True #If lichen groundlayer and Spruce, reduce by 20 percent. if New_GroundLayer in [1, 2, 3] and not changeToPine: Adjustment *= 0.80 #If shallow or stony soil. # If lateral groundwater feeding is not longer periods, reclass soil depth to rather shallow. # Otherwise, set texture to gravel (2) if not peat: if New_SoilDepth >= shallow or New_SoilTexture == 1: if New_LateralWater < 3: New_SoilDepth = rshallow if New_LateralWater == 3: New_SoilTexture = 2 #if wet with no lateral water movement.. if New_SoilMoisture == 5 and New_LateralWater == 1: New_GroundLayer = 5 #groundlayer will be swamp-moss. Adjustment *= 0.70 #reduce SI by 30%. New_LateralWater = 3 #lateralwater movement is longer periods. # if wet with lateral water movement. if New_SoilMoisture == 5 and New_LateralWater == 3: New_GroundLayer = 5 #groundlayer will be swamp moss. New_LateralWater = 3 #this is superfluous. #if wet class soil moisture to moist... if New_SoilMoisture == 5: New_SoilMoisture = 4 #SoilTextureIndex = 2.265 - 0.204 * soilTexture + 2.94 / soilTexture #Set to 2.2 if New_SoilTexture not found. SoilTextureIndex = { 0: 5.0, 1: 5.0, 2: 3.5, 3: 2.7, 4: 2.2, 5: 1.8, 6: 1.4, 7: 1.2, 8: 1.0, 9: 2.0 }.get(New_SoilTexture, 2.2) if changeToPine and peat: FGRAN = 5.03077 - 0.03627 * (latitude - 60.0) if nfi_adjustments: #Always apply close to coast and norrland for same as NFI results. FGRAN -= 0.000000901 * (altitude ** 2) else: FGRAN -= 0.000000901 * (altitude ** 2) * ~coast * limes_norrlandicus #Ordinary routine. i.e. no effect for areas close to coast north of limes norrlandicus. if New_LateralWater == 3: FGRAN += 0.09195 if ditched: FGRAN += 0.13622 if 2 <= New_GroundLayer <= 5: #Lichen-rich types, sphagnum and swamp-mosses. FGRAN -= 0.1497 if New_Vegetation < 7: #herb type. FGRAN += 0.22273 if New_Vegetation == 13: #bilberry type FGRAN += 0.13997 if New_Vegetation in [7, 8, 9, 14]: #no field layer, grasses and cowberry. FGRAN += 0.13377 elif changeToPine and New_SoilMoisture == 2 and New_Vegetation > 9 and New_GroundLayer > 3: FGRAN = 5.30943 - 0.01716 * (latitude - 60.0 + abs(latitude- 60.0)) - 0.0039 * (latitude - 60.0 - abs(latitude - 60.0)) - 0.000000678 * (altitude ** 2) - 0.01243 * (SoilTextureIndex ** 2) if New_LateralWater == 3: FGRAN += 0.0488 if New_Vegetation == 13: #bilberry FGRAN += 0.09429 if New_Vegetation == 14: #cowberry FGRAN += 0.06167 if New_SoilDepth == deepsoil: FGRAN += 0.1158 if New_Vegetation == 16: #poor shrubs.. FGRAN -= 0.07775 elif changeToPine and New_SoilMoisture == 2 and New_Vegetation > 9 and New_GroundLayer <= 3: FGRAN = 5.21803 - 0.01193 * (latitude - 60.0 + abs(latitude - 60.0)) if New_LateralWater in [1, 2]: FGRAN -= 0.000000593 * (altitude ** 2) if New_LateralWater == 3: FGRAN -= 0.000000355 * (altitude ** 2) if New_SoilDepth == deepsoil: FGRAN += 0.12454 if incline_percent <= 10.0 and New_LateralWater == 1: #if incline and seldom/never lateral groundwater.. FGRAN -= 0.06329 if altitude >= 350 and incline_percent > 10.0 and (aspect in range(0, 113) or aspect > 337): FGRAN -= 0.07189 if New_GroundLayer == 1: FGRAN -= 0.06842 elif changeToPine and New_SoilMoisture == 2 and New_Vegetation <= 9: FGRAN = 5.34912 - 0.02037 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.000000481 * (altitude ** 2) if New_SoilDepth == deepsoil: FGRAN += 0.11574 if climate_code == 'M2':# and 14 <= Dlan <= 31: # S. sv. FGRAN -= 0.16403 if New_Vegetation in [4, 5, 8, 9]: FGRAN += 0.08376 if New_Vegetation in [1, 2]: FGRAN += 0.12296 elif changeToPine and New_SoilMoisture == 1: FGRAN = 5.44789 - 0.01566 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.00000102 * (altitude ** 2) - 0.0417 * SoilTextureIndex if New_SoilDepth == deepsoil: FGRAN += 0.09162 if climate_code == 'K3': FGRAN += 0.12 if New_GroundLayer == 1: FGRAN -= 0.19805 if New_GroundLayer in [2, 3]: FGRAN -= 0.1381 if New_GroundLayer >= 4 and New_Vegetation in [1, 2, 3, 4, 5, 6, 8]: FGRAN += 0.0953 if New_GroundLayer >= 4 and New_Vegetation in [7, 9, 13]: FGRAN += 0.0488 elif changeToPine and New_SoilMoisture in [3, 4, 5]: FGRAN = 5.46782 - 0.02013 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.01074 * (SoilTextureIndex ** 2) if New_LateralWater in [1, 2]: FGRAN -= 0.01517 * 0.0001 * (altitude ** 2) if New_LateralWater == 3: FGRAN -= 0.00747 * 0.0001 * (altitude ** 2) if New_Vegetation <= 6: FGRAN += 0.11585 if New_Vegetation in [10, 11, 12]: FGRAN -= 0.22358 if New_Vegetation in [7, 8, 9, 13, 14]: FGRAN += 0.0770 if New_Vegetation in [15, 16]: FGRAN -= 0.0726 if ground_layer in [2, 3, 4, 5]: FGRAN -= 0.0730 elif peat: FGRAN = 5.51735 - 0.03958 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.02466 * (latitude - 60.0 - abs(latitude - 60.0)) if New_LateralWater in [1, 2] and nfi_adjustments: #Always apply close to coast and norrland for same as NFI results. HL1979 removed coast limitation for altitude expression for MINERAL soils. FGRAN -= 0.000001174 * (altitude ** 2) elif New_LateralWater in [1, 2]: FGRAN -= 0.000001174 * (altitude ** 2) * ~coast * limes_norrlandicus #Ordinary routine. i.e. no effect for areas close to coast north of limes norrlandicus. if New_LateralWater == 3 and nfi_adjustments: FGRAN -= 0.000000899 * (altitude ** 2) elif New_LateralWater == 3: FGRAN -= 0.000000899 * (altitude ** 2) * ~coast * limes_norrlandicus #Ordinary routine. i.e. no effect for areas close to coast north of limes norrlandicus. if New_LateralWater == 2: FGRAN += 0.04577 if New_LateralWater == 3: FGRAN += 0.063 if ditched: FGRAN += 0.04428 if 2 <= New_GroundLayer <= 5: FGRAN -= 0.20935 if New_Vegetation in [1, 4, 7]: FGRAN += 0.09517 if New_Vegetation in [2, 3, 5, 6, 13, 14]: FGRAN -= 0.04868 if New_Vegetation in [10, 11, 12, 15, 16]: FGRAN -= 0.12545 elif New_SoilMoisture in [1, 2] and New_Vegetation > 9: FGRAN = 5.51876 - 0.04342 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.01837 * (latitude - 60.0 - abs(latitude - 60.0)) if New_LateralWater in [1, 2]: FGRAN -= 0.000001095 * (altitude ** 2) if New_LateralWater == 3: FGRAN -= 0.000000716 * (altitude ** 2) if New_LateralWater == 2: FGRAN += 0.03361 if New_LateralWater == 3: FGRAN += 0.04605 if 2 <= New_GroundLayer <= 5: FGRAN -= 0.073 if New_Vegetation == 13: FGRAN += 0.07842 elif New_SoilMoisture in [1, 2] and New_Vegetation <= 9: FGRAN = 5.68205 - 0.03423 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.02122 * (latitude - 60.0 - abs(latitude - 60.0)) - 0.000000691 * (altitude ** 2) if New_LateralWater == 2: FGRAN += 0.03247 if New_LateralWater == 3: FGRAN += 0.05097 if climate_code == 'M2': if dlan is not None: if 14 <= dlan <= 31: # S. sv.? FGRAN -= 0.10806 else: FGRAN -= 0.10806 if New_Vegetation in [2, 3, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16]: FGRAN -= 0.02991 if New_Vegetation in [8, 9]: FGRAN -= 0.06787 if New_Vegetation == 1: FGRAN += 0.039 if 2 <= New_GroundLayer <= 5: FGRAN -= 0.073 elif New_SoilMoisture in [3, 4, 5]: FGRAN = 5.59884 - 0.03722 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.02499 * (latitude - 60.0 - abs(latitude - 60.0)) if New_LateralWater in [1, 2]: FGRAN -= 0.000001206 * (altitude ** 2) if New_LateralWater == 3: FGRAN -= 0.000000937 * (altitude ** 2) if New_LateralWater == 2: FGRAN += 0.04766 if New_LateralWater == 3: FGRAN += 0.05939 if ditched: FGRAN += 0.02383 if New_Vegetation == 1: FGRAN += 0.012 if New_Vegetation == 7: FGRAN += 0.08075 if New_Vegetation == 4: FGRAN += 0.05342 if New_Vegetation in [13, 14]: FGRAN -= 0.05889 if New_Vegetation in [10, 11, 12, 15, 16]: FGRAN -= 0.16425 if 2 <= New_GroundLayer <= 5: FGRAN -= 0.073 else: raise ValueError('No SIS method found.') if gotland: Adjustment *= 0.80 #0.1 converts dm to m. SIS = 0.1 * exp(FGRAN) * Adjustment if SIS < 0.0: raise ValueError('SIS estimated < 0.') if SIS > 50.0: raise ValueError('SIS estimated > 50') return SIS
[docs] def NFI_SIS_PINE( **kwargs ) -> float: """ Estimate Site Index H100 for Pine in Sweden by stand factors. Source: Corrected functions from Appendix II for estimation of site index by site factors according to Hägglund and Lundmark (1977), in Hägglund, B. (1979) Ett system för bonitering av skogsmark - analys, kontroll och diskussion inför praktisk tillämpning. Rapport 14, Projekt HUGIN. Skogsvetenskapliga fakulteten, Sveriges Lantbruksuniversitet. Umeå. Sweden. Hägglund, B., Lundmark, J-E. (1977) Skattning av höjdboniteten med ståndortsfaktorer: Tall och gran i Sverige. Research Notes nr. 28. Dept. Forest Ecology & Forest Soils. Royal College of Forestry. Stockholm. Sweden. Details: Uses corrected functions from appendix II in Hägglund & Lundmark 1979, except for when used on peat soils, in which case functions f. 8.1 & f. 8.2. pages 138-139 from Hägglund & Lundmark (1977) is used. Parameters: vegetation (int): Integer 1-18 groundLayer (int): Integer 1-6 latitude (float): Decimal WGS84. altitude (float): Meters above sea level. aspect (int): Main aspect of the site. int 0-360. incline_percent (float): Incline of slope, in percent. Default == 0. soil_moisture (int): Integer 1-5. 1: Dry, 2: Mesic, 3: Mesic-Moist, 4: Moist, 5: Wet soil_texture (int): Integer 1-9. see [forester::Sweden_soil_types(type="texture")] soil_depth (int): Integer 1-5. 1: Deep >70 cm, 2: Rather shallow 20-70 cm, 3: Shallow < 20 cm, 4: Varying. 5: New nfi designation 'häll', en: 'outcrop'. lateral_water (int): Integer 1-3. 1: Seldom/never, 2: Shorter periods, 3: Longer periods. ditched (bool): FALSE (default) if plot affected by ditching. climate_code (str): One of: NA (default),"M1","M2","K1","K2","K3". coast (bool): FALSE (default) if plot is closer to coast than 50km. limes_norrlandicus (bool): FALSE (default) if plot is north of Sernanders Limes Norrlandicus (used for Peat soils only.) gotland (bool): FALSE (default) if plot is located in Gotland. nfi_adjustments (bool): TRUE (default) if adjustments based on NFI should be applied. N.B. This applies coastal correction n. of Limes norrlandicus always. Returns: float: Site Index H100 (meters). Will return NA & issue a warning message if no method was found. """ latitude = kwargs.get('latitude') altitude = kwargs.get('altitude') soil_moisture = kwargs.get('soil_moisture') ground_layer = kwargs.get('ground_layer') vegetation = kwargs.get('vegetation') soil_texture = kwargs.get('soil_texture') climate_code = kwargs.get('climate_code') lateral_water = kwargs.get('lateral_water') soil_depth = kwargs.get('soil_depth') incline_percent = kwargs.get('incline_percent') aspect = kwargs.get('aspect') ditched = kwargs.get('ditched') peat = kwargs.get('peat') gotland = kwargs.get('gotland') dlan = kwargs.get('dlan') limes_norrlandicus = kwargs.get('limes_norrlandicus') coast = kwargs.get('coast') nfi_adjustments = kwargs.get('nfi_adjustments') Adjustment = 1.00 #helpers deepsoil = 1 rshallow = 2 shallow = 3 varying = 4 New_SoilMoisture = soil_moisture New_GroundLayer = ground_layer New_Vegetation = vegetation New_SoilTexture = soil_texture New_LateralWater = lateral_water New_SoilDepth = soil_depth #nfi-code for missing soil (0). Replace with shallow, stony soil if not peat: if New_SoilTexture == 0: New_SoilDepth = shallow New_SoilTexture = 1 #if dry soil and swamp-mosses, set soil_moisture to mesic. if New_SoilMoisture == 1 and New_GroundLayer in [2, 4, 5]: New_SoilMoisture = 2 #moist-wet mineral soils with lichen-dominated ground layer: set vegetation to horsetail. ground-layer to swamp-mosses. if not peat and New_SoilMoisture in [3, 4] and New_GroundLayer == 1: New_Vegetation = 12 New_GroundLayer = 5 #If shallow or varying (3,4) or stony soil + not under longer periods groundwater feeding: set soil moisture to dry. #reduce coefficient 10 percent. if not peat: if (New_SoilDepth >= shallow or New_SoilTexture == 1) and New_LateralWater < 3: New_SoilMoisture = 1 Adjustment = 0.90 #If shallow or stony soil. # If lateral groundwater feeding is not longer periods, reclass soil depth to rather shallow. # Otherwise, set texture to gravel (2) if not peat: if New_SoilDepth >= shallow or New_SoilTexture == 1: if New_LateralWater < 3: New_SoilDepth = rshallow if New_LateralWater == 3: New_SoilTexture = 2 #if wet with no lateral water movement.. if New_SoilMoisture == 5 and New_LateralWater == 1: New_GroundLayer = 5 #groundlayer will be swamp-moss. Adjustment *= 0.70 #reduce SI by 30%. New_LateralWater = 3 #lateralwater movement is longer periods. # if wet with lateral water movement. if New_SoilMoisture == 5 and New_LateralWater == 3: New_GroundLayer = 5 #groundlayer will be swamp moss. New_LateralWater = 3 #this is superfluous. #if wet class soil moisture to moist... if New_SoilMoisture == 5: New_SoilMoisture = 4 #SoilTextureIndex = 2.265 - 0.204 * soilTexture + 2.94 / soilTexture #Set to 2.2 if New_SoilTexture not found. SoilTextureIndex = { 0: 5.0, 1: 5.0, 2: 3.5, 3: 2.7, 4: 2.2, 5: 1.8, 6: 1.4, 7: 1.2, 8: 1.0, 9: 2.0 }.get(New_SoilTexture, 2.2) if peat: FTALL = 5.03077 - 0.03627 * (latitude - 60.0) if nfi_adjustments: #Always apply close to coast and norrland for same as NFI results. HL1979 removed coast limitation for altitude expression for MINERAL soils. FTALL -= 0.000000901 * (altitude ** 2) else: FTALL -= 0.000000901 * (altitude ** 2) * ~coast * limes_norrlandicus #Ordinary routine, i.e. no effect for areas close to coast north of limes norrlandicus. if New_LateralWater == 3: FTALL += 0.09195 if ditched: FTALL += 0.13622 if 2 <= New_GroundLayer <= 5: FTALL -= 0.1497 if New_Vegetation < 7: FTALL += 0.22273 if New_Vegetation == 13: FTALL += 0.13997 if New_Vegetation in [7, 8, 9, 14]: FTALL += 0.13377 G1 = 0 G2 = 0 else: FTALL = None G1 = 0 G2 = 0 if New_SoilMoisture == 2 and New_Vegetation > 9 and New_GroundLayer > 3: FTALL = (5.30943 - 0.01716 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.0039 * (latitude - 60.0 - abs(latitude - 60.0)) - 0.000000678 * (altitude ** 2) - 0.01243 * (SoilTextureIndex ** 2)) if New_LateralWater== 3: FTALL += 0.0488 if New_Vegetation == 13: FTALL += 0.09429 if New_Vegetation == 14: FTALL += 0.06167 if New_SoilDepth == deepsoil: FTALL += 0.1158 if New_Vegetation == 16: FTALL -= 0.07775 G1 = 1 if New_Vegetation in [13, 14] else 0 G2 = 1 if New_Vegetation in [15, 16] else 0 elif New_SoilMoisture == 2 and New_Vegetation > 9 and New_GroundLayer <= 3: FTALL = (5.21803 - 0.01193 * (latitude - 60.0 + abs(latitude - 60.0))) if New_LateralWater in [1, 2]: FTALL -= 0.000000593 * (altitude ** 2) if New_LateralWater == 3: FTALL -= 0.000000355 * (altitude ** 2) if New_SoilDepth == deepsoil: FTALL += 0.12454 if incline_percent <= 10.0 and New_LateralWater == 1: FTALL -= 0.06329 if incline_percent > 10.0 and altitude >= 350 and (0 <= aspect <= 112 or aspect > 337): FTALL -= 0.07189 if New_GroundLayer == 1: FTALL -= 0.06842 G1 = 0 G2 = 1 #OK elif New_SoilMoisture == 2 and New_Vegetation <= 9: FTALL = (5.34912 - 0.02037 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.00481 * (altitude ** 2)/10000) if New_SoilDepth == deepsoil: FTALL += 0.11574 if climate_code == 'M2': if dlan is not None: if 14 <= dlan <= 31: FTALL -= 0.16403 else: FTALL -= 0.16403 if New_Vegetation in [4, 5, 8, 9]: FTALL += 0.08376 if New_Vegetation in [1, 2]: FTALL += 0.12296 G1 = 1 if 1 <= New_Vegetation <= 9 else 0 #Can be just <= 9 ? G2 = 0 #TODO : Double check. elif New_SoilMoisture == 1: FTALL = (5.44789 - 0.01566 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.00000102 * (altitude ** 2) - 0.0417 * SoilTextureIndex) if New_SoilDepth == deepsoil: FTALL += 0.09162 if climate_code == 'K3': #Should be K2 according to Elfving notes? But HL1977, hand-written code write-up (Elfving?), HL1979 all have K3. FTALL += 0.12 if New_GroundLayer == 1: #If veg == 18 ? FTALL -= 0.19805 if New_GroundLayer in [2, 3]: #If veg == 17? FTALL -= 0.1381 if New_GroundLayer >= 4 and New_Vegetation in [1, 2, 3, 4, 5, 6, 8]: #GroundLayer requirement necessary? FTALL += 0.0953 if New_GroundLayer >= 4 and New_Vegetation in [7, 9, 13]: #Groundlayer requirement? FTALL += 0.0488 G1 = 1 if New_GroundLayer >= 4 and New_Vegetation in [1, 2, 3, 4, 5, 6, 7, 8, 9, 13] else 0 #Groundlayer requirement? G2 = 1 if New_GroundLayer in [1, 2, 3] else (1 if New_GroundLayer >= 4 and New_Vegetation >= 14 else 0) #Groundlayer requirement? #OK. elif New_SoilMoisture in [3, 4, 5]: #Pine on 5? Only 3, 4? FTALL = (5.46782 - 0.02013 * (latitude - 60.0 + abs(latitude - 60.0)) - 0.01074 * (SoilTextureIndex ** 2)) if New_LateralWater in [1, 2]: FTALL -= 0.01517 * 0.0001 * (altitude ** 2) if New_LateralWater == 3: FTALL -= 0.00747 * 0.0001 * (altitude ** 2) if New_Vegetation <= 6: FTALL += 0.11585 if New_Vegetation in [10, 11, 12]: FTALL -= 0.22358 if New_Vegetation in [7, 8, 9, 13, 14]: FTALL += 0.0770 if New_Vegetation in [15, 16]: FTALL -= 0.0726 if New_GroundLayer in [2, 3, 4, 5]: FTALL -= 0.0730 G1 = 1 if New_Vegetation in [1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14] else 0 G2 = 1 if New_Vegetation in [10, 11, 12, 15, 16] else 0 else: raise ValueError('No SIS method found.') # Final adjustments D = altitude + 130.0 * latitude - 8900.0 if G1 == 1 and D < -60.0: D = -60.0 if G2 == 1 and D >= 60.0: D = 60.0 if G1 == 1 and D < 0.0: Adjustment *= (1.0 - 0.00083333333 * D) if G2 == 1 and D > 0.0: Adjustment *= (1.0 - 0.00166666667 * D) if gotland: Adjustment *= 0.80 #0.1 converts dm to m. SIS = 0.1 * exp(FTALL) * Adjustment if SIS < 0.0: raise ValueError('SIS estimated < 0.') if SIS > 50.0: raise ValueError('SIS estimated > 50') return SIS
[docs] def Hagglund_Lundmark_1979_SIS( latitude : float, altitude : float, soil_moisture : int, ground_layer : int, vegetation : int, soil_texture : int, climate_code : Union[float,str], lateral_water : int, soil_depth: int, incline_percent: float, aspect : float, ditched : bool = False, peat : bool=False, gotland : bool = False, coast: bool = False, limes_norrlandicus : bool = False, nfi_adjustments : bool = True, species : str = 'Picea abies', dlan: int = None ) -> float: """ Estimate Site Index H100 for Scots Pine or Norway Spruce in Sweden by stand factors. Source: Corrected functions from Appendix II for estimation of site index by site factors according to Hägglund and Lundmark (1977), in Hägglund, B. (1979) Ett system för bonitering av skogsmark - analys, kontroll och diskussion inför praktisk tillämpning. Rapport 14, Projekt HUGIN. Skogsvetenskapliga fakulteten, Sveriges Lantbruksuniversitet. Umeå. Sweden. Hägglund, B., Lundmark, J-E. (1977) Skattning av höjdboniteten med ståndortsfaktorer: Tall och gran i Sverige. Research Notes nr. 28. Dept. Forest Ecology & Forest Soils. Royal College of Forestry. Stockholm. Sweden. Details: Uses corrected functions from appendix II in Hägglund & Lundmark 1979, except for when used on peat soils, in which case functions f. 8.1 & f. 8.2. pages 138-139 from Hägglund & Lundmark (1977) is used. Parameters: species (str): One of "Picea abies" (default) or "Pinus sylvestris". vegetation (int): Integer 1-18 ground_layer (int): Integer 1-6 latitude (float): Decimal WGS84. altitude (float): Meters above sea level. aspect (int): Main aspect of the site. int 0-360. incline_percent (float): Incline of slope, in percent. Default == 0. soil_moisture (int): Integer 1-5. 1: Dry, 2: Mesic, 3: Mesic-Moist, 4: Moist, 5: Wet soil_texture (int): Integer 1-9. see [forester::Sweden_soil_types(type="texture")] soil_depth (int): Integer 1-5. 1: Deep >70 cm, 2: Rather shallow 20-70 cm, 3: Shallow < 20 cm, 4: Varying. 5: New nfi designation 'häll', en: 'outcrop'. lateral_water (int): Integer 1-3. 1: Seldom/never, 2: Shorter periods, 3: Longer periods. ditched (bool): FALSE (default) if plot affected by ditching. climate_code (str): One of: NA (default),"M1","M2","K1","K2","K3". coast (bool): FALSE (default) if plot is closer to coast than 50km. limes_norrlandicus (bool): FALSE (default) if plot is north of Sernanders Limes Norrlandicus (used for Peat soils only.) gotland (bool): FALSE (default) if plot is located in Gotland. nfi_adjustments (bool): TRUE (default) if adjustments based on NFI should be applied. N.B. This applies coastal correction n. of Limes norrlandicus always. dlan (int): Optional. None (default). 1-31 according to NFI ID. Returns: float: Site Index H100 (meters). Will return NA & issue a warning message if no method was found. """ if not species in ['Picea abies','Pinus sylvestris']: raise ValueError(f"species must be one of : 'Picea abies' or 'Pinus sylvestris', not '{species}'") if latitude < 55.2 or latitude > 69.1 or latitude is None: print('latitude must be between (55.2, 69.1)') return np.nan if altitude < 0 or altitude > 2117 or altitude is None: print('altitude must be between (0,2117)') return np.nan if soil_moisture not in range(1, 6) or soil_moisture is None: print('soil_moisture must be int 1 - 5') return np.nan if ground_layer not in range(1, 7) or ground_layer is None: print('ground_layer must be int 1-6') return np.nan if vegetation not in range(1, 19) or vegetation is None: print('Vegetation must be int 1-18') return np.nan if type(peat)!=bool and type(peat)!=np.bool_: print('Peat must be a bool: True or False') return np.nan if not peat: if soil_texture not in range(0, 10) or soil_texture is None: print('soil_texture must be int 0-10') return np.nan if pdisna(soil_depth) and not peat: print('soil_depth must be one of float: NA; int 1-5. If soil_depth is NA peat must be True.') return np.nan if not peat: if soil_depth not in range(0,6) or soil_depth is None : print('soil_depth must be one of float: NA; int 1-5. If soil_depth is NA peat must be True.') return np.nan if dlan is not None: if dlan<1 or dlan>31: print('dlan must be one of int: 1-31 or NA.') return(np.nan) if climate_code not in ['M1','M2','M3','K1','K2','K3'] and not pdisna(climate_code): print("climate_code must be one of float: NA; str: M1, M2, M3, K1, K2, K3") return np.nan if lateral_water not in range(1, 4) or lateral_water is None: print("lateral_water must be int 1-3") return np.nan if incline_percent < 0.0 or incline_percent > 100.0: print('incline_percent must be float 0-100') return np.nan if aspect < 0 or aspect > 360: print('Aspect must be float 0-360') return np.nan if type(gotland)!=bool and type(gotland)!=np.bool_: print('Gotland must be a bool: True or False') return np.nan if type(nfi_adjustments)!= bool and type(nfi_adjustments)!=np.bool_: print('nfi_adjustments must be a bool: True or False') if type(coast)!=bool and type(coast)!=np.bool_: print('coast must be a bool: True or False') if type(limes_norrlandicus)!=bool and type(limes_norrlandicus)!=np.bool_: print('limes_norrlandicus must be a bool: True or False') if nfi_adjustments: limes_norrlandicus = True coast = True args = { 'vegetation' : vegetation, 'ground_layer' : ground_layer, 'latitude' : latitude, 'altitude' : altitude, 'aspect' : aspect, 'incline_percent' : incline_percent, 'soil_moisture' : soil_moisture, 'soil_texture' : soil_texture, 'soil_depth' : soil_depth, 'peat' : peat, 'lateral_water' : lateral_water, 'ditched' : ditched, 'climate_code' : climate_code, 'coast' : coast, 'limes_norrlandicus' : limes_norrlandicus, 'gotland' : gotland, 'nfi_adjustments' : nfi_adjustments, 'dlan': dlan } if species == 'Picea abies': SIS = NFI_SIS_SPRUCE(**args) else: SIS = NFI_SIS_PINE(**args) return SIS