//--------------------------------------------------------------------- /// <summary> /// Computes the change in a cohort's biomass due to Annual Net Primary /// Productivity (ANPP), age-related mortality (M_AGE), and development- /// related mortality (M_BIO). /// </summary> public int ComputeChange(ICohort cohort, ActiveSite site) { ecoregion = PlugIn.ModelCore.Ecoregion[site]; int siteBiomass = SiteVars.TotalBiomass[site]; //Save the pre-growth root biomass. This needs to be calculated BEFORE growth and mortality double TotalRoots = Roots.CalculateRootBiomass(site, cohort.Species, cohort.Biomass); SiteVars.soilClass[site].CollectRootBiomass(TotalRoots, 0); // First, calculate age-related mortality. // Age-related mortality will include woody and standing leaf biomass (=0 for deciduous trees). double mortalityAge = ComputeAgeMortality(cohort); double actualANPP = ComputeActualANPP(cohort, site, siteBiomass, SiteVars.PreviousYearMortality[site]); // Age mortality is discounted from ANPP to prevent the over- // estimation of mortality. ANPP cannot be negative. actualANPP = Math.Max(1, actualANPP - mortalityAge); // Growth-related mortality double mortalityGrowth = ComputeGrowthMortality(cohort, site, siteBiomass); // Age-related mortality is discounted from growth-related // mortality to prevent the under-estimation of mortality. Cannot be negative. mortalityGrowth = Math.Max(0, mortalityGrowth - mortalityAge); // Also ensure that growth mortality does not exceed actualANPP. mortalityGrowth = Math.Min(mortalityGrowth, actualANPP); // Total mortality for the cohort double totalMortality = mortalityAge + mortalityGrowth; if (totalMortality > cohort.Biomass) { throw new ApplicationException("Error: Mortality exceeds cohort biomass"); } // Defoliation ranges from 1.0 (total) to none (0.0). defoliation = CohortDefoliation.Compute(cohort, site, siteBiomass); double defoliationLoss = 0.0; if (defoliation > 0) { double standing_nonwood = ComputeFractionANPPleaf(cohort.Species) * actualANPP; defoliationLoss = standing_nonwood * defoliation; SiteVars.soilClass[site].DisturbanceImpactsDOM(site, "defol", 0); //just soil impacts. Dist impacts are handled differently?? } int deltaBiomass = (int)(actualANPP - totalMortality - defoliationLoss); double newBiomass = cohort.Biomass + (double)deltaBiomass; double totalLitter = UpdateDeadBiomass(cohort, actualANPP, totalMortality, site, newBiomass); //if (site.Location.Row == 279 && site.Location.Column == 64 && cohort.Species.Name == "Pl") //{ // PlugIn.ModelCore.UI.WriteLine("Yr={0}, Age={1}, mortGrow={2}, mortAge={3}, ANPP={4}, totLitter={5}", PlugIn.ModelCore.CurrentTime, cohort.Age, mortalityGrowth, mortalityAge, actualANPP, totalLitter); // } //if (cohort.Species.Name == "pinubank") //{ //PlugIn.ModelCore.UI.WriteLine("Age={0}, ANPPact={1:0.0}, M={2:0.0}, litter={3:0.00}.", cohort.Age, actualANPP, totalMortality, totalLitter); //PlugIn.ModelCore.UI.WriteLine("Name={0}, Age={1}, B={2}, ANPPact={3:0.0}, delta={4:0.0}", cohort.Species.Name, cohort.Age, cohort.Biomass, actualANPP, deltaBiomass); // PlugIn.ModelCore.UI.WriteLine("Name={0}, Age={1}, B={2}, Mage={3:0.0}, Mgrowth={4:0.0}, ANPPact={5:0.0}, delta={6:0.0}, newbiomass={7:0.0}", cohort.Species.Name, cohort.Age, cohort.Biomass, mortalityAge, mortalityGrowth, actualANPP, deltaBiomass, newBiomass); //} //The KillNow flag indicates that this is the year of growth in which to kill off some cohorts in order to make snags. if (SiteVars.soilClass[site].bKillNow && Snags.bSnagsPresent) { //if (SiteVars.soilClass[site].diedAt == cohort.Age && SiteVars.soilClass[site].spIndex == cohort.Species.Index) //there could be more than one species-age combination, so we have to loop through them. //However, the user has been asked to put the ages in order from smallest to largest, so we can stop looking //as soon as we reach an age that is older than the cohort's age. for (int idx = 0; idx < Snags.NUMSNAGS; idx++) { if (cohort.Age == Snags.DiedAt[idx] && Snags.initSpecIdx[idx] == cohort.Species.Index) { deltaBiomass = -cohort.Biomass; //set biomass to 0 to make core remove this from the list //when this cohort gets passed to the cohort died event, there is no longer any biomass present, so //we have to capture the biomass information here, while we still can. double foliar = (double)cohort.ComputeNonWoodyBiomass(site); double wood = ((double)cohort.Biomass - foliar); Snags.bSnagsUsed[idx] = true; SiteVars.soilClass[site].CollectBiomassMortality(cohort.Species, cohort.Age, wood, foliar, 5 + idx); } if (Snags.DiedAt[idx] > cohort.Age || Snags.DiedAt[idx] == 0) { break; } } } if (deltaBiomass > -cohort.Biomass) { //if we didn't kill this cohort to make a snag, then update the post-growth root biomass. TotalRoots = Roots.CalculateRootBiomass(site, cohort.Species, newBiomass); SiteVars.soilClass[site].CollectRootBiomass(TotalRoots, 1); } return(deltaBiomass); }
//--------------------------------------------------------------------- private double UpdateDeadBiomass(ICohort cohort, double actualANPP, double totalMortality, ActiveSite site, double newBiomass) { ISpecies species = cohort.Species; double leafLongevity = SpeciesData.LeafLongevity[species]; double cohortBiomass = newBiomass; // Mortality is for the current year's biomass. double leafFraction = ComputeFractionANPPleaf(species); // First, deposit the a portion of the leaf mass directly onto the forest floor. // In this way, the actual amount of leaf biomass is added for the year. // In addition, add the equivalent portion of fine roots to the surface layer. // 0.8 was used to calibrate the model to steady-state Nitrogen. Without this reduction, total N // increases by 0.038% each year. // Remove jan 2020 to more closely match code with the current Biomass succession model //double annualLeafANPP = actualANPP * leafFraction * 0.8; double annualLeafANPP = actualANPP * leafFraction; // -------------------------------------------------------------------------------- // The next section allocates mortality from standing (wood and leaf) biomass, i.e., // biomass that has accrued from previous years' growth. // Subtract annual leaf growth as that was taken care of above. totalMortality -= annualLeafANPP; // Assume that standing foliage is equal to this years annualLeafANPP * leaf longevity // minus this years leaf ANPP. This assumes that actual ANPP has been relatively constant // over the past 2 or 3 years (if coniferous). double standing_nonwood = (annualLeafANPP * leafLongevity) - annualLeafANPP; double standing_wood = Math.Max(0, cohortBiomass - standing_nonwood); double fractionStandingNonwood = standing_nonwood / cohortBiomass; // Assume that the remaining mortality is divided proportionally // between the woody mass and non-woody mass (Niklaus & Enquist, // 2002). Do not include current years growth. double mortality_nonwood = Math.Max(0.0, totalMortality * fractionStandingNonwood); double mortality_wood = Math.Max(0.0, totalMortality - mortality_nonwood); if (mortality_wood < 0 || mortality_nonwood < 0) { throw new ApplicationException("Error: Woody input is < 0"); } // Total mortality not including annual leaf litter M_noLeafLitter = (int)mortality_wood; SiteVars.soilClass[site].CollectBiomassMortality(species, cohort.Age, mortality_wood, (mortality_nonwood + annualLeafANPP), 0); //add root biomass information - now calculated based on both woody and non-woody biomass Roots.CalculateRootTurnover(site, species, cohortBiomass); SiteVars.soilClass[site].CollectBiomassMortality(species, cohort.Age, Roots.CoarseRootTurnover, Roots.FineRootTurnover, 1); //if biomass is going down, then we need to capture a decrease in the roots as well. if (cohortBiomass < cohort.Biomass) { double preMortRoots = Roots.CalculateRootBiomass(site, species, cohort.Biomass); double preMortCoarse = Roots.CoarseRoot; double preMortFine = Roots.FineRoot; double TotRoots = Roots.CalculateRootBiomass(site, species, cohortBiomass); if (preMortRoots > TotRoots) //if the root biomass went down, then we need to allocate that difference. { //We will allocate the total root decline to the different pools based on the relative proportions //prior to the decline. (Note that we are not calculating actual declines for each type because //sometimes if we are changing calculation methods, we may change the allocation and may cause a large //decrease in one pool and an increase in the other.) double diffFine = (preMortFine / preMortRoots) * (preMortRoots - TotRoots); double diffCoarse = (preMortRoots - TotRoots) - diffFine; SiteVars.soilClass[site].CollectBiomassMortality(species, cohort.Age, diffCoarse, diffFine, 1); //write a note to the file if the allocation changes unexpectedly, but not during spin-up if (((preMortCoarse - Roots.CoarseRoot) < 0 || (preMortFine - Roots.FineRoot) < 0) && PlugIn.ModelCore.CurrentTime > 0) { string strCombo = "from: " + preMortCoarse; strCombo += " to: " + Roots.CoarseRoot; string strCombo2 = "from: " + preMortFine; strCombo2 += " to: " + Roots.FineRoot; PlugIn.ModelCore.UI.WriteLine("Root Dynamics: Overall root biomass declined but note change in coarse root allocation " + strCombo + " and fine root allocation" + strCombo2); } } else if (PlugIn.ModelCore.CurrentTime > 0) { //write a note to the file if the root biomass increases while abio decreases, but not during spin-up string strCombo = "from: " + cohort.Biomass; strCombo += " to: " + cohortBiomass; string strCombo2 = "from: " + preMortRoots; strCombo2 += " to: " + TotRoots; PlugIn.ModelCore.UI.WriteLine("Root Dynamics: Note that aboveground biomass decreased " + strCombo + " but root biomass increased " + strCombo2); } } if (PlugIn.ModelCore.CurrentTime == 0) { SiteVars.soilClass[site].CollectBiomassMortality(species, cohort.Age, standing_wood, standing_nonwood, 3); Roots.CalculateRootTurnover(site, species, (standing_wood + standing_nonwood)); SiteVars.soilClass[site].CollectBiomassMortality(species, cohort.Age, Roots.CoarseRootTurnover, Roots.FineRootTurnover, 4); } return(annualLeafANPP + mortality_nonwood + mortality_wood); }