/// <summary> /// Adds some biomass for a species to the WOODY pools at a site. /// </summary> public static void AddWoody(double woodyBiomass, ISpecies species, ActiveSite site) { SiteVars.WoodyDebris[site].AddMass(woodyBiomass, SpeciesData.WoodyDebrisDecay[species]); }
//--------------------------------------------------------------------- /// <summary> /// Assigns an active site to a particular stand. /// </summary> /// <param name="activeSite"> /// The active site that is being assigned to a stand. /// </param> /// <param name="mapCode"> /// The map code of the stand that the site is being assigned to. /// </param> public static void AssignSiteToStand(ActiveSite activeSite, ushort mapCode) { double blockArea = (activeSite.SharesData ? Model.BlockArea : Model.Core.CellArea) // Skip the site if its management area is not active. if (SiteVars.ManagementArea[activeSite] == null) return; Stand stand; //check if this stand is already in the dictionary if (stands.TryGetValue(mapCode, out stand)) { //if the stand is already in the dictionary, check if it is in the same management area. //if it's not in the same MA, throw exception. if (SiteVars.ManagementArea[activeSite] != stand.ManagementArea) { throw new PixelException(activeSite.Location, "Stand {0} is in management areas {1} and {2}", stand.MapCode, stand.ManagementArea.MapCode, SiteVars.ManagementArea[activeSite].MapCode); } } //valid site location which has not been keyed by the dictionary. else { //assign stand (trygetvalue set it to null when it wasn't found in the dictionary) stand = new Stand(mapCode, blockArea); //add this stand to the correct management area (pointed to by the site) SiteVars.ManagementArea[activeSite].Add(stand); stands[mapCode] = stand; } //add this site to this stand stand.Add(activeSite); }
//--------------------------------------------------------------------- /// <summary> /// Grows all cohorts at a site for a specified number of years. The /// dead pools at the site also decompose for the given time period. /// </summary> public static void GrowCohorts(Landis.Library.BiomassCohorts.SiteCohorts cohorts, ActiveSite site, int years, bool isSuccessionTimestep) { if (SiteVars.Cohorts[site] == null) return; for (int y = 1; y <= years; ++y) { SpeciesData.ChangeDynamicParameters(PlugIn.ModelCore.CurrentTime + y - 1); SiteVars.ResetAnnualValues(site); CohortBiomass.SubYear = y - 1; CohortBiomass.CanopyLightExtinction = 0.0; // SiteVars.LAI[site] = 0.0; SiteVars.PercentShade[site] = 0.0; SiteVars.LightTrans[site] = 1.0; SiteVars.Cohorts[site].Grow(site, (y == years && isSuccessionTimestep)); SiteVars.WoodyDebris[site].Decompose(); SiteVars.Litter[site].Decompose(); } }
//--------------------------------------------------------------------- public static void MyAddNewCohort(ISpecies species, ActiveSite site) { Assert.IsTrue(speciesThatReproduce.Contains(species)); Assert.AreEqual(expectedSite, site); actualSpecies_AddNewCohort.Add(species); }
//--------------------------------------------------------------------- public void AddNewCohort(ISpecies species, ActiveSite site) { cohorts[site].AddNewCohort(species, CohortBiomass.InitialBiomass(cohorts[site], site, species)); }
public static bool Algorithm(ISpecies species, ActiveSite site) { return Reproduction.SufficientLight(species, site) && Reproduction.Establish(species, site) && Reproduction.MaturePresent(species, site); }
//--------------------------------------------------------------------- public static bool MySeedingAlgorithm(ISpecies species, ActiveSite site) { Assert.IsTrue(expectedSpecies.Contains(species)); Assert.AreEqual(expectedSite, site); actualSpecies_SeedingAlg.Add(species); return speciesThatReproduce.Contains(species); }
//public static bool Establish(double[,] establishment) //--------------------------------------------------------------------- /// <summary> /// Determines if a species can establish on a site. /// </summary> public static bool Establish(ISpecies species, ActiveSite site) { double establishProbability = 0; // Reproduction.GetEstablishProbability(species, site); //return Landis.Model.GenerateUniform() < establishment; return Model.Core.GenerateUniform() < establishProbability; }
public static bool Algorithm(ISpecies species, ActiveSite site) { return Reproduction.SufficientResources(species, site) && Reproduction.Establish(species, site) && //Reproduction.MaturePresent(species, site); SiteVars.Cohorts[site].IsMaturePresent(species); }
//--------------------------------------------------------------------- /// <summary> /// Raises a Cohort.DeathEvent. /// </summary> public static void Died(object sender, ICohort cohort, ActiveSite site, PlugInType disturbanceType) { if (DeathEvent != null) DeathEvent(sender, new DeathEventArgs(cohort, site, disturbanceType)); }
//--------------------------------------------------------------------- public override byte ComputeShade(ActiveSite site) { byte shade = 0; foreach (SpeciesCohorts speciesCohorts in cohorts[site]) { ISpecies species = speciesCohorts.Species; if (species.ShadeTolerance > shade) shade = species.ShadeTolerance; } return shade; }
/// <summary> /// The default method for determining if there is sufficient light at /// a site for a species to germinate/resprout. /// </summary> public static bool SufficientResources(ISpecies species, ActiveSite site) { byte siteShade = SiteVars.Shade[site]; bool sufficientLight; sufficientLight = (species.ShadeTolerance <= 4 && species.ShadeTolerance > siteShade) || (species.ShadeTolerance == 5 && siteShade > 1); // pg 14, Model description, this ----------------^ may be 2? return sufficientLight; }
public void Init() { species = Data.Species[0]; expectedSender = null; deadCohorts = new List<ICohort>(); ILandscape landscape = Data.Make1by1Landscape(); activeSite = landscape[1,1]; Cohort.DeathEvent += MySenescenceDeathMethod; }
//--------------------------------------------------------------------- /// <summary> /// Assigns an active site to a particular management area. /// </summary> /// <param name="activeSite"> /// The active site that is being assigned to a management area. /// </param> /// <param name="mapCode"> /// The map code of the management area that the site is being assigned /// to. /// </param> public static void AssignSiteToMgmtArea(ActiveSite activeSite, ushort mapCode) { ManagementArea mgmtArea = mgmtAreas.Find(mapCode); if (mgmtArea == null) { if (! inactiveMgmtAreas.Contains(mapCode)) inactiveMgmtAreas.Add(mapCode); } else { mgmtArea.OnMap = true; SiteVars.ManagementArea[activeSite] = mgmtArea; } }
public void Init() { species = Data.Species[0]; expectedSender = null; deadCohorts = new List<ICohort>(); bool[,] grid = new bool[,]{ {true} }; DataGrid<bool> dataGrid = new DataGrid<bool>(grid); ILandscape landscape = new Landscape(dataGrid); activeSite = landscape[1,1]; Cohort.DeathEvent += MySenescenceDeathMethod; }
//--------------------------------------------------------------------- /// <summary> /// Computes the actual biomass at a site. The biomass is the total /// of all the site's cohorts except young ones. The total is limited /// to being no more than the site's maximum biomass less the previous /// year's mortality at the site. /// </summary> public static double ActualSiteBiomass(SiteCohorts siteCohorts, ActiveSite site, out IEcoregion ecoregion) { int youngBiomass; int totalBiomass = Cohorts.ComputeBiomass(siteCohorts, out youngBiomass); double B_ACT = totalBiomass - youngBiomass; int lastMortality = siteCohorts.PrevYearMortality; ecoregion = Model.Core.Ecoregion[site]; B_ACT = Math.Min( B_MAX[ecoregion] - lastMortality, B_ACT); return B_ACT; }
public void Init() { abiebals = Data.Species["abiebals"]; betualle = Data.Species["betualle"]; ILandscape landscape = Data.Make1by1Landscape(); activeSite = landscape[1,1]; disturbance = new MockSpeciesCohortsDisturbance(); disturbance.CurrentSite = activeSite; deadCohorts = new Dictionary<ISpecies, List<ushort>>(); Cohort.DeathEvent += MyCohortDiedMethod; }
public void Init() { abiebals = Data.Species["abiebals"]; betualle = Data.Species["betualle"]; bool[,] grid = new bool[,]{ {true} }; DataGrid<bool> dataGrid = new DataGrid<bool>(grid); ILandscape landscape = new Landscape(dataGrid); activeSite = landscape[1,1]; disturbance = new MockSpeciesCohortsDisturbance(); disturbance.CurrentSite = activeSite; deadCohorts = new Dictionary<ISpecies, List<ushort>>(); Cohort.DeathEvent += MyCohortDiedMethod; }
//--------------------------------------------------------------------- bool IFormOfReproduction.TryAt(ActiveSite site) { bool success = false; BitArray selectedSpeciesAtSite = selectedSpecies[site]; for (int index = 0; index < speciesDataset.Count; ++index) { if (selectedSpeciesAtSite.Get(index)) { ISpecies species = speciesDataset[index]; if (PreconditionsSatisfied(species, site)) { Reproduction.AddNewCohort(species, site); success = true; } } } return success; }
//--------------------------------------------------------------------- /// <summary> /// Adds some biomass for a species to the LITTER pools at a site. /// </summary> public static void AddLitter(double nonWoodyBiomass, ISpecies species, ActiveSite site) { IEcoregion ecoregion = PlugIn.ModelCore.Ecoregion[site]; double siteAET = (double)EcoregionData.AET[ecoregion]; //Calculation of decomposition rate for species litter cohort // Decay rate from Meentemeyer 1978. Ecology 59: 465-472. double leafKReg = (-0.5365 + (0.00241 * siteAET)) - (((-0.01586 + (0.000056 * siteAET)) * SpeciesData.LeafLignin[species] * 100)); // From Fan et al. 1998 Ecological Applications 8: 734-737: //double leafKReg = ((0.10015 * siteAET - 3.44618) - (0.01341 + 0.00147 * siteAET) * //SpeciesData.LeafLignin[species]) / 100; //PlugIn.ModelCore.Log.WriteLine("Decay rate for {0} within {1} = {2}. LL = {3}.", species.Name, ecoregion.Name, leafKReg, SpeciesData.LeafLignin[species]); double decayValue = leafKReg; SiteVars.Litter[site].AddMass(nonWoodyBiomass, decayValue); }
//--------------------------------------------------------------------- /// <summary> /// Schedules a list of species to be planted at a site. /// </summary> public static void PreventEstablishment(ActiveSite site) { noEstablish[site] = true; }
public RemoveAgeBetween5And30_AgeOnly(ActiveSite currentSite) : base(currentSite) { }
//--------------------------------------------------------------------- /// <summary> /// Schedules a list of species to be planted at a site. /// </summary> public static void ScheduleForPlanting(Planting.SpeciesList speciesToPlant, ActiveSite site) { planting.Schedule(speciesToPlant, site); }
//--------------------------------------------------------------------- /// <summary> /// Grows all the cohorts by advancing their ages by 1 year and /// updating their biomasses accordingly. /// </summary> /// <param name="site"> /// The site where the cohorts are located. /// </param> private void GrowFor1Year(ActiveSite site) { // Create a list of iterators, one iterator per set of species // cohorts. Iterators go through a species' cohorts from oldest // to youngest. The list is sorted by age, oldest to youngest; // so the first iterator in the list is the iterator's whose // current cohort has the oldest age. List <OldToYoungIterator> itors = new List <OldToYoungIterator>(); foreach (SpeciesCohorts speciesCohorts in cohorts) { OldToYoungIterator itor = speciesCohorts.OldToYoung; InsertIterator(itor, itors); } int siteMortality = 0; // Loop through iterators until they're exhausted while (itors.Count > 0) { // Grow the current cohort of the first iterator in the list. // The cohort's biomass is updated for 1 year's worth of // growth and mortality. OldToYoungIterator itor = itors[0]; siteMortality += itor.GrowCurrentCohort(site, ref totalBiomass, prevYearMortality); if (itor.MoveNext()) { // Iterator has been moved to the next cohort, so see if // the age of this cohort is the oldest. if (itors.Count > 1 && itor.Age < itors[1].Age) { // Pop the first iterator of the list, and then re- // insert it into proper place. itors.RemoveAt(0); InsertIterator(itor, itors); } } else { // Iterator has no more cohorts, so remove it from list. itors.RemoveAt(0); if (itor.SpeciesCohorts.Count > 0) { itor.SpeciesCohorts.UpdateMaturePresent(); } else { // The set of species cohorts is now empty, so remove // it from the list of species cohorts. for (int i = 0; i < cohorts.Count; i++) { if (cohorts[i] == itor.SpeciesCohorts) { cohorts.RemoveAt(i); break; } } } } } prevYearMortality = siteMortality; }
//--------------------------------------------------------------------- public void DecomposeMetabolic(ActiveSite site) { double litterC = this.Carbon; double anerb = SiteVars.AnaerobicEffect[site]; if (litterC > 0.0000001) { // Determine C/N ratios for flows to SOM1 double ratioCNtoSOM1 = 0.0; double co2loss = 0.0; // Compute ratios for surface metabolic residue if (this.Type == LayerType.Surface) { ratioCNtoSOM1 = Layer.AbovegroundDecompositionRatio(this.Nitrogen, litterC); } //Compute ratios for soil metabolic residue else { ratioCNtoSOM1 = Layer.BelowgroundDecompositionRatio(site, OtherData.MinCNenterSOM1, OtherData.MaxCNenterSOM1, OtherData.MinContentN_SOM1); } //Compute total C flow out of metabolic layer double totalCFlow = litterC * SiteVars.DecayFactor[site] * OtherData.LitterParameters[(int)this.Type].DecayRateMetabolicC * OtherData.MonthAdjust; //PlugIn.ModelCore.UI.WriteLine("DecomposeMeta1. MineralN={0:0.00}.", SiteVars.MineralN[site]); //Added impact of soil anerobic conditions if (this.Type == LayerType.Soil) { totalCFlow *= anerb; } //Make sure metabolic C does not go negative. if (totalCFlow > litterC) { totalCFlow = litterC; } //If decomposition can occur, if (this.DecomposePossible(ratioCNtoSOM1, SiteVars.MineralN[site])) { //CO2 loss if (this.Type == LayerType.Surface) { co2loss = totalCFlow * OtherData.MetabolicToCO2Surface; } else { co2loss = totalCFlow * OtherData.MetabolicToCO2Soil; } //PlugIn.ModelCore.UI.WriteLine("BeforeResp. MineralN={0:0.00}.", SiteVars.MineralN[site]); this.Respiration(co2loss, site); //PlugIn.ModelCore.UI.WriteLine("AfterResp. MineralN={0:0.00}.", SiteVars.MineralN[site]); //Decompose metabolic into som1 double netCFlow = totalCFlow - co2loss; if (netCFlow > litterC) { PlugIn.ModelCore.UI.WriteLine(" ERROR: Decompose Metabolic: netCFlow={0:0.000} > layer.Carbon={0:0.000}.", netCFlow, this.Carbon); } // -- CARBON AND NITROGEN --------------------------- // Partition and schedule C flows // Compute and schedule N flows and update mineralization accumulators. if ((int)this.Type == (int)LayerType.Surface) { this.TransferCarbon(SiteVars.SOM1surface[site], netCFlow); this.TransferNitrogen(SiteVars.SOM1surface[site], netCFlow, litterC, ratioCNtoSOM1, site); //PlugIn.ModelCore.UI.WriteLine("DecomposeMetabolic. MineralN={0:0.00}.", SiteVars.MineralN[site]); } else { this.TransferCarbon(SiteVars.SOM1soil[site], netCFlow); this.TransferNitrogen(SiteVars.SOM1soil[site], netCFlow, litterC, ratioCNtoSOM1, site); } } } //} }
//--------------------------------------------------------------------- /// <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 float[] ComputeChange(ICohort cohort, ActiveSite site) { ecoregion = PlugIn.ModelCore.Ecoregion[site]; // First call to the Calibrate Log: if (PlugIn.ModelCore.CurrentTime > 0 && OtherData.CalibrateMode) { Outputs.CalibrateLog.Write("{0},{1},{2},{3},{4},{5:0.0},{6:0.0},", PlugIn.ModelCore.CurrentTime, Century.Month + 1, ecoregion.Index, cohort.Species.Name, cohort.Age, cohort.WoodBiomass, cohort.LeafBiomass); } double siteBiomass = Century.ComputeLivingBiomass(SiteVars.Cohorts[site]); if (siteBiomass < 0) { throw new ApplicationException("Error: Site biomass < 0"); } // ****** Mortality ******* // Age-related mortality includes woody and standing leaf biomass. double[] mortalityAge = ComputeAgeMortality(cohort, site); // Growth-related mortality double[] mortalityGrowth = ComputeGrowthMortality(cohort, site); double[] totalMortality = new double[2] { Math.Min(cohort.WoodBiomass, mortalityAge[0] + mortalityGrowth[0]), Math.Min(cohort.LeafBiomass, mortalityAge[1] + mortalityGrowth[1]) }; double nonDisturbanceLeafFall = totalMortality[1]; // ****** Growth ******* double[] actualANPP = ComputeActualANPP(cohort, site, siteBiomass, mortalityAge); double scorch = 0.0; defoliatedLeafBiomass = 0.0; if (Century.Month == 6) //July = 6 { if (SiteVars.FireSeverity != null && SiteVars.FireSeverity[site] > 0) { scorch = FireEffects.CrownScorching(cohort, SiteVars.FireSeverity[site]); } if (scorch > 0.0) // NEED TO DOUBLE CHECK WHAT CROWN SCORCHING RETURNS { totalMortality[1] = Math.Min(cohort.LeafBiomass, scorch + totalMortality[1]); } // Defoliation (index) ranges from 1.0 (total) to none (0.0). if (PlugIn.ModelCore.CurrentTime > 0) //Skip this during initialization { //defoliation = Landis.Library.LeafBiomassCohorts.CohortDefoliation.Compute(cohort, site, (int)siteBiomass); int cohortBiomass = (int)(cohort.LeafBiomass + cohort.WoodBiomass); defoliation = Landis.Library.Biomass.CohortDefoliation.Compute(site, cohort.Species, cohortBiomass, (int)siteBiomass); } if (defoliation > 1.0) { defoliation = 1.0; } if (defoliation > 0.0) { defoliatedLeafBiomass = (cohort.LeafBiomass) * defoliation; if (totalMortality[1] + defoliatedLeafBiomass - cohort.LeafBiomass > 0.001) { defoliatedLeafBiomass = cohort.LeafBiomass - totalMortality[1]; } //PlugIn.ModelCore.UI.WriteLine("Defoliation.Month={0:0.0}, LeafBiomass={1:0.00}, DefoliatedLeafBiomass={2:0.00}, TotalLeafMort={2:0.00}", Century.Month, cohort.LeafBiomass, defoliatedLeafBiomass , mortalityAge[1]); ForestFloor.AddFrassLitter(defoliatedLeafBiomass, cohort.Species, site); } } else { defoliation = 0.0; defoliatedLeafBiomass = 0.0; } // RMS 03/2016: Additional mortality as reaching capacity limit: SAVE FOR NEXT RELEASE //double maxBiomass = SpeciesData.B_MAX_Spp[cohort.Species][ecoregion]; //double limitCapacity = Math.Min(1.0, Math.Exp(siteBiomass / maxBiomass * 5.0) / Math.Exp(5.0)); // 1.0 = total limit; 0.0 = No limit //totalMortality[0] += (actualANPP[0] * limitCapacity); // totalMortality not to exceed ANPP allocation if (totalMortality[0] <= 0.0 || cohort.WoodBiomass <= 0.0) { totalMortality[0] = 0.0; } if (totalMortality[1] <= 0.0 || cohort.LeafBiomass <= 0.0) { totalMortality[1] = 0.0; } if ((totalMortality[0]) > cohort.WoodBiomass) { PlugIn.ModelCore.UI.WriteLine("Warning: WOOD Mortality exceeds cohort wood biomass. M={0:0.0}, B={1:0.0}", (totalMortality[0]), cohort.WoodBiomass); PlugIn.ModelCore.UI.WriteLine("Warning: If M>B, then list mortality. Mage={0:0.0}, Mgrow={1:0.0},", mortalityAge[0], mortalityGrowth[0]); throw new ApplicationException("Error: WOOD Mortality exceeds cohort biomass"); } if ((totalMortality[1] + defoliatedLeafBiomass - cohort.LeafBiomass) > 0.01) { PlugIn.ModelCore.UI.WriteLine("Warning: LEAF Mortality exceeds cohort biomass. Mortality={0:0.000}, Leafbiomass={1:0.000}", (totalMortality[1] + defoliatedLeafBiomass), cohort.LeafBiomass); PlugIn.ModelCore.UI.WriteLine("Warning: If M>B, then list mortality. Mage={0:0.00}, Mgrow={1:0.00}, Mdefo={2:0.000},", mortalityAge[1], mortalityGrowth[1], defoliatedLeafBiomass); throw new ApplicationException("Error: LEAF Mortality exceeds cohort biomass"); } float deltaWood = (float)(actualANPP[0] - totalMortality[0]); float deltaLeaf = (float)(actualANPP[1] - totalMortality[1] - defoliatedLeafBiomass); float[] deltas = new float[2] { deltaWood, deltaLeaf }; //if((totalMortality[1] + defoliatedLeafBiomass) > cohort.LeafBiomass) // PlugIn.ModelCore.UI.WriteLine("Warning: Leaf Mortality exceeds cohort leaf biomass. M={0:0.0}, B={1:0.0}, DefoLeafBiomass={2:0.0}, defoliationIndex={3:0.0}", totalMortality[1], cohort.LeafBiomass, defoliatedLeafBiomass, defoliation); UpdateDeadBiomass(cohort, site, totalMortality); CalculateNPPcarbon(site, cohort, actualANPP); AvailableN.AdjustAvailableN(cohort, site, actualANPP); if (OtherData.CalibrateMode && PlugIn.ModelCore.CurrentTime > 0) { Outputs.CalibrateLog.WriteLine("{0:0.00},{1:0.00},{2:0.00},{3:0.00},", deltaWood, deltaLeaf, totalMortality[0], totalMortality[1]); //Outputs.CalibrateLog.WriteLine("{0:0.00}, {1:0.00}, {2:0.00}", resorbedNused, mineralNused, totalNdemand); } return(deltas); }
/// <summary> /// Grows all cohorts at a site for a specified number of years. /// Litter is decomposed following the Century model. /// </summary> public static ISiteCohorts Run(ActiveSite site, int years, bool isSuccessionTimeStep) { ISiteCohorts siteCohorts = SiteVars.Cohorts[site]; IEcoregion ecoregion = PlugIn.ModelCore.Ecoregion[site]; for (int y = 0; y < years; ++y) { Year = y + 1; //if (PlugIn.ModelCore.CurrentTime > 0 && Climate.Future_MonthlyData.ContainsKey(PlugIn.FutureClimateBaseYear + y + PlugIn.ModelCore.CurrentTime- years)) if (Climate.Future_MonthlyData.ContainsKey(PlugIn.FutureClimateBaseYear + y + PlugIn.ModelCore.CurrentTime - years)) { ClimateRegionData.AnnualWeather[ecoregion] = Climate.Future_MonthlyData[PlugIn.FutureClimateBaseYear + y - years + PlugIn.ModelCore.CurrentTime][ecoregion.Index]; } //PlugIn.ModelCore.UI.WriteLine("PlugIn_FutureClimateBaseYear={0}, y={1}, ModelCore_CurrentTime={2}, CenturyTimeStep = {3}, SimulatedYear = {4}.", PlugIn.FutureClimateBaseYear, y, PlugIn.ModelCore.CurrentTime, years, (PlugIn.FutureClimateBaseYear + y - years + PlugIn.ModelCore.CurrentTime)); SiteVars.ResetAnnualValues(site); if (y == 0 && SiteVars.FireSeverity != null && SiteVars.FireSeverity[site] > 0) { FireEffects.ReduceLayers(SiteVars.FireSeverity[site], site); } // Next, Grow and Decompose each month int[] months = new int[12] { 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5 }; if (OtherData.CalibrateMode) { //months = new int[12]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; This output will not match normal mode due to differences in initialization months = new int[12] { 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5 } } ; PlugIn.AnnualWaterBalance = 0; for (MonthCnt = 0; MonthCnt < 12; MonthCnt++) { // Calculate mineral N fractions based on coarse root biomass. Only need to do once per year. if (MonthCnt == 0) { AvailableN.CalculateMineralNfraction(site); } //PlugIn.ModelCore.UI.WriteLine("SiteVars.MineralN = {0:0.00}, month = {1}.", SiteVars.MineralN[site], i); Month = months[MonthCnt]; SiteVars.MonthlyAGNPPcarbon[site][Month] = 0.0; SiteVars.MonthlyBGNPPcarbon[site][Month] = 0.0; SiteVars.MonthlyNEE[site][Month] = 0.0; SiteVars.MonthlyResp[site][Month] = 0.0; SiteVars.MonthlyStreamN[site][Month] = 0.0; SiteVars.SourceSink[site].Carbon = 0.0; SiteVars.TotalWoodBiomass[site] = Century.ComputeWoodBiomass((ActiveSite)site); //SiteVars.LAI[site] = Century.ComputeLAI((ActiveSite)site); double ppt = ClimateRegionData.AnnualWeather[ecoregion].MonthlyPrecip[Century.Month]; double monthlyNdeposition; if (PlugIn.AtmosNintercept != -1 && PlugIn.AtmosNslope != -1) { monthlyNdeposition = PlugIn.AtmosNintercept + (PlugIn.AtmosNslope * ppt); } else { monthlyNdeposition = ClimateRegionData.AnnualWeather[ecoregion].MonthlyNDeposition[Century.Month]; } if (monthlyNdeposition < 0) { throw new System.ApplicationException("Error: Nitrogen deposition less than zero."); } ClimateRegionData.MonthlyNDeposition[ecoregion][Month] = monthlyNdeposition; ClimateRegionData.AnnualNDeposition[ecoregion] += monthlyNdeposition; SiteVars.MineralN[site] += monthlyNdeposition; //PlugIn.ModelCore.UI.WriteLine("Ndeposition={0},MineralN={1:0.00}.", monthlyNdeposition, SiteVars.MineralN[site]); double liveBiomass = (double)ComputeLivingBiomass(siteCohorts); double baseFlow, stormFlow, AET; SoilWater.Run(y, Month, liveBiomass, site, out baseFlow, out stormFlow, out AET); PlugIn.AnnualWaterBalance += ppt - AET; // Calculate N allocation for each cohort AvailableN.SetMineralNallocation(site); if (MonthCnt == 11) { siteCohorts.Grow(site, (y == years && isSuccessionTimeStep), true); } else { siteCohorts.Grow(site, (y == years && isSuccessionTimeStep), false); } WoodLayer.Decompose(site); LitterLayer.Decompose(site); SoilLayer.Decompose(site); //...Volatilization loss as a function of the mineral N which // remains after uptake by plants. ML added a correction factor for wetlands since their denitrification rate is double that of wetlands //based on a review paper by Seitziner 2006. double volatilize = (SiteVars.MineralN[site] * PlugIn.DenitrificationRate); //ClimateRegionData.Denitrif[ecoregion]); // monthly value //PlugIn.ModelCore.UI.WriteLine("BeforeVol. MineralN={0:0.00}.", SiteVars.MineralN[site]); SiteVars.MineralN[site] -= volatilize; SiteVars.SourceSink[site].Nitrogen += volatilize; SiteVars.Nvol[site] += volatilize; SoilWater.Leach(site, baseFlow, stormFlow); SiteVars.MonthlyNEE[site][Month] -= SiteVars.MonthlyAGNPPcarbon[site][Month]; SiteVars.MonthlyNEE[site][Month] -= SiteVars.MonthlyBGNPPcarbon[site][Month]; SiteVars.MonthlyNEE[site][Month] += SiteVars.SourceSink[site].Carbon; } } ComputeTotalCohortCN(site, siteCohorts); return(siteCohorts); }
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public RemoveAgeBetween5And30(ActiveSite currentSite) { this.currentSite = currentSite; }
//--------------------------------------------------------------------- // 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; SiteVars.SourceSink[site].Carbon += woodFireConsumption * 0.47; SiteVars.SourceSink[site].Carbon += foliarFireConsumption * 0.47; woodInput -= woodFireConsumption; foliarInput -= 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); } double woodLoss = woodInput * (float)HarvestEffects.GetCohortWoodRemoval(site); double foliarLoss = foliarInput * (float)HarvestEffects.GetCohortLeafRemoval(site); SiteVars.SourceSink[site].Carbon += woodLoss * 0.47; SiteVars.SourceSink[site].Carbon += foliarLoss * 0.47; woodInput -= woodLoss; foliarInput -= foliarLoss; } // 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; }
//--------------------------------------------------------------------- // Initialize landscape with patches of defoliation during the first year public static void InitializeDefoliationPatches(IInsect insect) { PlugIn.ModelCore.UI.WriteLine(" Initializing Defoliation Patches... "); SiteVars.InitialOutbreakProb.ActiveSiteValues = 0.0; insect.Disturbed.ActiveSiteValues = false; insect.NeighborhoodDefoliation.ActiveSiteValues = 0.0; foreach (ActiveSite site in PlugIn.ModelCore.Landscape) { double suscIndexSum = 0.0; double sumBio = 0.0; foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site]) { foreach (ICohort cohort in speciesCohorts) { suscIndexSum += cohort.Biomass * (insect.SppTable[cohort.Species.Index].Susceptibility); sumBio += cohort.Biomass; } } // If no biomass, no chance of defoliation, go to the next site. if (suscIndexSum <= 0 || sumBio <= 0) { SiteVars.InitialOutbreakProb[site] = 0.0; continue; } int suscIndex = (int)Math.Round(suscIndexSum / sumBio) - 1; if (suscIndex > 2.0 || suscIndex < 0) { PlugIn.ModelCore.UI.WriteLine("SuscIndex < 0 || > 2. Site R/C={0}/{1},suscIndex={2},suscIndexSum={3},sumBio={4}.", site.Location.Row, site.Location.Column, suscIndex, suscIndexSum, sumBio); throw new ApplicationException("Error: SuscIndex is not between 2.0 and 0.0"); } // Assume that there are no neighbors whatsoever: DistributionType dist = insect.SusceptibleTable[suscIndex].Distribution_80.Name; // PlugIn.ModelCore.UI.WriteLine("suscIndex={0},suscIndexSum={1},cohortBiomass={2}.", suscIndex,suscIndexSum,sumBio); double value1 = insect.SusceptibleTable[suscIndex].Distribution_80.Value1; double value2 = insect.SusceptibleTable[suscIndex].Distribution_80.Value2; double probability = Distribution.GenerateRandomNum(dist, value1, value2); if (probability > 1.0 || probability < 0) { PlugIn.ModelCore.UI.WriteLine("Initial Defoliation Probility < 0 || > 1. Site R/C={0}/{1}.", site.Location.Row, site.Location.Column); throw new ApplicationException("Error: Probability is not between 1.0 and 0.0"); } SiteVars.InitialOutbreakProb[site] = probability; // PlugIn.ModelCore.UI.WriteLine("Susceptiblity index={0}. Outbreak Probability={1:0.00}. R/C={2}/{3}.", suscIndex, probability, site.Location.Row, site.Location.Column); } foreach (ActiveSite site in PlugIn.ModelCore.Landscape) { //get a random site from the stand double randomNum = PlugIn.ModelCore.GenerateUniform(); double randomNum2 = PlugIn.ModelCore.GenerateUniform(); //Create random variability in outbreak area within a simulation so outbreaks are more variable. double initialAreaCalibratorRandomNum = (randomNum2 - 0.5) * insect.InitialPatchOutbreakSensitivity / 2; //Start spreading! if (randomNum < SiteVars.InitialOutbreakProb[site] * (insect.InitialPatchOutbreakSensitivity + initialAreaCalibratorRandomNum)) { //start with this site (if it's active) ActiveSite currentSite = site; //queue to hold sites to defoliate Queue <ActiveSite> sitesToConsider = new Queue <ActiveSite>(); //put initial site on queue sitesToConsider.Enqueue(currentSite); DistributionType dist = insect.InitialPatchDistr; double targetArea = Distribution.GenerateRandomNum(dist, insect.InitialPatchValue1, insect.InitialPatchValue2); //PlugIn.ModelCore.UI.WriteLine(" Target Patch Area={0:0.0}.", targetArea); double areaSelected = 0.0; //loop through stand, defoliating patches of size target area while (sitesToConsider.Count > 0 && areaSelected < targetArea) { currentSite = sitesToConsider.Dequeue(); // Because this is the first year, neighborhood defoliation is given a value. // The value is used in Defoliate.DefoliateCohort() insect.NeighborhoodDefoliation[currentSite] = SiteVars.InitialOutbreakProb[currentSite]; areaSelected += PlugIn.ModelCore.CellArea; //insect.Disturbed[currentSite] = true; //Next, add site's neighbors to the list of //sites to consider. //loop through the site's neighbors enqueueing all the good ones. //double maxNeighborProb = 0.0; //Site maxNeighbor = currentSite; //bool foundNewNeighbor = false; foreach (RelativeLocation loc in all_neighbor_locations) { Site neighbor = currentSite.GetNeighbor(loc); //get a neighbor site (if it's non-null and active) if (neighbor != null && neighbor.IsActive && !sitesToConsider.Contains((ActiveSite)neighbor) && !insect.Disturbed[neighbor]) { insect.Disturbed[currentSite] = true; randomNum = PlugIn.ModelCore.GenerateUniform(); /*if (SiteVars.InitialOutbreakProb[neighbor] > maxNeighborProb) * { * maxNeighbor = currentSite.GetNeighbor(loc); * maxNeighborProb = SiteVars.InitialOutbreakProb[neighbor]; * foundNewNeighbor = true; * }*/ //check if it's a valid neighbor: if (SiteVars.InitialOutbreakProb[neighbor] * insect.InitialPatchShapeCalibrator > randomNum) { sitesToConsider.Enqueue((ActiveSite)neighbor); } } } //if(foundNewNeighbor) // sitesToConsider.Enqueue((ActiveSite) maxNeighbor); } //PlugIn.ModelCore.UI.WriteLine(" Initial Patch Area Selected={0:0.0}.", areaSelected); } } }
public void BiomassDisturbance() { expectedDistType = RemoveAgeBetween5And30.DisturbanceType; expectedSite = activeSite; CohortsRemoved(new RemoveAgeBetween5And30_Biomass(activeSite)); }
//--------------------------------------------------------------------- /// <summary> /// Determines if there is a mature cohort at a site. /// This is a Delegate method to base succession. /// </summary> public bool MaturePresent(ISpecies species, ActiveSite site) { return(SiteVars.Cohorts[site].IsMaturePresent(species)); }
//--------------------------------------------------------------------- /// <summary> /// Add a new cohort to a site. /// This is a Delegate method to base succession. /// </summary> public void AddNewCohort(ISpecies species, ActiveSite site) { float[] initialBiomass = CohortBiomass.InitialBiomass(species, SiteVars.Cohorts[site], site); SiteVars.Cohorts[site].AddNewCohort(species, 1, initialBiomass[0], initialBiomass[1]); }
//--------------------------------------------------------------------- //Grows the cohorts for future climate protected override void AgeCohorts(ActiveSite site, ushort years, int?successionTimestep) { Main.Run(site, years, successionTimestep.HasValue); }
//--------------------------------------------------------------------- /// <summary> /// Does the appropriate forms of reproduction at a site. /// </summary> public static void Reproduce(ActiveSite site) { if (noEstablish[site]) { return; } bool plantingOccurred = planting.TryAt(site); bool sufficientLight; bool serotinyOccurred = false; if (!plantingOccurred) { for (int index = 0; index < speciesDataset.Count; ++index) { if (serotiny[site].Get(index)) { ISpecies species = speciesDataset[index]; sufficientLight = SufficientResources(species, site); if (sufficientLight && Establish(species, site)) { AddNewCohort(species, site); serotinyOccurred = true; if (isDebugEnabled) { log.DebugFormat("site {0}: {1} post-fire regenerated", site.Location, species.Name); } } else { if (isDebugEnabled) { log.DebugFormat("site {0}: {1} post-fire regen failed: {2}", site.Location, species.Name, !sufficientLight ? "insufficient light" : "didn't establish"); } } } } } serotiny[site].SetAll(false); bool speciesResprouted = false; if (!serotinyOccurred) { for (int index = 0; index < speciesDataset.Count; ++index) { if (resprout[site].Get(index)) { ISpecies species = speciesDataset[index]; sufficientLight = SufficientResources(species, site); if (sufficientLight && (Model.Core.GenerateUniform() < species.VegReprodProb)) { AddNewCohort(species, site); speciesResprouted = true; if (isDebugEnabled) { log.DebugFormat("site {0}: {1} resprouted", site.Location, species.Name); } } else { if (isDebugEnabled) { log.DebugFormat("site {0}: {1} resprouting failed: {2}", site.Location, species.Name, !sufficientLight ? "insufficient light" : "random # >= probability"); } } } } } resprout[site].SetAll(false); if (!plantingOccurred && !serotinyOccurred && !speciesResprouted) { seeding.Do(site); } }
//--------------------------------------------------------------------- /// <summary> /// Computes the initial biomass for a cohort at a site. /// </summary> public static int InitialBiomass(ISpecies species, SiteCohorts siteCohorts, ActiveSite site) { IEcoregion ecoregion = PlugIn.ModelCore.Ecoregion[site]; //double B_ACT = ActualSiteBiomass(siteCohorts, site, out ecoregion); double B_ACT = (double)Cohorts.ComputeNonYoungBiomass(siteCohorts); double maxBiomass = SpeciesData.B_MAX_Spp[species][ecoregion]; double maxANPP = SpeciesData.ANPP_MAX_Spp[species][ecoregion]; // Initial biomass exponentially declines in response to // competition. //double initialBiomass = 0.025 * maxBiomass * Math.Exp(-1.6 * B_ACT / EcoregionData.B_MAX[ecoregion]); double initialBiomass = maxANPP * Math.Exp(-1.6 * B_ACT / EcoregionData.B_MAX[ecoregion]); // Initial biomass cannot be greater than maxANPP initialBiomass = Math.Min(maxANPP, initialBiomass); // Initial biomass cannot be less than 1. initialBiomass = Math.Max(1.0, initialBiomass); return (int)initialBiomass; }
//-------------------------------------------------------------------------- // Originally from lacalc.f of CENTURY model private static double calculateLAI_Limit(ICohort cohort, ActiveSite site) { //...Calculate true LAI using leaf biomass and a biomass-to-LAI // conversion parameter which is the slope of a regression // line derived from LAI vs Foliar Mass for Slash Pine. //...Calculate theoretical LAI as a function of large wood mass. // There is no strong consensus on the true nature of the relationship // between LAI and stemwood mass. Version 3.0 used a negative exponential // relationship between leaf mass and large wood mass, which tended to // break down in very large forests. Many sutdies have cited as "general" // an increase of LAI up to a maximum, then a decrease to a plateau value // (e.g. Switzer et al. 1968, Gholz and Fisher 1982). However, this // response is not general, and seems to mostly be a feature of young // pine plantations. Northern hardwoods have shown a monotonic increase // to a plateau (e.g. Switzer et al. 1968). Pacific Northwest conifers // have shown a steady increase in LAI with no plateau evident (e.g. // Gholz 1982). In this version, we use a simple saturation fucntion in // which LAI increases linearly against large wood mass initially, then // approaches a plateau value. The plateau value can be set very large to // give a response of steadily increasing LAI with stemwood. // References: // 1) Switzer, G.L., L.E. Nelson and W.H. Smith 1968. // The mineral cycle in forest stands. 'Forest // Fertilization: Theory and Practice'. pp 1-9 // Tenn. Valley Auth., Muscle Shoals, AL. // // 2) Gholz, H.L., and F.R. Fisher 1982. Organic matter // production and distribution in slash pine (Pinus // elliotii) plantations. Ecology 63(6): 1827-1839. // // 3) Gholz, H.L. 1982. Environmental limits on aboveground // net primary production and biomass in vegetation zones of // the Pacific Northwest. Ecology 63:469-481. //...Local variables double leafC = (double)cohort.LeafBiomass * 0.47; double largeWoodC = (double)cohort.WoodBiomass * 0.47; double lai = 0.0; double laitop = -0.47; // This is the value given for all biomes in the tree.100 file. double btolai = FunctionalType.Table[SpeciesData.FuncType[cohort.Species]].BTOLAI; double klai = FunctionalType.Table[SpeciesData.FuncType[cohort.Species]].KLAI; double maxlai = FunctionalType.Table[SpeciesData.FuncType[cohort.Species]].MAXLAI; double rlai = (Math.Max(0.0, 1.0 - Math.Exp(btolai * leafC))); if (SpeciesData.LeafLongevity[cohort.Species] > 1.0) { rlai = 1.0; } double tlai = maxlai * largeWoodC / (klai + largeWoodC); //...Choose the LAI reducer on production. I don't really understand // why we take the average in the first case, but it will probably // change... //if (rlai < tlai) lai = (rlai + tlai) / 2.0; lai = tlai * rlai; //else lai = tlai; // This will allow us to set MAXLAI to zero such that LAI is completely dependent upon // foliar carbon, which may be necessary for simulating defoliation events. if (tlai <= 0.0) { lai = rlai; } //lai = tlai; // Century 4.5 ignores rlai. // Limit aboveground wood production by leaf area // index. // // REF: Efficiency of Tree Crowns and Stemwood // Production at Different Canopy Leaf Densities // by Waring, Newman, and Bell // Forestry, Vol. 54, No. 2, 1981 //totalLAI += lai; // if (totalLAI > ClimateRegionData.MaxLAI) // lai = 0.1; // The minimum LAI to calculate effect is 0.2. //if (lai < 0.5) lai = 0.5; if (lai < 0.1) { lai = 0.1; } if (Century.Month == 6) { SiteVars.LAI[site] += lai; //Tracking LAI. } double LAI_limit = Math.Max(0.0, 1.0 - Math.Exp(laitop * lai)); //This allows LAI to go to zero for deciduous trees. if (SpeciesData.LeafLongevity[cohort.Species] <= 1.0 && (Century.Month > FunctionalType.Table[SpeciesData.FuncType[cohort.Species]].LeafNeedleDrop || Century.Month < 3)) { lai = 0.0; LAI_limit = 0.0; } if (PlugIn.ModelCore.CurrentTime > 0 && OtherData.CalibrateMode) { Outputs.CalibrateLog.Write("{0:0.00},{1:0.00},{2:0.00},", lai, tlai, rlai); } //PlugIn.ModelCore.UI.WriteLine("Yr={0},Mo={1}. Spp={2}, leafC={3:0.0}, woodC={4:0.00}.", PlugIn.ModelCore.CurrentTime, month + 1, species.Name, leafC, largeWoodC); //PlugIn.ModelCore.UI.WriteLine("Yr={0},Mo={1}. Spp={2}, lai={3:0.0}, woodC={4:0.00}.", PlugIn.ModelCore.CurrentTime, month + 1, species.Name, lai, largeWoodC); //PlugIn.ModelCore.UI.WriteLine("Yr={0},Mo={1}. LAI Limits: lai={2:0.0}, woodLAI={3:0.0}, leafLAI={4:0.0}, LAIlimit={5:0.00}.", PlugIn.ModelCore.CurrentTime, month + 1, lai, woodLAI, leafLAI, LAI_limit); return(LAI_limit); }
//--------------------------------------------------------------------- /// <summary> /// Computes the percentage of a cohort's standing biomass that is non-woody. /// This method is designed for external calls that need to /// estimate the amount of non-wood biomass. /// </summary> public Percentage ComputeNonWoodyPercentage(ICohort cohort, ActiveSite site) { SiteCohorts siteCohorts = SiteVars.Cohorts[site]; double mortalityAge = ComputeAgeMortality(cohort); //if(siteCohorts == null) return new Percentage(0.0); double actualANPP = ComputeActualANPP(cohort, site, siteCohorts.TotalBiomass, siteCohorts.PrevYearMortality); // Age mortality is discounted from ANPP to prevent the over- // estimation of mortality. ANPP cannot be negative. actualANPP = Math.Max(0, actualANPP - mortalityAge); return new Percentage(ComputeStandingLeafBiomass(actualANPP, cohort) / cohort.Biomass); }
//--------------------------------------------------------------------- private double[] ComputeActualANPP(ICohort cohort, ActiveSite site, double siteBiomass, double[] mortalityAge) { double leafFractionNPP = FunctionalType.Table[SpeciesData.FuncType[cohort.Species]].FCFRACleaf; double maxBiomass = SpeciesData.Max_Biomass[cohort.Species]; //.B_MAX_Spp[cohort.Species][ecoregion]; double sitelai = SiteVars.LAI[site]; double maxNPP = SpeciesData.Max_ANPP[cohort.Species]; //.ANPP_MAX_Spp[cohort.Species][ecoregion]; double limitT = calculateTemp_Limit(site, cohort.Species); double limitH20 = calculateWater_Limit(site, ecoregion, cohort.Species); double limitLAI = calculateLAI_Limit(cohort, site); // RMS 03/2016: Testing alternative more similar to how Biomass Succession operates: REMOVE FOR NEXT RELEASE double limitCapacity = 1.0 - Math.Min(1.0, Math.Exp(siteBiomass / maxBiomass * 5.0) / Math.Exp(5.0)); //double potentialNPP = maxNPP * limitLAI * limitH20 * limitT; // * limitCapacity; double potentialNPP = maxNPP * limitLAI * limitH20 * limitT * limitCapacity; double limitN = calculateN_Limit(site, cohort, potentialNPP, leafFractionNPP); potentialNPP *= limitN; //if (Double.IsNaN(limitT) || Double.IsNaN(limitH20) || Double.IsNaN(limitLAI) || Double.IsNaN(limitCapacity) || Double.IsNaN(limitN)) //{ // PlugIn.ModelCore.UI.WriteLine(" A limit = NaN! Will set to zero."); // PlugIn.ModelCore.UI.WriteLine(" Yr={0},Mo={1}. GROWTH LIMITS: LAI={2:0.00}, H20={3:0.00}, N={4:0.00}, T={5:0.00}, Capacity={6:0.0}", PlugIn.ModelCore.CurrentTime, month + 1, limitLAI, limitH20, limitN, limitT, limitCapacity); // PlugIn.ModelCore.UI.WriteLine(" Yr={0},Mo={1}. Other Information: MaxB={2}, Bsite={3}, Bcohort={4:0.0}, SoilT={5:0.0}.", PlugIn.ModelCore.CurrentTime, month + 1, maxBiomass, (int)siteBiomass, (cohort.WoodBiomass + cohort.LeafBiomass), SiteVars.SoilTemperature[site]); //} // Age mortality is discounted from ANPP to prevent the over- // estimation of growth. ANPP cannot be negative. double actualANPP = Math.Max(0.0, potentialNPP - mortalityAge[0] - mortalityAge[1]); // Growth can be reduced by another extension via this method. // To date, no extension has been written to utilize this hook. double growthReduction = CohortGrowthReduction.Compute(cohort, site); if (growthReduction > 0.0) { actualANPP *= (1.0 - growthReduction); } double leafNPP = actualANPP * leafFractionNPP; double woodNPP = actualANPP * (1.0 - leafFractionNPP); if (Double.IsNaN(leafNPP) || Double.IsNaN(woodNPP)) { PlugIn.ModelCore.UI.WriteLine(" EITHER WOOD or LEAF NPP = NaN! Will set to zero."); //PlugIn.ModelCore.UI.WriteLine(" Yr={0},Mo={1}, SpeciesName={2}, CohortAge={3}. GROWTH LIMITS: LAI={4:0.00}, H20={5:0.00}, N={6:0.00}, T={7:0.00}, Capacity={8:0.0}.", PlugIn.ModelCore.CurrentTime, Century.Month + 1, cohort.Species.Name, cohort.Age, limitLAI, limitH20, limitN, limitT, limitCapacity); PlugIn.ModelCore.UI.WriteLine(" Yr={0},Mo={1}. Other Information: MaxB={2}, Bsite={3}, Bcohort={4:0.0}, SoilT={5:0.0}.", PlugIn.ModelCore.CurrentTime, Century.Month + 1, maxBiomass, (int)siteBiomass, (cohort.WoodBiomass + cohort.LeafBiomass), SiteVars.SoilTemperature[site]); PlugIn.ModelCore.UI.WriteLine(" Yr={0},Mo={1}. WoodNPP={2:0.00}, LeafNPP={3:0.00}.", PlugIn.ModelCore.CurrentTime, Century.Month + 1, woodNPP, leafNPP); if (Double.IsNaN(leafNPP)) { leafNPP = 0.0; } if (Double.IsNaN(woodNPP)) { woodNPP = 0.0; } } if (PlugIn.ModelCore.CurrentTime > 0 && OtherData.CalibrateMode) { //Outputs.CalibrateLog.Write("{0:0.00},{1:0.00},{2:0.00},{3:0.00}, {4:0.00},", limitLAI, limitH20, limitT, limitCapacity, limitN); Outputs.CalibrateLog.Write("{0:0.00},{1:0.00},{2:0.00},{3:0.00},", limitLAI, limitH20, limitT, limitN); Outputs.CalibrateLog.Write("{0},{1},{2},{3:0.0},{4:0.0},", maxNPP, maxBiomass, (int)siteBiomass, (cohort.WoodBiomass + cohort.LeafBiomass), SiteVars.SoilTemperature[site]); Outputs.CalibrateLog.Write("{0:0.00},{1:0.00},", woodNPP, leafNPP); } return(new double[2] { woodNPP, leafNPP }); }
//--------------------------------------------------------------------- // New method for calculating competition limits. // Iterates through cohorts, assigning each a competitive efficiency private static double CalculateCompetition(ActiveSite site, ICohort cohort) { double competitionPower = 0.95; double CMultiplier = Math.Max(Math.Pow(cohort.Biomass, competitionPower), 1.0); double CMultTotal = CMultiplier; //PlugIn.ModelCore.Log.WriteLine("Competition: spp={0}, age={1}, CMultiplier={2:0}, CMultTotal={3:0}.", cohort.Species.Name, cohort.Age, CMultiplier, CMultTotal); foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site]) { foreach (ICohort xcohort in speciesCohorts) { if (xcohort.Age+1 != cohort.Age || xcohort.Species.Index != cohort.Species.Index) { double tempMultiplier = Math.Max(Math.Pow(xcohort.Biomass, competitionPower), 1.0); CMultTotal += tempMultiplier; //PlugIn.ModelCore.Log.WriteLine("Competition: spp={0}, age={1}, CMultiplier={2:0}, CMultTotal={3:0}.", xcohort.Species.Name, xcohort.Age, tempMultiplier, CMultTotal); } } } double Cfraction = CMultiplier / CMultTotal; //PlugIn.ModelCore.Log.WriteLine("Competition: spp={0}, age={1}, CMultiplier={2:0}, CMultTotal={3:0}, CI={4:0.00}.", cohort.Species.Name, cohort.Age, CMultiplier, CMultTotal, Cfraction); return Cfraction; }
public static void PartitionResidue( double inputMass, double inputDecayValue, double inputCNratio, double fracLignin, double ratioCNstructural, //double CNratiofrass, LayerName name, LayerType type, ActiveSite site) { double cAddToMetabolic, cAddToStructural, directAbsorb; double NAddToMetabolic, NAddToStructural, Npart; double fracStructuralLignin, fracMetabolic, fracN, ratioCNtotal, ratioLigninN; double totalNitrogen = 0.0; double totalC = inputMass * 0.47; if (totalC < 0.0000001) { //PlugIn.ModelCore.UI.WriteLine("C inputs to litter layer below threshold"); return; } // ...For each mineral element.. // ...Compute amount of element in residue. Npart = totalC / inputCNratio; // ...Direct absorption of mineral element by residue // (mineral will be transferred to donor compartment // and then partitioned into structural and metabolic // using flow routines.) // ...If minerl(SRFC,iel) is negative then directAbsorb = zero. if (SiteVars.MineralN[site] <= 0.0) { directAbsorb = 0.0; } else { directAbsorb = SiteVars.MineralN[site] * OtherData.FractionSurfNAbsorbed * System.Math.Max(totalC / OtherData.ResidueMaxDirectAbsorb, 1.0); } // ...If C/N ratio is too low, transfer just enough to make // C/N of residue = damrmn if (Npart + directAbsorb <= 0.0) { ratioCNtotal = 0.0; } else { ratioCNtotal = totalC / (Npart + directAbsorb); } if (ratioCNtotal < OtherData.MinResidueCN) { directAbsorb = (totalC / OtherData.MinResidueCN) - Npart; } if (directAbsorb < 0.0) { directAbsorb = 0.0; } if (directAbsorb > SiteVars.MineralN[site]) { directAbsorb = SiteVars.MineralN[site]; } SiteVars.MineralN[site] -= directAbsorb; totalNitrogen = directAbsorb + Npart; // ...Partition carbon into structural and metabolic fraction of // residue (including direct absorption) which is nitrogen fracN = totalNitrogen / inputMass; // (totalC * 2.0); // ...Lignin/nitrogen ratio of residue ratioLigninN = fracLignin / fracN; // METABOLIC calculations // ...Carbon added to metabolic // Compute the fraction of carbon that goes to metabolic. fracMetabolic = OtherData.MetaStructSplitIntercept - OtherData.MetaStructSplitSlope * ratioLigninN; // ...Make sure the fraction of residue which is lignin isn't // greater than the fraction which goes to structural. -rm 12/91 if (fracLignin > (1.0 - fracMetabolic)) { fracMetabolic = (1.0 - fracLignin); } // ...Make sure at least 1% goes to metabolic if (fracMetabolic < 0.20) { fracMetabolic = 0.20; } // ...Compute amounts to flow cAddToMetabolic = totalC * fracMetabolic; if (cAddToMetabolic < 0.0) { cAddToMetabolic = 0.0; } if ((int)type == (int)LayerType.Surface) { SiteVars.SurfaceMetabolic[site].Carbon += cAddToMetabolic; } else { SiteVars.SoilMetabolic[site].Carbon += cAddToMetabolic; } // STRUCTURAL calculations cAddToStructural = totalC - cAddToMetabolic; // ...Adjust lignin content of structural. // ...fracStructuralLignin is the fraction of incoming structural residue // which is lignin; restricting it to a maximum of .8 fracStructuralLignin = fracLignin / (cAddToStructural / totalC); if ((int)type == (int)LayerType.Surface && cAddToMetabolic <= 0.0) { //PlugIn.ModelCore.UI.WriteLine(" SURFACE cAddToMetabolic={0}.", cAddToMetabolic); // ...Changed allowable maximum fraction from .6 to 1.0 -lh 1/93 if (fracStructuralLignin > 1.0) { fracStructuralLignin = 1.0; } } if ((int)type == (int)LayerType.Surface) { SiteVars.SurfaceStructural[site].Carbon += cAddToStructural; } else { SiteVars.SoilStructural[site].Carbon += cAddToStructural; } // ...Adjust lignin in Structural Layers // adjlig(strucc(lyr), fracStructuralLignin, cAddToStructural, strlig(lyr)); Layer structuralLayer; if ((int)type == (int)LayerType.Surface) { structuralLayer = SiteVars.SurfaceStructural[site]; } else { structuralLayer = SiteVars.SoilStructural[site]; } structuralLayer.AdjustLignin(cAddToStructural, fracStructuralLignin); // Don't adjust litter decay rate: the base decay rate is // always 1.0 // AdjustDecayRate(); // ...Partition mineral elements into structural and metabolic // ...Flow into structural // ...Flow into metabolic NAddToStructural = cAddToStructural / ratioCNstructural; //RATIO CN STRUCTURAL from species data NAddToMetabolic = totalNitrogen - NAddToStructural; if ((int)type == (int)LayerType.Surface) { SiteVars.SurfaceStructural[site].Nitrogen += NAddToStructural; SiteVars.SurfaceMetabolic[site].Nitrogen += NAddToMetabolic; } else { SiteVars.SoilStructural[site].Nitrogen += NAddToStructural; SiteVars.SoilMetabolic[site].Nitrogen += NAddToMetabolic; //PlugIn.ModelCore.UI.WriteLine(" N added to Structural Soil: {0}.", NAddToStructural); } return; //} }
// -------------------------------------------------- public void DecomposeLignin(double totalCFlow, ActiveSite site) // Originally from declig.f for decomposition of compartment lignin { double carbonToSOM1; //Net C flow to SOM1 double carbonToSOM2; //Net C flow to SOM2 double litterC = this.Carbon; double ratioCN = litterC / this.Nitrogen; //See if Layer can decompose to SOM1. //If it can decompose to SOM1, it will also go to SOM2. //If it can't decompose to SOM1, it can't decompose at all. //If Wood Object can decompose if (this.DecomposePossible(ratioCN, SiteVars.MineralN[site])) { // Decompose Wood Object to SOM2 // ----------------------- // Gross C flow to som2 carbonToSOM2 = totalCFlow * this.FractionLignin; //MicrobialRespiration associated with decomposition to som2 double co2loss = carbonToSOM2 * OtherData.LigninRespirationRate; this.Respiration(co2loss, site); //Net C flow to SOM2 double netCFlow = carbonToSOM2 - co2loss; // Partition and schedule C flows this.TransferCarbon(SiteVars.SOM2[site], netCFlow); this.TransferNitrogen(SiteVars.SOM2[site], netCFlow, litterC, ratioCN, site); //PlugIn.ModelCore.UI.WriteLine("Decompose1. MineralN={0:0.00}.", SiteVars.MineralN[site]); // ---------------------------------------------- // Decompose Wood Object to SOM1 // Gross C flow to som1 carbonToSOM1 = totalCFlow - carbonToSOM2 - co2loss; //MicrobialRespiration associated with decomposition to som1 if (this.Type == LayerType.Surface) { co2loss = carbonToSOM1 * OtherData.StructuralToCO2Surface; } else { co2loss = carbonToSOM1 * OtherData.StructuralToCO2Soil; } this.Respiration(co2loss, site); //Net C flow to SOM1 carbonToSOM1 -= co2loss; if (this.Type == LayerType.Surface) { this.TransferCarbon(SiteVars.SOM1surface[site], carbonToSOM1); this.TransferNitrogen(SiteVars.SOM1surface[site], carbonToSOM1, litterC, ratioCN, site); } else { this.TransferCarbon(SiteVars.SOM1soil[site], carbonToSOM1); this.TransferNitrogen(SiteVars.SOM1soil[site], carbonToSOM1, litterC, ratioCN, site); } } //PlugIn.ModelCore.UI.WriteLine("Decompose2. MineralN={0:0.00}.", SiteVars.MineralN[site]); return; }
public void TransferNitrogen(Layer destination, double CFlow, double totalC, double ratioCNtoDestination, ActiveSite site) { // this is the source. double mineralNFlow = 0.0; //...N flow is proportional to C flow. double NFlow = this.Nitrogen * CFlow / totalC; //...This was added to avoid a 0/0 error on the pc. if (CFlow <= 0.0 || NFlow <= 0.0) { return; } if ((NFlow - this.Nitrogen) > 0.01) { //PlugIn.ModelCore.UI.WriteLine(" Transfer N: N flow > source N."); //PlugIn.ModelCore.UI.WriteLine(" NFlow={0:0.000}, SourceN={1:0.000}", NFlow, this.Nitrogen); //PlugIn.ModelCore.UI.WriteLine(" CFlow={0:0.000}, totalC={1:0.000}", CFlow, totalC); //PlugIn.ModelCore.UI.WriteLine(" this.Name={0}, this.Type={1}", this.Name, this.Type); //PlugIn.ModelCore.UI.WriteLine(" dest.Name ={0}, dest.Type ={1}", destination.Name, destination.Type); //PlugIn.ModelCore.UI.WriteLine(" ratio CN to dest={0}", ratioCNtoDestination); } //...If C/N of Box A > C/N of new material entering Box B if ((CFlow / NFlow) > ratioCNtoDestination) { //...IMMOBILIZATION occurs. //...Compute the amount of N immobilized. // since ratioCNtoDestination = netCFlow / (Nflow + immobileN), // where immobileN is the extra N needed from the mineral pool double immobileN = (CFlow / ratioCNtoDestination) - NFlow; //PlugIn.ModelCore.UI.WriteLine(" CFlow={0:0.000}, totalC={1:0.000}", CFlow, totalC); // PlugIn.ModelCore.UI.WriteLine(" this.Name={0}, this.Type={1}", this.Name, this.Type); //PlugIn.ModelCore.UI.WriteLine(" NFlow={0:0.000}, SourceN={1:0.000},CNdestination={2:0}", NFlow, this.Nitrogen,ratioCNtoDestination); //PlugIn.ModelCore.UI.WriteLine("CalculatingImmobil. MineralN={0:0.00}.", SiteVars.MineralN[site]); //...Schedule flow from Box A to Box B (outofa) //flow(anps,bnps,time,outofa); this.Nitrogen -= NFlow; destination.Nitrogen += NFlow; //PlugIn.ModelCore.UI.WriteLine("NFlow. MineralN={0:0.00}, ImmobileN={1:0.000}.", SiteVars.MineralN[site],immobileN); // Schedule flow from mineral pool to Box B (immobileN) // flow(labile,bnps,time,immflo); //Don't allow mineral N to go to zero or negative.- ML if (immobileN > SiteVars.MineralN[site]) { immobileN = SiteVars.MineralN[site] - 0.01; //leave some small amount of mineral N } SiteVars.MineralN[site] -= immobileN; //PlugIn.ModelCore.UI.WriteLine("AfterImmobil. MineralN={0:0.00}.", SiteVars.MineralN[site]); destination.Nitrogen += immobileN; //PlugIn.ModelCore.UI.WriteLine("AdjustImmobil. MineralN={0:0.00}.", SiteVars.MineralN[site]); //PlugIn.ModelCore.UI.WriteLine(" TransferN immobileN={0:0.000}, C={1:0.000}, N={2:0.000}, ratioCN={3:0.000}.", immobileN, CFlow, NFlow, ratioCNtoDestination); //PlugIn.ModelCore.UI.WriteLine(" source={0}-{1}, destination={2}-{3}.", this.Name, this.Type, destination.Name, destination.Type); //...Return mineralization value. mineralNFlow = -1 * immobileN; //PlugIn.ModelCore.UI.WriteLine("MineralNflow. MineralN={0:0.00}.", SiteVars.MineralN[site]); } else //...MINERALIZATION occurs //...Schedule flow from Box A to Box B { //PlugIn.ModelCore.UI.WriteLine(" Transfer Nitrogen Min."); double mineralizedN = (CFlow / ratioCNtoDestination); this.Nitrogen -= mineralizedN; destination.Nitrogen += mineralizedN; //...Schedule flow from Box A to mineral pool mineralNFlow = NFlow - mineralizedN; if ((mineralNFlow - this.Nitrogen) > 0.01) { //PlugIn.ModelCore.UI.WriteLine(" Transfer N mineralization: mineralN > source N."); //PlugIn.ModelCore.UI.WriteLine(" MineralNFlow={0:0.000}, SourceN={1:0.000}", mineralNFlow, this.Nitrogen); //PlugIn.ModelCore.UI.WriteLine(" CFlow={0:0.000}, totalC={1:0.000}", CFlow, totalC); //PlugIn.ModelCore.UI.WriteLine(" this.Name={0}, this.Type={1}", this.Name, this.Type); // PlugIn.ModelCore.UI.WriteLine(" dest.Name ={0}, dest.Type ={1}", destination.Name, destination.Type); //PlugIn.ModelCore.UI.WriteLine(" ratio CN to dest={0}", ratioCNtoDestination); } this.Nitrogen -= mineralNFlow; SiteVars.MineralN[site] += mineralNFlow; //PlugIn.ModelCore.UI.WriteLine(" this.Name={0}, this.Type={1}", this.Name, this.Type); //PlugIn.ModelCore.UI.WriteLine("IfMinOccurs. MineralN={0:0.00}.", SiteVars.MineralN[site]); //PlugIn.ModelCore.UI.WriteLine(" TransferN NFlow={0:0.000}, mineralizedN = {1:0.000}, N mineralalization = {1:0.000}", NFlow, mineralizedN, mineralNFlow); //PlugIn.ModelCore.UI.WriteLine(" Source: this.Name={0}, this.Type={1}", this.Name, this.Type); } if (mineralNFlow > 0) { SiteVars.GrossMineralization[site] += mineralNFlow; } //...Net mineralization this.NetMineralization += mineralNFlow; //PlugIn.ModelCore.UI.WriteLine(" this.Nitrogen={0:0.000}.", this.Nitrogen); //PlugIn.ModelCore.UI.WriteLine("AfterMinOccurs. MineralN={0:0.00}.", SiteVars.MineralN[site]); return; }
//--------------------------------------------------------------------------- //... Originally from pprdwc(wc,x,pprpts) of CENTURY //...This funtion returns a value for potential plant production // due to water content. Basically you have an equation of a // line with a moveable y-intercept depending on the soil type. // The value passed in for x is ((avh2o(1) + prcurr(month) + irract)/pet) // pprpts(1): The minimum ratio of available water to pet which // would completely limit production assuming wc=0. // pprpts(2): The effect of wc on the intercept, allows the // user to increase the value of the intercept and // thereby increase the slope of the line. // pprpts(3): The lowest ratio of available water to pet at which // there is no restriction on production. private static double calculateWater_Limit(ActiveSite site, IEcoregion ecoregion, ISpecies species) { // Ratio_AvailWaterToPET used to be pptprd and WaterLimit used to be pprdwc double Ratio_AvailWaterToPET = 0.0; double waterContent = SiteVars.SoilFieldCapacity[site] - SiteVars.SoilWiltingPoint[site]; //ClimateRegionData.FieldCapacity[ecoregion] - ClimateRegionData.WiltingPoint[ecoregion]; // Difference between two fractions (FC - WP), not the actual water content, per se. double tmin = ClimateRegionData.AnnualWeather[ecoregion].MonthlyMinTemp[Century.Month]; double H2Oinputs = ClimateRegionData.AnnualWeather[ecoregion].MonthlyPrecip[Century.Month]; //rain + irract; double pet = ClimateRegionData.AnnualWeather[ecoregion].MonthlyPET[Century.Month]; //PlugIn.ModelCore.UI.WriteLine("pet={0}, waterContent={1}, precip={2}.", pet, waterContent, H2Oinputs); if (pet >= 0.01) { // Trees are allowed to access the whole soil profile -rm 2/97 // pptprd = (avh2o(1) + tmoist) / pet // pptprd = (SiteVars.AvailableWater[site] + H2Oinputs) / pet; Ratio_AvailWaterToPET = (SiteVars.AvailableWater[site] / pet); //Modified by ML so that we weren't double-counting precip as in above equation //PlugIn.ModelCore.UI.WriteLine("RatioAvailWaterToPET={0}, AvailableWater={1}.", Ratio_AvailWaterToPET, SiteVars.AvailableWater[site]); } else { Ratio_AvailWaterToPET = 0.01; } //...The equation for the y-intercept (intcpt) is A+B*WC. A and B // determine the effect of soil texture on plant production based // on moisture. //...Old way: // intcpt = 0.0 + 1.0 * wc // The second point in the equation is (.8,1.0) // slope = (1.0-0.0)/(.8-intcpt) // pprdwc = 1.0+slope*(x-.8) //PPRPTS naming convention is imported from orginal Century model. Now replaced with 'MoistureCurve' to be more intuitive //...New way (with updated naming convention): double moisturecurve1 = OtherData.MoistureCurve1; double moisturecurve2 = FunctionalType.Table[SpeciesData.FuncType[species]].MoistureCurve2; double moisturecurve3 = FunctionalType.Table[SpeciesData.FuncType[species]].MoistureCurve3; double intcpt = moisturecurve1 + (moisturecurve2 * waterContent); double slope = 1.0 / (moisturecurve3 - intcpt); double WaterLimit = 1.0 + slope * (Ratio_AvailWaterToPET - moisturecurve3); if (WaterLimit > 1.0) { WaterLimit = 1.0; } if (WaterLimit < 0.01) { WaterLimit = 0.01; } //PlugIn.ModelCore.UI.WriteLine("Intercept={0}, Slope={1}, WaterLimit={2}.", intcpt, slope, WaterLimit); if (PlugIn.ModelCore.CurrentTime > 0 && OtherData.CalibrateMode) { Outputs.CalibrateLog.Write("{0:0.00},", SiteVars.AvailableWater[site]); } return(WaterLimit); }
//--------------------------------------------------------------------- public void Grow(ushort years, ActiveSite site, int?successionTimestep, ICore mCore) { }
public RemoveAgeBetween5And30_Biomass(ActiveSite currentSite) : base(currentSite) { }
//--------------------------------------------------------------------- /// <summary> /// Determines if a species can establish on a site. /// This is a Delegate method to base succession. /// </summary> public bool Establish(ISpecies species, ActiveSite site) { double establishProbability = Establishment.Calculate(species, site); return(modelCore.GenerateUniform() < establishProbability); }
public void Grow(ActiveSite site, bool isSuccessionTimestep) { throw new System.Exception("Incompatibility issue"); }
//--------------------------------------------------------------------- protected override void InitializeSite(ActiveSite site, ICommunity initialCommunity) { InitialBiomass initialBiomass = InitialBiomass.Compute(site, initialCommunity); cohorts[site] = initialBiomass.Cohorts.Clone(); Dead.Pools.Woody[site] = initialBiomass.DeadWoodyPool; Dead.Pools.NonWoody[site] = initialBiomass.DeadNonWoodyPool; }
//--------------------------------------------------------------------- /// <summary> /// The mortality caused by development processes, /// including self-thinning and loss of branches, twigs, etc. /// See equation 5 in Scheller and Mladenoff, 2004. /// </summary> private double ComputeGrowthMortality(ICohort cohort, ActiveSite site, int siteBiomass) { //double percentDefoliation = CohortDefoliation.Compute(cohort, site, siteBiomass); //const double y0 = 0.01; //const double r = 0.08; double maxANPP = SpeciesData.ANPP_MAX_Spp[cohort.Species][ecoregion]; double M_BIO = 0.0; //Michaelis-Menton function: if (B_AP > 1.0) M_BIO = maxANPP * B_PM; else M_BIO = maxANPP * (2.0 * B_AP) / (1.0 + B_AP) * B_PM; //double M_BIO = maxANPP * // (y0 / (y0 + (1 - y0) * Math.Exp(-r / y0 * B_AP))) * // B_PM; // Mortality should not exceed the amount of living biomass M_BIO = Math.Min(cohort.Biomass, M_BIO); // Calculated actual ANPP can not exceed the limit set by the // maximum ANPP times the ratio of potential to maximum biomass. // This down regulates actual ANPP by the available growing space. M_BIO = Math.Min(maxANPP * B_PM, M_BIO); if (growthReduction > 0) M_BIO *= (1.0 - growthReduction); return M_BIO; }
//--------------------------------------------------------------------- public override byte ComputeShade(ActiveSite site) { return LivingBiomass.ComputeShade(site); }
/// <summary> /// Default method for computing how much a cohort is defoliated at /// a site. /// </summary> /// <returns> /// 0% /// </returns> public static double Compute(ICohort cohort, ActiveSite site) //int siteBiomass) { return(0.0); }
//--------------------------------------------------------------------- /// <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, int siteBiomass, int prevYearSiteMortality) { ecoregion = PlugIn.ModelCore.Ecoregion[site]; // 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, prevYearSiteMortality); // Age mortality is discounted from ANPP to prevent the over- // estimation of mortality. ANPP cannot be negative. actualANPP = Math.Max(1, actualANPP - mortalityAge); SiteVars.AGNPP[site] += actualANPP; // 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; } int deltaBiomass = (int)(actualANPP - totalMortality - defoliationLoss); double newBiomass = cohort.Biomass + (double)deltaBiomass; double totalLitter = UpdateDeadBiomass(cohort, actualANPP, totalMortality, site, newBiomass); //CalculateCohortLAI(cohort, actualANPP, newBiomass, site); CalculateCohortLight(cohort, actualANPP, newBiomass, site); if (PlugIn.CalibrateMode && PlugIn.ModelCore.CurrentTime > 0) { PlugIn.ModelCore.Log.WriteLine("Yr={0}. Calculate Delta Biomass...", (PlugIn.ModelCore.CurrentTime+SubYear)); PlugIn.ModelCore.Log.WriteLine("Yr={0}. Spp={1}, Age={2}.", (PlugIn.ModelCore.CurrentTime+SubYear), cohort.Species.Name, cohort.Age); PlugIn.ModelCore.Log.WriteLine("Yr={0}. ANPPact={1:0.0}, Mtotal={2:0.0}, litter={3:0.00}.", (PlugIn.ModelCore.CurrentTime+SubYear), actualANPP, totalMortality, totalLitter); PlugIn.ModelCore.Log.WriteLine("Yr={0}. DeltaB={1:0.0}, CohortB={2}, Bsite={3}", (PlugIn.ModelCore.CurrentTime+SubYear), deltaBiomass, cohort.Biomass, (int) siteBiomass); } return deltaBiomass; }
//--------------------------------------------------------------------- /// <summary> /// Re-enables establishment at a site for a list of species /// </summary> public static void EnableEstablishment(ActiveSite site) { noEstablish[site] = false; }
//--------------------------------------------------------------------- // Added 10/5/09 - BRM // Replaces CalculateCohortLAI private static void CalculateCohortLight(ICohort cohort, double actualANPP, double newBiomass, ActiveSite site) { ISpecies species = cohort.Species; double pctBioMaxLAI = SpeciesData.PctBioMaxLAI[species]; double cohortBiomass = newBiomass; IEcoregion ecoregion = PlugIn.ModelCore.Ecoregion[site]; //double maxBiomass = SpeciesData.B_MAX_Spp[species][ecoregion]; double maxBiomass = EcoregionData.B_MAX[ecoregion]; double maxlai = SpeciesData.MAXLAI[species]; double LAIactual = 0; double pctBiomass = (cohortBiomass / maxBiomass) * 100; if (pctBiomass >= pctBioMaxLAI) { LAIactual = maxlai; } else { double slope = 100.0 / pctBioMaxLAI; double pctLAI = pctBiomass * slope; LAIactual = (pctLAI * maxlai) / 100; } double cohortLightExt = SpeciesData.LightExtinctionCoeff[species] * LAIactual; double cohortLightTrans = Math.Exp(-1.0 * cohortLightExt); SiteVars.LightTrans[site] *= cohortLightTrans; //Combine cohortLightTrans for all cohorts on the site }
public static void PartitionResidue( double inputMass, double inputDecayValue, double inputCNratio, double fracLignin, LayerName name, LayerType type, ActiveSite site) { double directAbsorb = 0.0; double ratioCNtotal = 0.0; double totalNitrogen = 0.0; // from dry matter to C, 0.47 ratio double totalC = inputMass * 0.47; if (totalC < 0.0000001) { return; } // ...For each mineral element.. // ...Compute amount of element in residue. double Npart = totalC / inputCNratio; //PlugIn.ModelCore.UI.WriteLine(" totalCadded={0:0.00}, inputCNratio={1}, name={2}, type={3}.", totalC, inputCNratio, name, type); // ...Direct absorption of mineral element by residue // (mineral will be transferred to donor compartment // and then partitioned into structural and metabolic // using flow routines.) // ...If minerl(SRFC,iel) is negative then directAbsorb = zero. if (SiteVars.MineralN[site] <= 0.0) { directAbsorb = 0.0; } else { directAbsorb = OtherData.FractionSurfNAbsorbed * SiteVars.MineralN[site] * System.Math.Max(totalC / OtherData.ResidueMaxDirectAbsorb, 1.0); } // ...If C/N ratio is too low, transfer just enough to make // C/N of residue = damrmn if (Npart + directAbsorb <= 0.0) { ratioCNtotal = 0.0; } else { ratioCNtotal = totalC / (Npart + directAbsorb); } if (ratioCNtotal < OtherData.MinResidueCN) { directAbsorb = (totalC / OtherData.MinResidueCN) - Npart; } if (directAbsorb < 0.0) { directAbsorb = 0.0; } if (directAbsorb > SiteVars.MineralN[site]) { directAbsorb = SiteVars.MineralN[site]; } SiteVars.MineralN[site] -= directAbsorb; totalNitrogen = directAbsorb + Npart; //PlugIn.ModelCore.UI.WriteLine(" totalNadded={0:0.00}, totalC={1:0.0}, inputCNratio={2}, name={3}, type={4}.", totalNitrogen, totalC, inputCNratio, name, type); if ((int)name == (int)LayerName.Wood) { SiteVars.SurfaceDeadWood[site].Carbon += totalC; SiteVars.SurfaceDeadWood[site].Nitrogen += totalNitrogen; SiteVars.SurfaceDeadWood[site].AdjustLignin(totalC, fracLignin); SiteVars.SurfaceDeadWood[site].AdjustDecayRate(totalC, inputDecayValue); } else // Dead Coarse Roots { SiteVars.SoilDeadWood[site].Carbon += totalC; SiteVars.SoilDeadWood[site].Nitrogen += totalNitrogen; SiteVars.SoilDeadWood[site].AdjustLignin(totalC, fracLignin); SiteVars.SoilDeadWood[site].AdjustDecayRate(totalC, inputDecayValue); } return; }
//--------------------------------------------------------------------- private double ComputeActualANPP(ICohort cohort, ActiveSite site, int siteBiomass, int prevYearSiteMortality) { growthReduction = CohortGrowthReduction.Compute(cohort, site, siteBiomass); double growthShape = SpeciesData.GrowthCurveShapeParm[cohort.Species]; double cohortBiomass = cohort.Biomass; double capacityReduction = 1.0; if(SiteVars.CapacityReduction != null && SiteVars.CapacityReduction[site] > 0) { capacityReduction = 1.0 - SiteVars.CapacityReduction[site]; if(PlugIn.CalibrateMode) PlugIn.ModelCore.Log.WriteLine("Yr={0}. Capacity Remaining={1:0.00}, Spp={2}, Age={3} B={4}.", (PlugIn.ModelCore.CurrentTime+SubYear), capacityReduction, cohort.Species.Name, cohort.Age, cohort.Biomass); } double maxBiomass = SpeciesData.B_MAX_Spp[cohort.Species][ecoregion] * capacityReduction; double maxANPP = SpeciesData.ANPP_MAX_Spp[cohort.Species][ecoregion]; // Potential biomass, equation 3 in Scheller and Mladenoff, 2004 double potentialBiomass = Math.Max(1.0, maxBiomass - siteBiomass + cohortBiomass); // Species can use new space from mortality immediately // but not in the case of capacity reduction due to harvesting. if(capacityReduction >= 1.0) potentialBiomass = Math.Max(potentialBiomass, prevYearSiteMortality); // Ratio of cohort's actual biomass to potential biomass B_AP = cohortBiomass / potentialBiomass; double indexC = CalculateCompetition(site, cohort); if ((indexC <= 0.0 && cohortBiomass > 0) || indexC > 1.0) { PlugIn.ModelCore.Log.WriteLine("Error: Competition Index [{0:0.00}] is <= 0.0 or > 1.0", indexC); PlugIn.ModelCore.Log.WriteLine("Yr={0}. SPECIES={1}, AGE={2}, B={3}", (PlugIn.ModelCore.CurrentTime + SubYear), cohort.Species.Name, cohort.Age, cohortBiomass); throw new ApplicationException("Application terminating."); } // Ratio of cohort's potential biomass to maximum biomass. The // ratio cannot be exceed 1. double indexOldSchool = Math.Min(1.0, potentialBiomass / maxBiomass); double initialMultiplier = (CanopyLightExtinction == 0.0 ? indexC : 1.0); double indexLightC = initialMultiplier * Math.Exp(CanopyLightExtinction); B_PM = indexLightC; PlugIn.ModelCore.Log.WriteLine("indexC={0:0.00}, lightIndexC={1:0.00}, OldSchool={2:0.00}.", indexC, indexLightC, indexOldSchool); // Actual ANPP: equation (4) from Scheller & Mladenoff, 2004. // Constants k1 and k2 control whether growth rate declines with // age. Set to default = 1. //double actualANPP = maxANPP * Math.E * B_AP * Math.Exp(-1 * B_AP) * B_PM; double actualANPP = maxANPP * Math.E * Math.Pow(B_AP, growthShape) * Math.Exp(-1 * Math.Pow(B_AP, growthShape)) * B_PM; // Calculated actual ANPP can not exceed the limit set by the // maximum ANPP times the ratio of potential to maximum biomass. // This down regulates actual ANPP by the available growing space. actualANPP = Math.Min(maxANPP * B_PM, actualANPP); if (growthReduction > 0) actualANPP *= (1.0 - growthReduction); double LAIactual = SpeciesData.MAXLAI[cohort.Species] * actualANPP / maxANPP; CanopyLightExtinction += (-1.0 * SpeciesData.LightExtinctionCoeff[cohort.Species] * LAIactual) * indexC; if(PlugIn.CalibrateMode && PlugIn.ModelCore.CurrentTime > 0) { PlugIn.ModelCore.Log.WriteLine("Yr={0}. Calculate ANPPactual...", (PlugIn.ModelCore.CurrentTime+SubYear)); PlugIn.ModelCore.Log.WriteLine("Yr={0}. Spp={1}, Age={2}.", (PlugIn.ModelCore.CurrentTime+SubYear), cohort.Species.Name, cohort.Age); PlugIn.ModelCore.Log.WriteLine("Yr={0}. MaxANPP={1}, MaxB={2:0}, Bsite={3}, Bcohort={4:0.0}.", (PlugIn.ModelCore.CurrentTime+SubYear), maxANPP, maxBiomass, (int) siteBiomass, cohort.Biomass); PlugIn.ModelCore.Log.WriteLine("Yr={0}. B_PM={1:0.0}, B_AP={2:0.0}, actualANPP={3:0.0}, capacityReduction={4:0.0}.", (PlugIn.ModelCore.CurrentTime+SubYear), B_PM, B_AP, actualANPP, capacityReduction); PlugIn.ModelCore.Log.WriteLine("Yr={0}. CanopyLightExtinction = {1:0.00}, LightTransmittance = {2:0.00}, LAIactual={3:0.0}.", (PlugIn.ModelCore.CurrentTime + SubYear), CanopyLightExtinction, B_PM, LAIactual); } return actualANPP; }
//--------------------------------------------------------------------- public static bool Algorithm(ISpecies species, ActiveSite site) { if (species.EffectiveSeedDist == EffectiveSeedDist.Universal) { return(UniversalDispersal.Algorithm(species, site)); } if (!Reproduction.SufficientLight(species, site) || !Reproduction.Establish(species, site)) { return(false); } if (Reproduction.MaturePresent(species, site)) { return(true); } int row = (int)site.Location.Row; int col = (int)site.Location.Column; int cellDiam = (int)Model.CellLength; int EffD = species.EffectiveSeedDist; int MaxD = species.MaxSeedDist; double ratio = 0.95; //the portion of the probability in the effective distance //lambda1 parameterized for effective distance double lambda1 = Math.Log((1 - ratio) / EffD); //lambda2 parameterized for maximum distance double lambda2 = Math.Log(0.01) / MaxD; double lowBound = 0, upBound = 0; //bool suitableDist=false;//flag to trigger if seed (plural) can get to a site based on distance probability double distanceProb = 0.0; int pixRange = Math.Max((int)((float)MaxD / (float)cellDiam), 1); int maxrow = (int)Math.Min(row + pixRange, Model.Landscape.Rows); int minrow = Math.Max(row - pixRange, 1); for (int i = minrow; i <= maxrow; i++) { int x1, x2; //float b = 5.0; findX1X2(out x1, out x2, col, row, i, pixRange); for (int j = x1; j <= x2; j++) { Location loc = new Location((uint)i, (uint)j); if (Model.Landscape.GetSite(loc, ref neighbor) && neighbor.IsActive) { if (Reproduction.MaturePresent(species, neighbor)) { float distance = (float)Math.Sqrt((float)((row - i) * (row - i) + (col - j) * (col - j))) * cellDiam; //Pythag //set lower boundary to the theoretical (straight-line) edge of parent cell lowBound = distance - cellDiam; if (lowBound < 0) { lowBound = 0; } //set upper boundary to the outer theoretical boundary of the cell upBound = distance; if (cellDiam <= EffD) { //Draw probabilities from either EffD or MaxD curves if (distance <= (float)EffD) { //BCW May 04 distanceProb = Math.Exp(lambda1 * lowBound) - Math.Exp(lambda1 * upBound); } else { //BCW May 04 distanceProb = (1 - ratio) * Math.Exp(lambda2 * (lowBound - EffD)) - (1 - ratio) * Math.Exp(lambda2 * (upBound - EffD)); } } else { if (distance <= cellDiam) { //Draw probabilities from both EffD and MaxD curves distanceProb = Math.Exp(lambda1 * lowBound) - (1 - ratio) * Math.Exp(lambda2 * (upBound - EffD)); } else { distanceProb = (1 - ratio) * Math.Exp(lambda2 * (lowBound - EffD)) - (1 - ratio) * Math.Exp(lambda2 * (upBound - EffD)); } } if (distanceProb > Landis.Util.Random.GenerateUniform()) // && frand() < l->probRepro(speciesNum)) Modified BCW May '04 { // success = sites(row,col)->addNewCohort(s, sa, 10); return(true); } } } // if neighor is active } // for each column, j, of current row in neighborhood } // for each row, i, in the neighborhood // Search failed. return(false); }
//--------------------------------------------------------------------- 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. double annualLeafANPP = actualANPP * leafFraction; ForestFloor.AddLitter(annualLeafANPP, species, site); // -------------------------------------------------------------------------------- // 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"); // Add mortality to dead biomass pools. // Coarse root mortality is assumed equal to aboveground woody mortality // mass is assumed 25% of aboveground wood (White et al. 2000, Niklas & Enquist 2002) if (mortality_wood > 0) { // Add mortality to dead biomass pools. ForestFloor.AddWoody((ushort)mortality_wood, species, site); } if (mortality_nonwood > 0) { ForestFloor.AddLitter(mortality_nonwood, species, site); } // Total mortality not including annual leaf litter M_noLeafLitter = (int)mortality_wood; return (annualLeafANPP + mortality_nonwood + mortality_wood); }
//--------------------------------------------------------------------- public ushort ComputeNonWoodyBiomass(ActiveSite site) { Percentage nonWoodyPercentage = Cohorts.BiomassCalculator.ComputeNonWoodyPercentage(this, site); return((ushort)(data.Biomass * nonWoodyPercentage)); }