!! Simulate vegetation dynamic !|author: Giovanni Ravazzani ! license: GPL ! !### History ! ! current version 1.0 - 31st October 2018 ! ! | version | date | comment | ! |----------|-------------|----------| ! | 1.0 | 31/Oct/2018 | Original code | ! !### License ! license: GNU GPL ! !### Module Description ! Module to model vegetation dynamic ! MODULE Plants ! ! Modules used: USE DataTypeSizes, ONLY : & ! Imported Type Definitions: short, float USE LogLib, ONLY: & !Imported routines: Catch USE GridLib, ONLY: & !imported definitions: grid_real, grid_integer, & !imported routines: NewGrid, GridDestroy, & !imported parameters: NET_CDF USE GridOperations, ONLY : & !imported routines: GridByIni, CellArea USE Chronos, ONLY: & !imported definitions: DateTime, & !Imported routines: DayOfYear, GetYear, & !Imported operations: OPERATOR (==), & OPERATOR ( > ) USE IniLib, ONLY : & !Imported derived types: IniList, & !Imported routines: IniOpen, IniClose , & IniReadInt , IniReadReal, & IniReadString, SectionIsPresent, & KeyIsPresent USE StringManipulation, ONLY : & !imported routines ToString USE PlantsAllometrics, ONLY : & !imported routines: CrownDiameter, CanopyCover USE PlantsModifiers, ONLY: & !imported routines: AGEmod, TEMPmod, SWCmod, VPDmod, CO2mod USE PlantsInterception, ONLY: & !imported variables interceptionParametersByMap, & laimax, canopymax USE PlantsManagement, ONLY: & !imported definition: Practice, & !imported variables: plants_management, & management_map, & !imported routines: SetPlantsManagement, & ApplyPlantsManagement, & SetPractice USE PlantsMortality, ONLY : & !imported routines: KillPlants USE Units, ONLY : & ! imported parameters: hectare, pi IMPLICIT NONE ! Global (i.e. public) Declarations: PUBLIC :: PlantsConfig PUBLIC :: PlantsGrow PUBLIC :: PlantsParameterUpdate INTEGER (KIND = short) :: dtPlants TYPE (grid_real) :: lai !!leaf area index (m2/m2) TYPE (grid_real) :: fvcover !!fraction of cell covered by vegetation TYPE (grid_real) :: rsMin !!minimum stomatal resistance TYPE (grid_real) :: gpp !! carbon gross primary production (t) TYPE (grid_real) :: npp !! carbon net primary production (t) TYPE (grid_real) :: carbonroot !! carbon mass of root (t) TYPE (grid_real) :: carbonstem !! carbon mass of stem (t) TYPE (grid_real) :: carbonleaf !! carbon mass of leaf (t) TYPE (grid_real) :: dbh !!diameter at brest height (cm) TYPE (grid_real) :: plantsHeight !! tree height (m) TYPE (grid_real) :: density !! Trees/hectare TYPE (grid_real) :: stemyield !! stem yield (t) LOGICAL :: rsMinLoaded = .FALSE. LOGICAL :: fvcoverLoaded = .FALSE. LOGICAL :: laiLoaded = .FALSE. LOGICAL :: plantsHeightLoaded = .FALSE. LOGICAL :: updatePlantsParameters = .FALSE. TYPE PlantsSpecies !Descriptor CHARACTER (LEN = 50) :: name !!species name, Fagus, Abies, ... CHARACTER (LEN = 1) :: phenology !! evergreen (E), deciduous (D) REAL (KIND = float) :: alpha !! canopy quantum efficiency (molC/molPAR) REAL (KIND = float) :: GPPtoNPP !!GPP/NPP ratio REAL (KIND = float) :: k !! extinction coefficient for absorption of PAR by canopy REAL (KIND = float) :: cra !! Chapman-Richards asymptotic maximum height REAL (KIND = float) :: crb !! Chapman-Richards exponential decay parameter REAL (KIND = float) :: crc !! Chapman-Richards shape parameter REAL (KIND = float) :: as !! scaling coefficient in stem mass v diameter relationship REAL (KIND = float) :: ns !! scaling exponent in stem mass v diameter relationship !REAL (KIND = float) :: saf !! stem allocation factor !REAL (KIND = float) :: raf !! root allocation factor !REAL (KIND = float) :: laf !! leaf (foliage) allocation factor REAL (KIND = float) :: dbhdcmin !! minimum ratio between stem and crown diameters (m/cm) REAL (KIND = float) :: dbhdcmax !! maximum ratio between stem and crown diameters (m/cm) REAL (KIND = float) :: denmin !! minimum tree density (trees/ha) REAL (KIND = float) :: denmax !! minimum tree density (trees/ha) REAL (KIND = float) :: agemax !! maximum age REAL (KIND = float) :: tmax !! maximum temperature for vegetation growing (°C) REAL (KIND = float) :: tmin !! minimum temperature for vegetation growing (°C) REAL (KIND = float) :: topt !! optimum temperature for vegetation growing (°C) REAL (KIND = float) :: theta_fswc !! parameter to compute soil water content modifier REAL (KIND = float) :: theta_fvpd !! parameter to compute vapor pressure deficit modifier REAL (KIND = float) :: fpra !! parameter 1 to compute allocation factors REAL (KIND = float) :: fprn !! parameter 2 to compute allocation factors REAL (KIND = float) :: spra !! parameter 3 to compute allocation factors REAL (KIND = float) :: sprn !! parameter 4 to compute allocation factors REAL (KIND = float) :: ltr !!leaf turnover rate [s-1] REAL (KIND = float) :: rtr !!root turnover rate [s-1] REAL (KIND = float) :: tcold_leaf !! temperature threshold that accelerates leaf turnover (°C) REAL (KIND = float) :: sla !! specific leaf area [m2/Kg] REAL (KIND = float) :: hdmin !! H/D ratio in carbon partitioning for low density REAL (KIND = float) :: hdmax !! H/D ratio in carbon partitioning for high density REAL (KIND = float) :: albedo !!plant albedo (0-1) REAL (KIND = float) :: laimax !!maximum leaf area index used for precipitation interception (m2/m2) REAL (KIND = float) :: canopymax !! maximum canopy storage capacity (m) REAL (KIND = float) :: wood_density !! wood density (kg/m3) REAL (KIND = float) :: ms !! Fractions of mean stem biomass pools per tree on each dying tree REAL (KIND = float) :: mr !! Fractions of mean root biomass pools per tree on each dying tree REAL (KIND = float) :: mf !! Fractions of mean leaf biomass pools per tree on each dying tree REAL (KIND = float) :: wSx1000 !! Fractions of mean leaf biomass pools per tree on each dying tree END TYPE PlantsSpecies TYPE PlantsCohort !group of plants of the same species that are born during the same year TYPE (PlantsSpecies) :: species !state variables REAL (KIND = float) :: age !!age [years] REAL (KIND = float) :: gpp !! gross primary production (t/ha) REAL (KIND = float) :: npp !! net primary production (t/ha) REAL (KIND = float) :: lai !! leaf area index [m2/m2] REAL (KIND = float) :: mass_root !! mass of root (t/ha) REAL (KIND = float) :: mass_stem !! mass of stem (t/ha) REAL (KIND = float) :: mass_stem_year_previous !! mass of stem of the previous year (t/ha) REAL (KIND = float) :: mass_leaf !! mass of leaf (t/ha) REAL (KIND = float) :: mass_total !! total biomass root + stem + foliage (t/ha) REAL (KIND = float) :: stem_yield !! stem mass produced by plants cut (t/ha) REAL (KIND = float) :: density !! number of plants per hectar REAL (KIND = float) :: height !! plants heigth [m] REAL (KIND = float) :: dbh !!diameter at brest heigth [cm] REAL (KIND = float) :: canopy_cover !! percentage of surface stand covered by canopy [0-1] REAL (KIND = float) :: crown_diameter !! diameter of crown [m] REAL (KIND = float) :: apar !! absorbed photosynthetically active radiation (molPAR m-2) TYPE(PlantsCohort), POINTER :: next =>null() ! pointer to the next cohort in the same stand END TYPE PlantsCohort TYPE PlantsStand ! list of cohorts TYPE (PlantsCohort), POINTER, PRIVATE :: first => null() !TYPE (PlantsCohort), POINTER, PRIVATE :: last => null() !TYPE (PlantsCohort), POINTER, PRIVATE :: iter => null() INTEGER (KIND = short) , PRIVATE :: lenght = 0 INTEGER (KIND = short) , PRIVATE :: i !row of the stand in grid plants_mask INTEGER (KIND = short) , PRIVATE :: j !column of the stand in grid plants_mask TYPE (Practice) :: thinning END TYPE PlantsStand !TYPE PlantsAge ! REAL (KIND = float) :: value !!age [yesrs] ! INTEGER (KIND = short) :: period !! 0 for adult tree, 1 for young tree ! INTEGER (KIND = short) :: species_count !! number of species in the cell ! TYPE (PlantsSpecies), ALLOCATABLE :: species (:) !END TYPE PlantsAge ! !TYPE PlantsHeight ! REAL (KIND = float) :: value !!value of height class [m] ! REAL (KIND = float) :: layer_coverage !! cell coverage of that layer ! INTEGER (KIND = short) :: dominance !! -1 no trees in veg period, 1 trees in veg period ! INTEGER (KIND = short) :: ages_count !! counter of age classes in the cell ! TYPE (PlantsAge), ALLOCATABLE :: ages (:) !END TYPE PlantsHeight !local (i.e. private) declarations: !variables and parameters LOGICAL, PRIVATE :: simulatePlants != 1 when plants dynamic is simulated, = 0 read vegetation parameters from files LOGICAL, PRIVATE :: useCO2modifier ! define wheter use the CO2 modifier LOGICAL, PRIVATE :: mortality ! wheter to apply mortality or not TYPE (grid_integer), PRIVATE :: plants_mask !!define cells active for plants dynamic simulation TYPE (PlantsStand), PRIVATE, ALLOCATABLE :: forest (:) !vector of stands INTEGER (KIND = short), PRIVATE :: count_stands !number of stands in the forest (equal to the number of cells in the domain) TYPE (PlantsSpecies), ALLOCATABLE, PRIVATE :: species (:) ! !REAL (KIND = float), PRIVATE :: rs_par !! fraction of shortwave radiation to compute PAR [0-1] INTEGER (KIND = short), PRIVATE :: year_prev, year_new REAL (KIND = float), PRIVATE, PARAMETER :: C_molar_mass = 0.0120107 ! kg/mol !routines PRIVATE :: AparCalc PRIVATE :: ReadSpecies PRIVATE :: SetIC PRIVATE :: GrowLeaf PRIVATE :: GrowDBH PRIVATE :: ForestToGrid !======= CONTAINS !======= !============================================================================== !| Description: ! Initialize Plants module SUBROUTINE PlantsConfig & ! (iniFile, mask, begin, end) IMPLICIT NONE ! Arguments with intent(in): CHARACTER (LEN = 300), INTENT(IN) :: iniFile !!configuration file TYPE (grid_integer), INTENT(IN) :: mask !!mask of simulation domain TYPE (DateTime), INTENT (IN) :: begin !!simulation starting date TYPE (DateTime), INTENT(IN) :: end !!simulation ending date !local declarations: TYPE(IniList) :: iniDB !!store configuration info INTEGER (KIND = short) :: i, j, k CHARACTER (LEN = 300) :: species_file CHARACTER (LEN = 300) :: ICfile CHARACTER (LEN = 300) :: PMfile REAL (KIND = float) :: scalar !------------end of declaration------------------------------------------------ CALL IniOpen (iniFile, iniDB) !need plants dynamic simulation? simulatePlants = IniReadInt ('plants-simulation', iniDB) IF ( simulatePlants == 1) THEN !plants dynamic is simulated: configure forest model !plants mask IF ( SectionIsPresent ( 'plants-mask', iniDB) ) THEN CALL GridByIni (iniDB, plants_mask, section = 'plants-mask') ELSE CALL Catch ('error', 'Plants', 'Mask missing in configuration file') END IF !CO2 modifier IF ( SectionIsPresent ( 'use-co2-modifier', iniDB) ) THEN IF ( IniReadInt ('use-co2-modifier', iniDB) == 1 ) THEN useCO2modifier = .TRUE. ELSE useCO2modifier = .FALSE. END IF ELSE !if key is not found, set it to default false useCO2modifier = .FALSE. END IF !mortality IF ( KeyIsPresent ( 'mortality', iniDB) ) THEN IF ( IniReadInt ('mortality', iniDB) == 1 ) THEN mortality = .TRUE. ELSE mortality = .FALSE. END IF ELSE !if mortality is not found, assume it is not considered mortality = .FALSE. END IF !load species species_file = IniReadString ('plants-species-file', iniDB) CALL ReadSpecies (species_file) !compute number of stands and allocate count_stands = 0 DO i = 1, plants_mask % idim DO j = 1, plants_mask % jdim IF ( plants_mask % mat (i,j) /= plants_mask % nodata ) THEN count_stands = count_stands + 1 END IF END DO END DO ALLOCATE ( forest ( count_stands) ) !initialize list k = 1 DO i = 1, plants_mask % idim DO j = 1, plants_mask % jdim IF ( plants_mask % mat (i,j) /= plants_mask % nodata ) THEN !initialize the first cohort in the stand ALLOCATE ( forest (k) % first) NULLIFY ( forest (k) % first % next) forest (k) % lenght = 1 !store position of stand forest (k) % i = i forest (k) % j = j k = k + 1 END IF END DO END DO !set initial condition ICfile = IniReadString ('plants-ic-file', iniDB) CALL SetIC (ICfile) !set plants management options IF (KeyIsPresent ('plants-management-file',iniDB) ) THEN plants_management = .TRUE. PMfile = IniReadString ('plants-management-file', iniDB) CALL SetPlantsManagement (PMfile, begin, end) !set management pratice for each stand DO k = 1, count_stands CALL SetPractice ( management_map % mat (forest (k) % i, forest (k) % j) , forest (k) % thinning ) END DO ELSE !management is turned off plants_management = .FALSE. END IF !set interception maps if required IF (interceptionParametersByMap == 0 ) THEN CALL Catch ('warning', 'Plants', 'interception parameters are set only where plants map is not nodata') !allocate maps CALL NewGrid (laimax, plants_mask) CALL NewGrid (canopymax, plants_mask) DO k = 1, count_stands i = forest (k) % i j = forest (k) % j laimax % mat (i,j) = forest (k) % first % species % laimax canopymax % mat (i,j) = forest (k) % first % species % canopymax END DO END IF !allocate grids for state variables CALL NewGrid (lai, plants_mask) CALL NewGrid (fvcover, plants_mask) CALL NewGrid (gpp, plants_mask) CALL NewGrid (npp, plants_mask) CALL NewGrid (carbonroot, plants_mask) CALL NewGrid (carbonstem, plants_mask) CALL NewGrid (carbonleaf, plants_mask) CALL NewGrid (dbh, plants_mask) CALL NewGrid (plantsHeight, plants_mask) CALL NewGrid (density, plants_mask) CALL NewGrid (stemyield, plants_mask) !fill in grids CALL ForestToGrid (lai, 'lai') CALL ForestToGrid (fvcover, 'fv') CALL ForestToGrid (gpp, 'gpp') CALL ForestToGrid (npp, 'npp') CALL ForestToGrid (carbonroot, 'root') CALL ForestToGrid (carbonstem, 'stem') CALL ForestToGrid (carbonleaf, 'leaf') CALL ForestToGrid (dbh, 'dbh') CALL ForestToGrid (plantsHeight, 'height') CALL ForestToGrid (density, 'density') CALL ForestToGrid (stemyield, 'stemyield') fvcoverLoaded = .TRUE. laiLoaded = .TRUE. plantsHeightLoaded = .TRUE. ELSE ! no plants dynamic simulation required: read parameters from files !leaf area index IF ( SectionIsPresent ( 'lai', iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, 'lai') ) THEN scalar = IniReadReal ('scalar', iniDB, 'lai') CALL NewGrid (lai, mask, scalar) ELSE !check if parameter may change in time IF (KeyIsPresent ('format', iniDB, 'lai') ) THEN IF ( IniReadString ('format', iniDB, 'lai') == 'net-cdf' ) THEN updatePlantsParameters = .TRUE. END IF END IF !read map CALL GridByIni (iniDB, lai, section = 'lai') END IF laiLoaded = .TRUE. ELSE CALL Catch ('warning', 'Plants', 'LAI missing in configuration file') END IF !fraction of vegetation coverage IF ( SectionIsPresent ( 'vegetation-fraction', iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, 'vegetation-fraction') ) THEN scalar = IniReadReal ('scalar', iniDB, 'vegetation-fraction') CALL NewGrid (fvcover, mask, scalar) ELSE !check if parameter may change in time IF (KeyIsPresent ('format', iniDB, 'vegetation-fraction') ) THEN IF ( IniReadString ('format', iniDB, 'vegetation-fraction') == 'net-cdf' ) THEN updatePlantsParameters = .TRUE. END IF END IF !read map CALL GridByIni (iniDB, fvcover, section = 'vegetation-fraction') END IF fvcoverLoaded = .TRUE. ELSE CALL Catch ('warning', 'Plants', 'vegetation-fraction is missing') END IF !plants height IF ( SectionIsPresent ( 'vegetation-height', iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, 'vegetation-height') ) THEN scalar = IniReadReal ('scalar', iniDB, 'vegetation-height') CALL NewGrid (plantsHeight, mask, scalar) ELSE !check if parameter may change in time IF (KeyIsPresent ('format', iniDB, 'vegetation-height') ) THEN IF ( IniReadString ('format', iniDB, 'vegetation-height') == 'net-cdf' ) THEN updatePlantsParameters = .TRUE. END IF END IF !read map CALL GridByIni (iniDB, plantsHeight, section = 'vegetation-height') END IF plantsHeightLoaded = .TRUE. ELSE CALL Catch ('warning', 'Plants', 'vegetation-height is missing') END IF END IF !minimum stomatal resistance. this is not computed by forest model and it may be used for computing PET IF ( SectionIsPresent ( 'min-stomatal-resistance', iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, 'min-stomatal-resistance') ) THEN scalar = IniReadReal ('scalar', iniDB, 'min-stomatal-resistance') CALL NewGrid (rsMin, mask, scalar) ELSE !check if parameter may change in time IF (KeyIsPresent ('format', iniDB, 'min-stomatal-resistance') ) THEN IF ( IniReadString ('format', iniDB, 'min-stomatal-resistance') == 'net-cdf' ) THEN updatePlantsParameters = .TRUE. END IF END IF !read map CALL GridByIni (iniDB, rsMin, section = 'min-stomatal-resistance') END IF rsMinLoaded = .TRUE. ELSE CALL Catch ('warning', 'Plants', 'min-stomatal-resistance is missing') END IF CALL IniClose (iniDB) RETURN END SUBROUTINE PlantsConfig !============================================================================== !| Description: ! update parameter map that change in time SUBROUTINE PlantsParameterUpdate & ! (time) IMPLICIT NONE !Arguments with intent(in): TYPE (DateTime), INTENT (IN) :: time !local declarations: CHARACTER (LEN = 300) :: filename CHARACTER (LEN = 300) :: varname !------------------------------end of declarations----------------------------- !leaf area index IF ( time == lai % next_time ) THEN !destroy current grid filename = lai % file_name varname = lai % var_name CALL GridDestroy (lai) !read new grid in netcdf file CALL NewGrid (lai, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) END IF !fraction of vegetation coverage IF ( time == fvcover % next_time ) THEN !destroy current grid filename = fvcover % file_name varname = fvcover % var_name CALL GridDestroy (fvcover) !read new grid in netcdf file CALL NewGrid (fvcover, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) END IF !plants height IF ( time == plantsHeight % next_time ) THEN !destroy current grid filename = plantsHeight % file_name varname = plantsHeight % var_name CALL GridDestroy (plantsHeight) !read new grid in netcdf file CALL NewGrid (plantsHeight, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) END IF !minimum stomatal resistance IF ( time == rsMin % next_time ) THEN !destroy current grid filename = rsMin % file_name varname = rsMin % var_name CALL GridDestroy (rsMin) !read new grid in netcdf file CALL NewGrid (rsMin, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) END IF RETURN END SUBROUTINE PlantsParameterUpdate !============================================================================== !| Description: ! Update plants state variables SUBROUTINE PlantsGrow & ! (time, radiation, temperature, swc, sfc, swp, rh, co2) IMPLICIT NONE !Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time !!current time TYPE (grid_real), INTENT(IN) :: radiation !!shortwave radiation [w/m2] TYPE (grid_real), INTENT(IN) :: temperature !!air temperature [°C] TYPE (grid_real), INTENT(IN) :: swc !! soil water content [m3/m3] TYPE (grid_real), INTENT(IN) :: sfc !! soil field capacity [m3/m3] TYPE (grid_real), INTENT(IN) :: swp !! soil wilting point [m3/m3] TYPE (grid_real), INTENT(IN) :: rh !! air relative humidity [0-1] REAL (KIND = float), OPTIONAL, INTENT(IN) :: co2 !! CO2 [ppm] !local declarations: CHARACTER (LEN = 300) :: filename, varname INTEGER (KIND = short) :: k, l TYPE (PlantsCohort), POINTER :: cohort REAL (KIND = float) :: fSWC !! GPP soil water content modifier REAL (KIND = float) :: fAGE !! GPP age modifier REAL (KIND = float) :: fTEMP !! GPP temperature modifier REAL (KIND = float) :: fVPD !! GPP vapor pressure deficit modififer REAL (KIND = float) :: fCO2 !! GPP CO2 modifier !REAL (KIND = float) :: deltaMroot !! root mass increment !REAL (KIND = float) :: deltaMstem !! stem mass increment REAL (KIND = float) :: rootaf !!root allocation factor REAL (KIND = float) :: stemaf !! stem allocation factor REAL (KIND = float) :: leafaf !! leaf allocation factor REAL (KIND = float) :: pfs !!used to compute stem allocation factor LOGICAL :: is_new_year !! flag to mark new year has begun TYPE (Practice) :: practice !------------end of declaration------------------------------------------------ ! need to update vegetation parameters from file? IF ( simulatePlants == 0) THEN !check leaf area index IF ( time == lai % next_time ) THEN !destroy current grid filename = lai % file_name varname = lai % var_name CALL GridDestroy (lai) !read grid in netcdf file CALL NewGrid (lai, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) END IF !check vegetation fraction IF ( time == fvcover % next_time ) THEN !destroy current grid filename = fvcover % file_name varname = fvcover % var_name CALL GridDestroy (fvcover) !read grid in netcdf file CALL NewGrid (fvcover, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) END IF !check plants height IF ( time == plantsHeight % next_time ) THEN !destroy current grid filename = plantsHeight % file_name varname = plantsHeight % var_name CALL GridDestroy (plantsHeight) !read grid in netcdf file CALL NewGrid (plantsHeight, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) END IF !check minimum stomatal resistance IF ( rsMinLoaded .AND. time == rsMin % next_time ) THEN !destroy current grid filename = rsMin % file_name varname = rsMin % var_name CALL GridDestroy (rsMin) !read grid in netcdf file CALL NewGrid (rsMin, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) END IF ELSE !simulate forest dynamics year_new = GetYear (time) DO k = 1, count_stands cohort => forest (k) % first DO l = 1, forest (k) % lenght !loop through all cohorts cohort % stem_yield = 0. !check management practices IF ( plants_management ) THEN IF ( ALLOCATED (forest (k) % thinning % cuts) ) THEN practice = forest (k) % thinning IF (time > practice % next .AND. practice % current < SIZE ( practice % cuts )) THEN practice % current = practice % current + 1 IF ( practice % cuts ( practice % current ) % reforestation ) THEN !set species parameters cohort % species = species ( practice % cuts ( practice % current ) % species) END IF CALL ApplyPlantsManagement (time, practice, cohort % density, & cohort % mass_root, cohort % mass_stem, & cohort % mass_leaf, cohort % mass_total, & cohort % lai, cohort % canopy_cover, & cohort % age, cohort % height, cohort % dbh, & cohort % stem_yield ) !update crown diameter cohort % crown_diameter = CrownDiameter ( dbh = cohort % dbh, & den = cohort % density, & denmin = cohort % species % denmin, & denmax = cohort % species % denmax, & dbhdcmin = cohort % species % dbhdcmin, & dbhdcmax = cohort % species % dbhdcmax ) !update canopy cover cohort % canopy_cover = CanopyCover ( cohort % crown_diameter, cohort % density ) !update time of next cut IF ( practice % current < SIZE ( practice % cuts ) ) THEN practice % next = practice % cuts ( practice % current + 1 ) % time END IF !copy practice back forest (k) % thinning = practice END IF END IF END IF !update age IF ( DayOfYear (time) == 1 .AND. year_new /= year_prev ) THEN is_new_year = .TRUE. cohort % age = cohort % age + 1 cohort % mass_stem_year_previous = cohort % mass_stem ELSE is_new_year = .FALSE. END IF !compute APAR cohort % apar = AparCalc (rad = radiation % mat (forest (k) % i, forest (k) % j) , & lai = cohort % lai, & k = cohort % species % k, & alb = cohort % species % albedo, & dt = dtPlants) !compute CO2 modifier IF ( useCO2modifier ) THEN fCO2 = CO2mod (co2, cohort % age ) ELSE fCO2 = 1. END IF !compute age modifier fAGE = AGEmod ( cohort % age, cohort % species % agemax) !compute temperature modifier fTEMP = TEMPmod (Ta = temperature % mat (forest (k) % i, forest (k) % j), & Tmin = cohort % species % Tmin, & Tmax = cohort % species % Tmax, & Topt = cohort % species % Topt ) !compute soil water content modifier fSWC = SWCmod (swc = swc % mat (forest (k) % i, forest (k) % j), & wp = swp % mat (forest (k) % i, forest (k) % j), & fc = sfc % mat (forest (k) % i, forest (k) % j), & theta = cohort % species % theta_fswc ) !compute vapor pressure deficit modifier fVPD = VPDmod (Ta = temperature % mat (forest (k) % i, forest (k) % j), & RH = rh % mat (forest (k) % i, forest (k) % j), & kd = cohort % species % theta_fvpd) !compute gross primary production cohort % gpp = cohort % apar * & ! absorbed photosynthetically active radiation molPAR m-2 cohort % species % alpha * &! canopy quantum efficiency (molC/molPAR) C_molar_mass * & !conversion to Kg hectare / & ! m2 in one hectare 1000. * & !conversion to t fAGE * & !age modifier fTEMP * & !temperature modifier fSWC * & !soil water content modifier fVPD * &!vapor pressure deficit modifier fCO2 !compute net primary production cohort % npp = cohort % gpp * cohort % species % GPPtoNPP !compute root allocation factor rootaf = 0.5 / ( 1. + 2.5 * fAGE * fTEMP * fSWC ) !compute stem allocation factor pfs = (cohort % species % fprn * cohort % species % fpra) / & (cohort % species % sprn * cohort % species % spra) * & (cohort % density * cohort % dbh / 100.) ** & (cohort % species % fprn - cohort % species % sprn) stemaf = ( 1. - rootaf ) / ( 1. + pfs ) !compute leaf allocation factor leafaf = 1. - rootaf - stemaf ! update stem biomass cohort % mass_stem = cohort % mass_stem + stemaf * cohort % npp !update root biomass cohort % mass_root = cohort % mass_root + & !current mass rootaf * cohort % npp - & !mass increment cohort % species % rtr * cohort % mass_root * dtPlants !turnover rate ! update leaf biomass and leaf area index CALL GrowLeaf (npp = cohort % npp , & af = leafaf, & Ta = temperature % mat (forest (k) % i, forest (k) % j), & Tcold = cohort % species % tcold_leaf, & swc = swc % mat (forest (k) % i, forest (k) % j), & swp = swp % mat (forest (k) % i, forest (k) % j), & sfc = sfc % mat (forest (k) % i, forest (k) % j), & tr = cohort % species % ltr, & sla = cohort % species % sla, & mleaf = cohort % mass_leaf, & lai = cohort % lai ) !update total biomass cohort % mass_total = cohort % mass_root + cohort % mass_stem + cohort % mass_leaf !update dbh and height tree every new year !IF (is_new_year) THEN !CALL GrowDBH (cc = cohort % canopy_cover, & ! hdmin = cohort % species % hdmin, & ! hdmax = cohort % species % hdmax, & ! ws = cohort % mass_stem, & ! dws = cohort % mass_stem - cohort % mass_stem_year_previous, & ! !dws = stemaf * cohort % npp, & ! DBH = cohort % dbh, & ! height = cohort % height) CALL GrowDBHech2o (cc = cohort % canopy_cover, & hdmin = cohort % species % hdmin, & hdmax = cohort % species % hdmax, & ws = cohort % mass_stem, & !dws = cohort % mass_stem - cohort % mass_stem_year_previous, & dws = stemaf * cohort % npp, & DBH = cohort % dbh, & height = cohort % height, & tree_density = cohort % density, & wood_density = cohort % species % wood_density, & age = cohort % age, & maxage = cohort % species % agemax ) !END IF !update crown diameter cohort % crown_diameter = CrownDiameter ( dbh = cohort % dbh, den = cohort % density, & denmin = cohort % species % denmin, & denmax = cohort % species % denmax, & dbhdcmin = cohort % species % dbhdcmin, & dbhdcmax = cohort % species % dbhdcmax ) !update canopy cover cohort % canopy_cover = CanopyCover ( cohort % crown_diameter, cohort % density ) !apply mortality IF (mortality) THEN CALL KillPlants (dtPlants, cohort % age, cohort % species % agemax, & cohort % species % ms, cohort % species % mf, & cohort % species % mr, cohort % species % wSx1000, & cohort % crown_diameter, cohort % density, & cohort % canopy_cover, cohort % mass_stem, & cohort % mass_root, cohort % mass_leaf, & cohort % mass_total) END IF !jump to next cohort cohort => cohort % next END DO END DO year_prev = year_new !update state variable grids CALL ForestToGrid (lai, 'lai') CALL ForestToGrid (fvcover, 'fv') CALL ForestToGrid (gpp, 'gpp') CALL ForestToGrid (npp, 'npp') CALL ForestToGrid (carbonroot, 'root') CALL ForestToGrid (carbonstem, 'stem') CALL ForestToGrid (carbonleaf, 'leaf') CALL ForestToGrid (dbh, 'dbh') CALL ForestToGrid (plantsHeight, 'height') CALL ForestToGrid (density, 'density') CALL ForestToGrid (stemyield, 'stemyield') END IF RETURN END SUBROUTINE PlantsGrow !============================================================================== !| Description: ! read species parameters SUBROUTINE ReadSpecies & ! (inifile) IMPLICIT NONE CHARACTER (LEN = *), INTENT(in) :: inifile !local declarations: TYPE (IniList) :: speciesDB INTEGER (KIND = short) :: nspecies INTEGER (KIND = short) :: i !------------------------------end of declarations---------------------------- ! open and read configuration file CALL IniOpen (inifile, speciesDB) nspecies = IniReadInt ('species', speciesDB) ALLOCATE (species (nspecies) ) DO i = 1, nspecies species (i) % name = IniReadString ('name', speciesDB, section = ToString(i)) species (i) % phenology = IniReadString ('phenology', speciesDB, section = ToString(i)) species (i) % alpha = IniReadReal ('alpha', speciesDB, section = ToString(i)) species (i) % GPPtoNPP = IniReadReal ('GPP-NPP', speciesDB, section = ToString(i)) species (i) % k = IniReadReal ('k', speciesDB, section = ToString(i)) species (i) % cra = IniReadReal ('cra', speciesDB, section = ToString(i)) species (i) % crb = IniReadReal ('crb', speciesDB, section = ToString(i)) species (i) % crc = IniReadReal ('crc', speciesDB, section = ToString(i)) species (i) % as = IniReadReal ('as', speciesDB, section = ToString(i)) species (i) % ns = IniReadReal ('ns', speciesDB, section = ToString(i)) !species (i) % saf = IniReadReal ('stem-allocation-factor', speciesDB, section = ToString(i)) !species (i) % raf = IniReadReal ('root-allocation-factor', speciesDB, section = ToString(i)) !species (i) % laf = IniReadReal ('leaf-allocation-factor', speciesDB, section = ToString(i)) species (i) % dbhdcmin = IniReadReal ('dbhdcmin', speciesDB, section = ToString(i)) species (i) % dbhdcmax = IniReadReal ('dbhdcmax', speciesDB, section = ToString(i)) species (i) % denmin = IniReadReal ('denmin', speciesDB, section = ToString(i)) species (i) % denmax = IniReadReal ('denmax', speciesDB, section = ToString(i)) species (i) % agemax = IniReadReal ('agemax', speciesDB, section = ToString(i)) species (i) % tmin = IniReadReal ('tmin', speciesDB, section = ToString(i)) species (i) % tmax = IniReadReal ('tmax', speciesDB, section = ToString(i)) species (i) % topt = IniReadReal ('topt', speciesDB, section = ToString(i)) species (i) % theta_fswc = IniReadReal ('phi-theta', speciesDB, section = ToString(i)) species (i) % theta_fvpd = IniReadReal ('phi-ea', speciesDB, section = ToString(i)) species (i) % fpra = IniReadReal ('fpra', speciesDB, section = ToString(i)) species (i) % fprn = IniReadReal ('fprn', speciesDB, section = ToString(i)) species (i) % spra = IniReadReal ('spra', speciesDB, section = ToString(i)) species (i) % sprn = IniReadReal ('sprn', speciesDB, section = ToString(i)) species (i) % ltr = IniReadReal ('leaf-turnover', speciesDB, section = ToString(i)) species (i) % rtr = IniReadReal ('root-turnover', speciesDB, section = ToString(i)) species (i) % tcold_leaf = IniReadReal ('tcold-leaf', speciesDB, section = ToString(i)) species (i) % sla = IniReadReal ('sla', speciesDB, section = ToString(i)) species (i) % hdmin = IniReadReal ('hdmin', speciesDB, section = ToString(i)) species (i) % hdmax = IniReadReal ('hdmax', speciesDB, section = ToString(i)) species (i) % albedo = IniReadReal ('albedo', speciesDB, section = ToString(i)) species (i) % laimax = IniReadReal ('laimax', speciesDB, section = ToString(i)) species (i) % canopymax = IniReadReal ('canopymax', speciesDB, section = ToString(i)) species (i) % wood_density = IniReadReal ('wood-density', speciesDB, section = ToString(i)) species (i) % ms = IniReadReal ('ms', speciesDB, section = ToString(i)) species (i) % mr = IniReadReal ('mr', speciesDB, section = ToString(i)) species (i) % mf = IniReadReal ('mf', speciesDB, section = ToString(i)) species (i) % wSx1000 = IniReadReal ('wSx1000', speciesDB, section = ToString(i)) END DO ! species DB is complete. Deallocate ini database CALL IniClose (speciesDB) RETURN END SUBROUTINE ReadSpecies !============================================================================== !| Description: ! set initial condition SUBROUTINE SetIC & ! (inifile) IMPLICIT NONE CHARACTER (LEN = *), INTENT(in) :: inifile !local declarations: TYPE (IniList) :: icDB INTEGER (KIND = short) :: ncohorts TYPE (grid_integer) :: cohort_map INTEGER (KIND = short) :: i, j, k, l INTEGER (KIND = short) :: ispecies REAL (KIND = float) :: dbh TYPE (PlantsCohort), POINTER :: cohort !------------------------------end of declarations---------------------------- ! open and read configuration file CALL IniOpen (inifile, icDB) !number of cohorts ncohorts = IniReadInt ('cohorts', icDB) !starting year year_new = IniReadInt ('year', icDB) year_prev = IniReadInt ('year', icDB) !cohort map CALL GridByIni (icDB, cohort_map, section = 'cohorts-map') DO k = 1, count_stands !aggiungere configurazione ricorsiva quando ci sono più coorti per cella !al momento solo una coorte per stand (cella) cohort => forest (k) % first DO l = 1, forest (k) % lenght ispecies = IniReadInt ('species', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % species = species (ispecies) cohort % age = IniReadInt ('age', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % density = IniReadReal ('density', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % height = IniReadReal ('height', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % dbh = IniReadReal ('dbh', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % lai = IniReadReal ('lai', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % mass_stem = IniReadReal ('stem-biomass', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % mass_root = IniReadReal ('root-biomass', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % mass_leaf = IniReadReal ('leaf-biomass', icDB, section = ToString (cohort_map % mat (forest (k) % i, forest (k) % j ) ) ) cohort % mass_total = cohort % mass_stem + cohort % mass_root + cohort % mass_leaf cohort % crown_diameter = CrownDiameter ( dbh = cohort % dbh, den = cohort % density, & denmin = species (ispecies) % denmin, & denmax = species (ispecies) % denmax, & dbhdcmin = species (ispecies) % dbhdcmin, & dbhdcmax = species (ispecies) % dbhdcmax ) cohort % canopy_cover = CanopyCover ( cohort % crown_diameter, cohort % density ) cohort => cohort % next END DO END DO ! Deallocate memory CALL IniClose (icDB) RETURN END SUBROUTINE SetIC !============================================================================== !| Description: ! Computes the absorbed photosynthetically active radiation. ! The amount of light available for the plant that is going to define ! its growth rate. The light that the plant could absorb is determined according ! to the canopy cover. The amount of absorbed photosynthetically active radiation ! is usually computed following Lambert-Beer law . ! ! References: ! ! McCree, K.J.: Test of current definitions of photosynthetically active ! radiation against leaf photosynthesis data. Agr. Forest. Meteorol, ! 10 , 443-453, 1972. FUNCTION AparCalc & ! (rad, lai, k, alb, dt) & ! RESULT (apar) IMPLICIT NONE !arguments with intent(in): REAL (KIND = float), INTENT(IN) :: rad !! shortwave direct radiation (w/m2) REAL (KIND = float), INTENT(IN) :: lai !! leaf area index (m2/m2) REAL (KIND = float), INTENT(IN) :: k !! extinction coefficient for absorption of PAR by canopy REAL (KIND = float), INTENT(IN) :: alb !! plant albedo (0-1) INTEGER (KIND = short), INTENT(IN) :: dt !!time step (s) !local declarations: REAL (KIND = float) :: apar REAL (KIND = float) :: par0 !!photosynthetically active radiation (molPAR m-2) REAL (KIND = float), PARAMETER :: radToPAR = 4.57 !conversion factor 1 W m-2 = 4.57 µmol m-2 s-1 (McCree, 1972) !------------------------------end of declarations---------------------------- !devo considerare l'albedo cioè è una short wave net radiation ? o quella incidente ? par0 = rad * ( 1. - alb ) * radToPAR * dt ! (µmol m-2) apar = par0 * ( 1. - EXP ( -k * lai) ) / 1000000. ! (molPAR m-2) RETURN END FUNCTION AparCalc !============================================================================== !| Description: ! update leaf biomass and leaf area index ! ! References: ! !Maneta, M. P., and N. L. Silverman, 2013: A spatially distributed model to ! simulate water, energy, and vegetation dynamics using information from ! regional climate models. Earth Interact., 17 ! doi:10.1175/2012EI000472.1. ! ! Arora, V. K., and G. J. Boer, 2005: A parameterization of leaf phenology ! for the terrestrial ecosystem component of climate models. ! Global Change Biol., 11, 39–59. ! SUBROUTINE GrowLeaf & ! (npp, af, Ta, Tcold, swc, swp, sfc, tr, sla, mleaf, lai) IMPLICIT NONE !arguments with intent(in): REAL (KIND = float), INTENT(IN) :: npp !!net primary production [t/ha] REAL (KIND = float), INTENT(IN) :: af !!allocation factor [0-1] REAL (KIND = float), INTENT(IN) :: Ta !! air temperature [°C] REAL (KIND = float), INTENT(IN) :: Tcold !! temperature threshold that accelerates leaf turnover (°C) REAL (KIND = float), INTENT(IN) :: swc !! soil water content [m3/m3] REAL (KIND = float), INTENT(IN) :: swp !! soil wilting point [m3/m3] REAL (KIND = float), INTENT(IN) :: sfc !! soil field capacity [m3/m3] REAL (KIND = float), INTENT(IN) :: tr !! leaf turnover rate [s-1] REAL (KIND = float), INTENT(IN) :: sla !! specific leaf area [m2/Kg] !arguments with intent(inout): REAL (KIND = float), INTENT(INOUT) :: mleaf !!mass of leaf [t/ha] REAL (KIND = float), INTENT(INOUT) :: lai !! leaf area index [m2/m2] !local declarations: REAL (KIND = float) :: deltaMleaf !! leaf mass increment REAL (KIND = float) :: swc_stress !! soil water content stress that affects leaf increment [0-1] REAL (KIND = float) :: temp_stress !! temperature stress that affects leaf increment [0-1] REAL (KIND = float) :: actual_tr !! actual leaf tirnover rate with temperature and soil water content stresses !------------------------------end of declarations---------------------------- !compute temperature stress IF (Ta >= Tcold) THEN temp_stress = 1. ELSE IF ((Tcold - 5.) < Ta < Tcold) THEN temp_stress = (Ta - Tcold - 5.) / 5. ELSE IF (Ta <= (Tcold - 5.)) THEN temp_stress = 0. END IF !temp_stress = MAX (0., MIN (1., ((Ta - (Tcold - 5.) / 5. ) ) )) !compute hydrologic stress swc_stress = MAX (0., MIN (1., (swc - swp) / (sfc - swp) ) ) !compute actual leaf turnover rate: base turnover + 0.001 hydrologic stress + temperature stress actual_tr = tr * ( 1. + 0.001 * (1. - swc_stress)**3. + (1. - temp_stress)**3. ) !leaf mass increment deltaMleaf = af * npp !update leaf biomass mleaf = mleaf + deltaMleaf !update leaf area index lai = lai + (deltaMleaf * sla/ 10.) - (actual_tr * dtPlants) *LAI !10 = conversion factor 1000 [kg/t] / 10000 [m2/hectare] !check lower boundary IF (mleaf < 0.) THEN mleaf = 0. END IF IF (lai < 0.) THEN lai = 0. END IF RETURN END SUBROUTINE GrowLeaf !============================================================================== !| Description: ! update DBH and height tree according to ech2o model by Maneta ! SUBROUTINE GrowDBHech2o & ! (cc, hdmin, hdmax, ws, dws, DBH, height, tree_density, wood_density, age, maxage) IMPLICIT NONE !arguments with intent(in): REAL (KIND = float), INTENT(IN) :: cc !! canopy cover REAL (KIND = float), INTENT(IN) :: hdmin !! H/D ratio in carbon partitioning for low density REAL (KIND = float), INTENT(IN) :: hdmax !! H/D ratio in carbon partitioning for high density REAL (KIND = float), INTENT(IN) :: ws !! stem biomass (t/ha) REAL (KIND = float), INTENT(IN) :: dws !! stem biomass increment (t/ha) REAL (KIND = float), INTENT(IN) :: tree_density !! tree density (trees/ha) REAL (KIND = float), INTENT(IN) :: wood_density !! wood density (kg/m3) REAL (KIND = float), INTENT(IN) :: age !! tree age (year) REAL (KIND = float), INTENT(IN) :: maxage !!tree max age (year) !arguments with intent inout REAL (KIND = float), INTENT(INOUT) :: DBH !diameter at brest height (cm) REAL (KIND = float), INTENT(INOUT) :: height !tree height (m) !local declarations REAL (KIND = float) :: delta_tree_mass !!tree mass increment (kg/tree) REAL (KIND = float) :: fhd !! grow factor that depends on the height-to-diameter ratio REAL (KIND = float) :: dDBH !DBH increment (cm) REAL (KIND = float) :: dheight !height increment (m) !-------------------------------end of declarations---------------------------- !compute mass increment per tree delta_tree_mass = dws / tree_density * 1000. ! mass increment per tree (kg) !set grow factor IF (height / DBH >= hdmin .AND. cc < 0.95) THEN fhd = hdmin ELSE IF (height / DBH <= hdmax .AND. cc >= 0.95 .AND. age < maxage / 2) THEN fhd = hdmin ELSE IF (height / DBH <= hdmax .AND. cc >= 0.95 .AND. age > maxage / 2 ) THEN fhd = hdmax ELSE IF (height / DBH < hdmin) THEN fhd = hdmax ELSE IF (height / DBH > hdmax) THEN fhd = 0.5 * hdmin ELSE IF ( age > 0.7 * maxage) THEN fhd = 0. END IF !compute DBH increment dDBH = 4. * delta_tree_mass / ( wood_density * pi * (DBH / 100.)**2. * ( 2. * height / (DBH / 100.) + fhd * 100. ) ) * 100. !compute tree height increment dheight = dDBH * fhd !update dbh and height tree DBH = DBH + dDBH height = height + dheight !write(*,*) dDBH, dheight !pause RETURN END SUBROUTINE GrowDBHech2o !============================================================================== !| Description: ! update DBH and height tree every new year ! SUBROUTINE GrowDBH & ! (cc, hdmin, hdmax, ws, dws, DBH, height) IMPLICIT NONE !arguments with intent(in): REAL (KIND = float), INTENT(IN) :: cc !! canopy cover REAL (KIND = float), INTENT(IN) :: hdmin !! H/D ratio in carbon partitioning for low density REAL (KIND = float), INTENT(IN) :: hdmax !! H/D ratio in carbon partitioning for high density REAL (KIND = float), INTENT(IN) :: ws !! stem biomass (t/ha) REAL (KIND = float), INTENT(IN) :: dws !! stem biomass increment (t/ha) !arguments with intent inout REAL (KIND = float), INTENT(INOUT) :: DBH !diameter at brest height (cm) REAL (KIND = float), INTENT(INOUT) :: height !tree height (m) !local declarations REAL (KIND = float) :: hdeff REAL (KIND = float) :: dDBH !DBH increment (cm) REAL (KIND = float) :: dheight !height increment (m) !-----------------------------end of declarations------------------------------ IF ( cc <= 0.95) THEN !low density hdeff = hdmin + (hdmax - hdmin) * cc ELSE ! high density hdeff = hdmax END IF !compute height increment !dheight = hdeff / ( hdeff / height + 200. / dbh ) * dws / ws dheight = hdeff / ( hdeff / height) + (200. / dbh ) * dws / ws !compute DBH increment dDBH = ( height / hdeff ) + ( 200. / dbh ) * ( dws / ws ) !update dbh and height tree DBH = DBH + dDBH height = height + dheight !write(*,*) dheight, hdeff, dws, ws !pause RETURN END SUBROUTINE GrowDBH !============================================================================== !| Description: ! fill in a grid with state variable values in forest stands ! SUBROUTINE ForestToGrid & ! (grid, statevar) IMPLICIT NONE !arguments with intent(in): CHARACTER (LEN = *), INTENT(IN) :: statevar !arguments with intent inout TYPE (grid_real), INTENT (INOUT) :: grid !local declarations INTEGER (KIND = short) :: i, j, k !-----------------------------end of declarations------------------------------ DO k = 1, count_stands i = forest (k) % i j = forest (k) % j SELECT CASE (statevar) CASE ('lai') !leaf area index grid % mat (i,j) = forest (k) % first % lai CASE ('fv') !canopy cover grid % mat (i,j) = forest (k) % first % canopy_cover CASE ('gpp') !GPP grid % mat (i,j) = forest (k) % first % gpp * & CellArea (grid,i,j) / hectare CASE ('npp') !NPP grid % mat (i,j) = forest (k) % first % npp * & CellArea (grid,i,j) / hectare CASE ('root') !root mass grid % mat (i,j) = forest (k) % first % mass_root * & CellArea (grid,i,j) / hectare CASE ('stem') !stem mass grid % mat (i,j) = forest (k) % first % mass_stem * & CellArea (grid,i,j) / hectare CASE ('leaf') !foliage mass grid % mat (i,j) = forest (k) % first % mass_leaf * & CellArea (grid,i,j) / hectare CASE ('stemyield') !stem mass grid % mat (i,j) = forest (k) % first % stem_yield * & CellArea (grid,i,j) / hectare CASE ('dbh') !diameter at brest heigth (cm) grid % mat (i,j) = forest (k) % first % dbh CASE ('height') !tree height (m) grid % mat (i,j) = forest (k) % first % height CASE ('density') !tree density (trees/hectare) grid % mat (i,j) = forest (k) % first % density END SELECT END DO RETURN END SUBROUTINE ForestToGrid END MODULE Plants