/// <summary> /// Summarize cohort C&N for output. /// </summary> private static void CalculateCohortCN(ActiveSite site, ICohort cohort) { ISpecies species = cohort.Species; double leafC = cohort.LeafBiomass * 0.47; double woodC = cohort.WoodBiomass * 0.47; double fRootC = Roots.CalculateFineRoot(cohort, leafC); double cRootC = Roots.CalculateCoarseRoot(cohort, woodC); double totalC = leafC + woodC + fRootC + cRootC; double leafN = leafC / (double)SpeciesData.LeafCN[species]; double woodN = woodC / (double)SpeciesData.WoodCN[species]; double cRootN = cRootC / (double)SpeciesData.CoarseRootCN[species]; double fRootN = fRootC / (double)SpeciesData.FineRootCN[species]; //double totalN = woodN + cRootN + leafN + fRootN; //PlugIn.ModelCore.UI.WriteLine("month={0}, species={1}, leafB={2:0.0}, leafC={3:0.00}, leafN={4:0.0}, woodB={5:0.0}, woodC={6:0.000}, woodN={7:0.0}", Month, cohort.Species.Name, cohort.LeafBiomass, leafC, leafN, cohort.WoodBiomass, woodC, woodN); SiteVars.CohortLeafC[site] += leafC; SiteVars.CohortFRootC[site] += fRootC; SiteVars.CohortLeafN[site] += leafN; SiteVars.CohortFRootN[site] += fRootN; SiteVars.CohortWoodC[site] += woodC; SiteVars.CohortCRootC[site] += cRootC; SiteVars.CohortWoodN[site] += woodN; SiteVars.CohortCRootN[site] += cRootN; return; }
//--------------------------------------------------------------------- // Method for calculating Mineral N allocation, called from Century.cs Run method before calling Grow // Iterates through cohorts, assigning each a portion of mineral N based on coarse root biomass. Uses an exponential function to "distribute" // the N more evenly between spp. so that the ones with the most woody biomass don't get all the N (L122). public static void CalculateMineralNfraction(Site site) { AvailableN.CohortMineralNfraction = new Dictionary <int, Dictionary <int, double> >(); double NAllocTotal = 0.0; foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site]) { foreach (ICohort cohort in speciesCohorts) { int cohortAddYear = GetAddYear(cohort); //PlugIn.ModelCore.UI.WriteLine("CALCMineralNfraction: year={0}, mo={1}, species={2}, cohortAge={3}, cohortAddYear={4}.", PlugIn.ModelCore.CurrentTime, Main.Month, cohort.Species.Name, cohort.Age, cohortAddYear); //Nallocation is a measure of how much N a cohort can gather relative to other cohorts //double Nallocation = Roots.CalculateFineRoot(cohort.LeafBiomass); double Nallocation = 1 - Math.Exp((-Roots.CalculateCoarseRoot(cohort, cohort.WoodBiomass) * 0.02)); if (Nallocation <= 0.0) { Nallocation = Math.Max(Nallocation, cohort.WoodBiomass * 0.01); } NAllocTotal += Nallocation; Dictionary <int, double> newEntry = new Dictionary <int, double>(); newEntry.Add(cohortAddYear, Nallocation); if (CohortMineralNfraction.ContainsKey(cohort.Species.Index)) { if (!CohortMineralNfraction[cohort.Species.Index].ContainsKey(cohortAddYear)) { CohortMineralNfraction[cohort.Species.Index][cohortAddYear] = Nallocation; } } else { CohortMineralNfraction.Add(cohort.Species.Index, newEntry); } } } // Next relativize foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site]) { //PlugIn.ModelCore.UI.WriteLine(" SpeciesCohorts = {0}", speciesCohorts.Species.Name); foreach (ICohort cohort in speciesCohorts) { int cohortAddYear = GetAddYear(cohort); double Nallocation = CohortMineralNfraction[cohort.Species.Index][cohortAddYear]; double relativeNallocation = Nallocation / NAllocTotal; CohortMineralNfraction[cohort.Species.Index][cohortAddYear] = relativeNallocation; if (Double.IsNaN(relativeNallocation) || Double.IsNaN(Nallocation) || Double.IsNaN(NAllocTotal)) { PlugIn.ModelCore.UI.WriteLine(" N ALLOCATION CALCULATION = NaN! "); PlugIn.ModelCore.UI.WriteLine(" Site_Row={0:0}, Site_Column={1:0}.", site.Location.Row, site.Location.Column); PlugIn.ModelCore.UI.WriteLine(" Nallocation={0:0.00}, NAllocTotal={1:0.00}, relativeNallocation={2:0.00}.", Nallocation, NAllocTotal, relativeNallocation); PlugIn.ModelCore.UI.WriteLine(" Wood={0:0.00}, Leaf={1:0.00}.", cohort.WoodBiomass, cohort.LeafBiomass); } } } }
//--------------------------------------------------------------------- // This method does not trigger reproduction public void CohortPartialMortality(object sender, Landis.Library.BiomassCohorts.PartialDeathEventArgs eventArgs) { //PlugIn.ModelCore.UI.WriteLine("Cohort Partial Mortality: {0}", eventArgs.Site); ExtensionType disturbanceType = eventArgs.DisturbanceType; ActiveSite site = eventArgs.Site; ICohort cohort = (Landis.Library.LeafBiomassCohorts.ICohort)eventArgs.Cohort; float fractionPartialMortality = (float)eventArgs.Reduction; float foliarInput = cohort.LeafBiomass * fractionPartialMortality; float woodInput = cohort.WoodBiomass * fractionPartialMortality; if (disturbanceType.IsMemberOf("disturbance:harvest")) { SiteVars.HarvestPrescriptionName = PlugIn.ModelCore.GetSiteVar <string>("Harvest.PrescriptionName"); if (!Disturbed[site]) // this is the first cohort killed/damaged { HarvestEffects.ReduceLayers(SiteVars.HarvestPrescriptionName[site], site); } woodInput -= woodInput * (float)HarvestEffects.GetCohortWoodRemoval(site); foliarInput -= foliarInput * (float)HarvestEffects.GetCohortLeafRemoval(site); } if (disturbanceType.IsMemberOf("disturbance:fire")) { SiteVars.FireSeverity = PlugIn.ModelCore.GetSiteVar <byte>("Fire.Severity"); if (!Disturbed[site]) // this is the first cohort killed/damaged { SiteVars.SmolderConsumption[site] = 0.0; SiteVars.FlamingConsumption[site] = 0.0; if (SiteVars.FireSeverity != null && SiteVars.FireSeverity[site] > 0) { FireEffects.ReduceLayers(SiteVars.FireSeverity[site], site); } } double woodFireConsumption = woodInput * (float)FireEffects.ReductionsTable[(int)SiteVars.FireSeverity[site]].CoarseLitterReduction; double foliarFireConsumption = foliarInput * (float)FireEffects.ReductionsTable[(int)SiteVars.FireSeverity[site]].FineLitterReduction; SiteVars.SmolderConsumption[site] += woodFireConsumption; SiteVars.FlamingConsumption[site] += foliarFireConsumption; woodInput -= (float)woodFireConsumption; foliarInput -= (float)foliarFireConsumption; } ForestFloor.AddWoodLitter(woodInput, cohort.Species, site); ForestFloor.AddFoliageLitter(foliarInput, cohort.Species, site); Roots.AddCoarseRootLitter(Roots.CalculateCoarseRoot(cohort, cohort.WoodBiomass * fractionPartialMortality), cohort, cohort.Species, site); Roots.AddFineRootLitter(Roots.CalculateFineRoot(cohort, cohort.LeafBiomass * fractionPartialMortality), cohort, cohort.Species, site); //PlugIn.ModelCore.UI.WriteLine("EVENT: Cohort Partial Mortality: species={0}, age={1}, disturbance={2}.", cohort.Species.Name, cohort.Age, disturbanceType); //PlugIn.ModelCore.UI.WriteLine(" Cohort Reductions: Foliar={0:0.00}. Wood={1:0.00}.", HarvestEffects.GetCohortLeafRemoval(site), HarvestEffects.GetCohortLeafRemoval(site)); //PlugIn.ModelCore.UI.WriteLine(" InputB/TotalB: Foliar={0:0.00}/{1:0.00}, Wood={2:0.0}/{3:0.0}.", foliarInput, cohort.LeafBiomass, woodInput, cohort.WoodBiomass); Disturbed[site] = true; return; }
//--------------------------------------------------------------------- /// <summary> /// Calculates cohort N demand depending upon how much N would be removed through growth (ANPP) of leaves, wood, coarse roots and fine roots. /// Demand is then used to determine the amount of N that a cohort "wants". /// </summary> public static double CalculateCohortNDemand(ISpecies species, ActiveSite site, ICohort cohort, double[] ANPP) { if (ANPP[1] <= 0.0) { return(0.0); } //return 0.01; if (SpeciesData.NFixer[species]) // We fix our own N! { return(0.0); } double ANPPwood = 0.0; double ANPPleaf = 0.0; double ANPPcoarseRoot = 0.0; double ANPPfineRoot = 0.0; double woodN = 0.0; double coarseRootN = 0.0; double leafN = 0.0; double fineRootN = 0.0; if (ANPP[0] > 0.0) // Wood { ANPPwood = ANPP[0]; ANPPcoarseRoot = Roots.CalculateCoarseRoot(cohort, ANPPwood); woodN = ANPPwood * 0.47 / SpeciesData.WoodCN[species]; coarseRootN = ANPPcoarseRoot * 0.47 / SpeciesData.CoarseRootCN[species]; } if (ANPP[1] > 0.0) // Leaf { ANPPleaf = ANPP[1]; ANPPfineRoot = Roots.CalculateFineRoot(cohort, ANPPleaf); leafN = ANPPleaf * 0.47 / SpeciesData.LeafCN[species]; fineRootN = ANPPfineRoot * 0.47 / SpeciesData.FineRootCN[species]; } double totalANPP_C = (ANPPleaf + ANPPwood + ANPPcoarseRoot + ANPPfineRoot) * 0.47; double Ndemand = leafN + woodN + coarseRootN + fineRootN; if (Ndemand < 0.0) { PlugIn.ModelCore.UI.WriteLine(" ERROR: TotalANPP-C={0:0.00} Nreduction={1:0.00}.", totalANPP_C, Ndemand); throw new ApplicationException("Error: N Reduction is < 0. See AvailableN.cs"); } return(Ndemand); }
//--------------------------------------------------------------------- /// <summary> /// Summarize NPP /// </summary> private static void CalculateNPPcarbon(ActiveSite site, ICohort cohort, double[] AGNPP) { double NPPwood = (double)AGNPP[0] * 0.47; double NPPleaf = (double)AGNPP[1] * 0.47; double NPPcoarseRoot = Roots.CalculateCoarseRoot(cohort, NPPwood); double NPPfineRoot = Roots.CalculateFineRoot(cohort, NPPleaf); if (Double.IsNaN(NPPwood) || Double.IsNaN(NPPleaf) || Double.IsNaN(NPPcoarseRoot) || Double.IsNaN(NPPfineRoot)) { PlugIn.ModelCore.UI.WriteLine(" EITHER WOOD or LEAF NPP or COARSE ROOT or FINE ROOT = NaN! Will set to zero."); PlugIn.ModelCore.UI.WriteLine(" Yr={0},Mo={1}. WoodNPP={0}, LeafNPP={1}, CRootNPP={2}, FRootNPP={3}.", NPPwood, NPPleaf, NPPcoarseRoot, NPPfineRoot); if (Double.IsNaN(NPPleaf)) { NPPleaf = 0.0; } if (Double.IsNaN(NPPwood)) { NPPwood = 0.0; } if (Double.IsNaN(NPPcoarseRoot)) { NPPcoarseRoot = 0.0; } if (Double.IsNaN(NPPfineRoot)) { NPPfineRoot = 0.0; } } SiteVars.AGNPPcarbon[site] += NPPwood + NPPleaf; SiteVars.BGNPPcarbon[site] += NPPcoarseRoot + NPPfineRoot; SiteVars.MonthlyAGNPPcarbon[site][Main.Month] += NPPwood + NPPleaf; SiteVars.MonthlyBGNPPcarbon[site][Main.Month] += NPPcoarseRoot + NPPfineRoot; if (PlugIn.ModelCore.CurrentTime > 0 && OtherData.CalibrateMode) { Outputs.CalibrateLog.Write("{0:0.00},{1:0.00},", NPPwood, NPPleaf); } }
//--------------------------------------------------------------------- private void UpdateDeadBiomass(ICohort cohort, ActiveSite site, double[] totalMortality) { double mortality_wood = (double)totalMortality[0]; double mortality_nonwood = (double)totalMortality[1]; // Add mortality to dead biomass pools. // Coarse root mortality is assumed proportional to aboveground woody mortality // mass is assumed 25% of aboveground wood (White et al. 2000, Niklas & Enquist 2002) if (mortality_wood > 0.0) { ForestFloor.AddWoodLitter(mortality_wood, cohort.Species, site); Roots.AddCoarseRootLitter(mortality_wood, cohort, cohort.Species, site); } if (mortality_nonwood > 0.0) { AvailableN.AddResorbedN(cohort, totalMortality[1], site); //ignoring input from scorching, which is rare, but not resorbed. ForestFloor.AddResorbedFoliageLitter(mortality_nonwood, cohort.Species, site); Roots.AddFineRootLitter(mortality_nonwood, cohort, cohort.Species, site); } return; }
//--------------------------------------------------------------------- // Total mortality, including from disturbance or senescence. public void CohortTotalMortality(object sender, Landis.Library.BiomassCohorts.DeathEventArgs eventArgs) { //PlugIn.ModelCore.UI.WriteLine("Cohort Total Mortality: {0}", eventArgs.Site); ExtensionType disturbanceType = eventArgs.DisturbanceType; ActiveSite site = eventArgs.Site; ICohort cohort = (Landis.Library.LeafBiomassCohorts.ICohort)eventArgs.Cohort; double foliarInput = (double)cohort.LeafBiomass; double woodInput = (double)cohort.WoodBiomass; if (disturbanceType != null) { //PlugIn.ModelCore.UI.WriteLine("DISTURBANCE EVENT: Cohort Died: species={0}, age={1}, disturbance={2}.", cohort.Species.Name, cohort.Age, eventArgs.DisturbanceType); if (disturbanceType.IsMemberOf("disturbance:fire")) { SiteVars.FireSeverity = PlugIn.ModelCore.GetSiteVar <byte>("Fire.Severity"); Landis.Library.Succession.Reproduction.CheckForPostFireRegen(eventArgs.Cohort, site); if (!Disturbed[site]) // the first cohort killed/damaged { SiteVars.SmolderConsumption[site] = 0.0; SiteVars.FlamingConsumption[site] = 0.0; if (SiteVars.FireSeverity != null && SiteVars.FireSeverity[site] > 0) { FireEffects.ReduceLayers(SiteVars.FireSeverity[site], site); } } double woodFireConsumption = woodInput * (float)FireEffects.ReductionsTable[(int)SiteVars.FireSeverity[site]].CoarseLitterReduction; double foliarFireConsumption = foliarInput * (float)FireEffects.ReductionsTable[(int)SiteVars.FireSeverity[site]].FineLitterReduction; SiteVars.SmolderConsumption[site] += woodFireConsumption; SiteVars.FlamingConsumption[site] += foliarFireConsumption; woodInput -= (float)woodFireConsumption; foliarInput -= (float)foliarFireConsumption; } else { if (disturbanceType.IsMemberOf("disturbance:harvest")) { SiteVars.HarvestPrescriptionName = PlugIn.ModelCore.GetSiteVar <string>("Harvest.PrescriptionName"); if (!Disturbed[site]) // the first cohort killed/damaged { HarvestEffects.ReduceLayers(SiteVars.HarvestPrescriptionName[site], site); } woodInput -= woodInput * (float)HarvestEffects.GetCohortWoodRemoval(site); foliarInput -= foliarInput * (float)HarvestEffects.GetCohortLeafRemoval(site); } // If not fire, check for resprouting: Landis.Library.Succession.Reproduction.CheckForResprouting(eventArgs.Cohort, site); } } //PlugIn.ModelCore.UI.WriteLine("Cohort Died: species={0}, age={1}, wood={2:0.00}, foliage={3:0.00}.", cohort.Species.Name, cohort.Age, wood, foliar); ForestFloor.AddWoodLitter(woodInput, cohort.Species, eventArgs.Site); ForestFloor.AddFoliageLitter(foliarInput, cohort.Species, eventArgs.Site); // Assume that ALL dead root biomass stays on site. Roots.AddCoarseRootLitter(Roots.CalculateCoarseRoot(cohort, cohort.WoodBiomass), cohort, cohort.Species, eventArgs.Site); Roots.AddFineRootLitter(Roots.CalculateFineRoot(cohort, cohort.LeafBiomass), cohort, cohort.Species, eventArgs.Site); if (disturbanceType != null) { Disturbed[site] = true; } return; }