/// <summary> /// Apply all updates from the ecological processes to the properties of the acting cohort and to the environment /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="actingCohort">The location of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current gird cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">A process tracker</param> public void UpdateAllEcology(GridCellCohortHandler gridCellCohorts, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, uint currentTimestep, ProcessTracker tracker) { // Apply cohort abundance changes UpdateAbundance(gridCellCohorts, actingCohort, deltas); // Apply cohort biomass changes UpdateBiomass(gridCellCohorts, actingCohort, deltas, currentTimestep, tracker, cellEnvironment); // Apply changes to the environmental biomass pools UpdatePools(cellEnvironment, deltas); }
/// <summary> /// Convert NPP estimate into biomass of an autotroph stock /// </summary> /// <param name="cellEnvironment">The environment of the current grid cell</param> /// <param name="gridCellStockHandler">The stock handler for the current stock</param> /// <param name="actingStock">The location of the stock to add biomass to</param> /// <param name="terrestrialNPPUnits">The units of the terrestrial NPP data</param> /// <param name="oceanicNPPUnits">The units of the oceanic NPP data</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="GlobalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="trackProcesses">Whether to output data describing the ecological processes</param> /// <param name="globalTracker">Whether to output data describing the global-scale environment</param> /// <param name="outputDetail">The level of output detail to use for the outputs</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="currentMonth">The current month in the model run</param> public void ConvertNPPToAutotroph(SortedList<string, double[]> cellEnvironment, GridCellStockHandler gridCellStockHandler, int[] actingStock, string terrestrialNPPUnits, string oceanicNPPUnits, uint currentTimestep, string GlobalModelTimeStepUnit, ProcessTracker trackProcesses, GlobalProcessTracker globalTracker, string outputDetail, bool specificLocations, uint currentMonth) { // Get NPP from the cell environment double NPP = cellEnvironment["NPP"][currentMonth]; // If NPP is a mssing value then set to zero if (NPP == cellEnvironment["Missing Value"][0]) NPP = 0.0; // Check that this is an ocean cell if (cellEnvironment["Realm"][0] == 2.0) { // Check that the units of oceanic NPP are gC per m2 per day Debug.Assert(oceanicNPPUnits == "gC/m2/day", "Oceanic NPP data are not in the correct units for this formulation of the model"); // Convert to g/cell/month NPP *= _MsqToKmSqConversion; // Multiply by cell area to get g/cell/day NPP *= cellEnvironment["Cell Area"][0]; // Convert to g wet matter, assuming carbon content of phytoplankton is 10% of wet matter NPP *= _PhytoplanktonConversionRatio; // Finally convert to g/cell/month and add to the stock totalbiomass NPP *= Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "day"); gridCellStockHandler[actingStock].TotalBiomass += NPP; if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations) { trackProcesses.TrackPrimaryProductionTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], NPP); } if (globalTracker.TrackProcesses) { globalTracker.RecordNPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0], NPP / cellEnvironment["Cell Area"][0]); } // If the biomass of the autotroph stock has been made less than zero (i.e. because of negative NPP) then reset to zero if (gridCellStockHandler[actingStock].TotalBiomass < 0.0) gridCellStockHandler[actingStock].TotalBiomass = 0.0; } // Else if neither on land or in the ocean else { Debug.Fail("This is not a marine cell!"); // Set the autotroph biomass to zero gridCellStockHandler[actingStock].TotalBiomass = 0.0; } Debug.Assert(gridCellStockHandler[actingStock].TotalBiomass >= 0.0, "stock negative"); }
/// <summary> /// Run reproduction /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimeStep">The current model time step</param> /// <param name="processTracker">An instance of ProcessTracker to hold diagnostics for eating</param> /// <param name="partial">Thread-locked variables for the parallelised version</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used for this model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string,Dictionary<string,double>> deltas , FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimeStep, ProcessTracker processTracker, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { // Holds the reproductive strategy of a cohort bool _Iteroparous = madingleyCohortDefinitions.GetTraitNames("reproductive strategy", actingCohort[0])=="iteroparity"; // Assign mass to reproductive potential Implementations["reproduction basic"].RunReproductiveMassAssignment(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimeStep, processTracker); // Run reproductive events. Note that we can't skip juveniles here as they could conceivably grow to adulthood and get enough biomass to reproduce in a single time step // due to other ecological processes Implementations["reproduction basic"].RunReproductionEvents(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimeStep, processTracker, ref partial, _Iteroparous, currentMonth); }
/// <summary> /// Run ecological processes that operate on stocks within a single grid cell /// </summary> ///<param name="gridCellStocks">The stocks in the current grid cell</param> ///<param name="actingStock">The acting stock</param> ///<param name="cellEnvironment">The stocks in the current grid cell</param> ///<param name="environmentalDataUnits">List of units associated with the environmental variables</param> ///<param name="humanNPPScenario">The human appropriation of NPP scenario to apply</param> ///<param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> ///<param name="currentTimeStep">The current model time step</param> ///<param name="burninSteps">The number of time steps to spin the model up for before applying human impacts</param> ///<param name="impactSteps">The number of time steps to apply human impacts for</param> ///<param name="globalModelTimeStepUnit">The time step unit used in the model</param> ///<param name="trackProcesses">Whether to track properties of ecological processes</param> ///<param name="tracker">An instance of the ecological process tracker</param> ///<param name="globalTracker">An instance of the global process tracker</param> ///<param name="currentMonth">The current model month</param> ///<param name="outputDetail">The level of detail to use in outputs</param> ///<param name="specificLocations">Whether to run the model for specific locations</param> ///<param name="impactCell">Whether this cell should have human impacts applied</param> public void RunWithinCellEcology(GridCellStockHandler gridCellStocks, int[] actingStock, SortedList<string, double[]> cellEnvironment, SortedList<string, string> environmentalDataUnits, Madingley.Common.ScenarioParameter humanNPPScenario, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimeStep, uint burninSteps, uint impactSteps, uint recoverySteps, uint instantStep, uint numInstantSteps, string globalModelTimeStepUnit, Boolean trackProcesses, ProcessTracker tracker, GlobalProcessTracker globalTracker, uint currentMonth, string outputDetail, bool specificLocations, Boolean impactCell) { if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "marine") { // Run the autotroph processor MarineNPPtoAutotrophStock.ConvertNPPToAutotroph(cellEnvironment, gridCellStocks, actingStock, environmentalDataUnits["LandNPP"], environmentalDataUnits["OceanNPP"], currentTimeStep, globalModelTimeStepUnit, tracker, globalTracker, outputDetail, specificLocations, currentMonth); } else if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "terrestrial") { // Run the dynamic plant model to update the leaf stock for this time step double WetMatterNPP = DynamicPlantModel.UpdateLeafStock(cellEnvironment, gridCellStocks, actingStock, currentTimeStep, madingleyStockDefinitions. GetTraitNames("leaf strategy", actingStock[0]).Equals("deciduous"), globalModelTimeStepUnit, tracker, globalTracker, currentMonth, outputDetail, specificLocations); /// <summary> double fhanpp = HANPP.RemoveHumanAppropriatedMatter(WetMatterNPP, cellEnvironment, humanNPPScenario, gridCellStocks, actingStock, currentTimeStep, burninSteps, impactSteps, recoverySteps, instantStep, numInstantSteps, impactCell, globalModelTimeStepUnit); // Apply human appropriation of NPP gridCellStocks[actingStock].TotalBiomass += WetMatterNPP * (1.0 - fhanpp); if (globalTracker.TrackProcesses) { globalTracker.RecordHANPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0], fhanpp); } if (gridCellStocks[actingStock].TotalBiomass < 0.0) gridCellStocks[actingStock].TotalBiomass = 0.0; } else { Debug.Fail("Stock must be classified as belonging to either the marine or terrestrial realm"); } }
/// <summary> /// Generate new cohorts from reproductive potential mass /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment of the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param> /// <param name="partial">Thread-locked variables</param> public void RunReproduction(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker, ref ThreadLockedParallelVariables partial) { // Check that the abundance in the cohort to produce is greater than or equal to zero Debug.Assert(_OffspringCohortAbundance >= 0.0, "Offspring abundance < 0"); // Get the adult and juvenile masses of the cohort to produce double[] OffspringProperties = GetOffspringCohortProperties(gridCellCohorts, actingCohort, madingleyCohortDefinitions); // Update cohort abundance in case juvenile mass has been altered _OffspringCohortAbundance = (_OffspringCohortAbundance * gridCellCohorts[actingCohort].JuvenileMass) / OffspringProperties[0]; //Create the offspring cohort Cohort OffspringCohort = new Cohort((byte)actingCohort[0], OffspringProperties[0], OffspringProperties[1], OffspringProperties[0], _OffspringCohortAbundance, (ushort)currentTimestep, ref partial.NextCohortIDThreadLocked); // Add the offspring cohort to the grid cell cohorts array gridCellCohorts[actingCohort[0]].Add(OffspringCohort); // If the cohort has never been merged with another cohort, then add it to the tracker for output as diagnostics if ((!gridCellCohorts[actingCohort].Merged) && tracker.TrackProcesses) tracker.RecordNewCohort((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, _OffspringCohortAbundance, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex); // Subtract all of the reproductive potential mass of the parent cohort, which has been used to generate the new // cohort, from the delta reproductive potential mass deltas["reproductivebiomass"]["reproduction"] -= (gridCellCohorts[actingCohort].IndividualReproductivePotentialMass); }
/// <summary> /// Run metabolism /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for metabolism</param> /// <param name="partial">Thread-locked variables</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used for the current model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { double Realm = cellEnvironment["Realm"][0]; if (madingleyCohortDefinitions.GetTraitNames("Heterotroph/Autotroph", gridCellCohorts[actingCohort].FunctionalGroupIndex) == "heterotroph") { if (madingleyCohortDefinitions.GetTraitNames("Endo/Ectotherm", gridCellCohorts[actingCohort].FunctionalGroupIndex) == "endotherm") { Implementations["basic endotherm"].RunMetabolism(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, currentMonth); } else { Implementations["basic ectotherm"].RunMetabolism(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, currentMonth); } } // If the process tracker is on and output detail is set to high and this cohort has not been merged yet, then record // the number of individuals that have died if (trackProcesses.TrackProcesses && (outputDetail == "high")) { trackProcesses.TrackTimestepMetabolism((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, actingCohort[0], cellEnvironment["Temperature"][currentMonth], deltas["biomass"]["metabolism"]); } }
/// <summary> /// Assigns ingested biomass from other ecological processes to reproductive potential mass /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param> public void RunReproductiveMassAssignment(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker) { // Biomass per individual in each cohort to be assigned to reproductive potential double _BiomassToAssignToReproductivePotential; // Net biomass change from other ecological functions this time step double NetBiomassFromOtherEcologicalFunctionsThisTimeStep; // Reset variable holding net biomass change of individuals in this cohort as a result of other ecological processes NetBiomassFromOtherEcologicalFunctionsThisTimeStep = 0.0; // Loop over all items in the biomass deltas foreach (var Biomass in deltas["biomass"]) { // Add the delta biomass to net biomass NetBiomassFromOtherEcologicalFunctionsThisTimeStep += Biomass.Value; } // If individual body mass after the addition of the net biomass from processes this time step will yield a body mass // greater than the adult body mass for this cohort, then assign the surplus to reproductive potential if ((gridCellCohorts[actingCohort].IndividualBodyMass + NetBiomassFromOtherEcologicalFunctionsThisTimeStep) > gridCellCohorts[actingCohort].AdultMass) { // Calculate the biomass for each individual in this cohort to be assigned to reproductive potential _BiomassToAssignToReproductivePotential = gridCellCohorts[actingCohort].IndividualBodyMass + NetBiomassFromOtherEcologicalFunctionsThisTimeStep - gridCellCohorts[actingCohort].AdultMass; // Check that a positive biomass is to be assigned to reproductive potential Debug.Assert(_BiomassToAssignToReproductivePotential >= 0.0, "Assignment of negative reproductive potential mass"); // If this is the first time reproductive potential mass has been assigned for this cohort, // then set the maturity time step for this cohort as the current model time step if (gridCellCohorts[actingCohort].MaturityTimeStep == uint.MaxValue) { gridCellCohorts[actingCohort].MaturityTimeStep = currentTimestep; // Track the generation length for this cohort if (tracker.TrackProcesses && (!gridCellCohorts[actingCohort].Merged)) tracker.TrackMaturity((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, gridCellCohorts[actingCohort].BirthTimeStep, gridCellCohorts[actingCohort].JuvenileMass, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex); } // Assign the specified mass to reproductive potential mass and remove it from individual biomass deltas["reproductivebiomass"]["reproduction"] += _BiomassToAssignToReproductivePotential; deltas["biomass"]["reproduction"] -= _BiomassToAssignToReproductivePotential; } else { // Cohort has not gained sufficient biomass to assign any to reproductive potential, so take no action } }
/// <summary> /// Assigns biomass from body mass to reproductive potential mass /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param> public void AssignMassToReproductivePotential(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker) { // If this is the first time reproductive potential mass has been assigned for this cohort, // then set the maturity time step for this cohort as the current model time step if (gridCellCohorts[actingCohort].MaturityTimeStep == uint.MaxValue) { gridCellCohorts[actingCohort].MaturityTimeStep = currentTimestep; // Track the generation length for this cohort if ((!gridCellCohorts[actingCohort].Merged) && tracker.TrackProcesses) tracker.TrackMaturity((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, gridCellCohorts[actingCohort].BirthTimeStep, gridCellCohorts[actingCohort].JuvenileMass, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex); } // Assign the specified mass to reproductive potential mass and remove it from individual biomass deltas["reproductivebiomass"]["reproduction"] += _BiomassToAssignToReproductivePotential; deltas["biomass"]["reproduction"] -= _BiomassToAssignToReproductivePotential; }
/// <summary> /// Update the leaf stock during a time step given the environmental conditions in the grid cell /// </summary> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingStock">The position of the acting stock in the array of grid cell stocks</param> /// <param name="currentTimeStep">The current model time step</param> /// <param name="deciduous">Whether the acting stock consists of deciduous leaves</param> /// <param name="GlobalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="tracker">Whether to track properties of the ecological processes</param> /// <param name="globalTracker">Whether to output data describing the global environment</param> /// <param name="currentMonth">The current model month</param> /// <param name="outputDetail">The level of detail to use in model outputs</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> public double UpdateLeafStock(SortedList<string, double[]> cellEnvironment, GridCellStockHandler gridCellStocks, int[] actingStock, uint currentTimeStep, bool deciduous, string GlobalModelTimeStepUnit, ProcessTracker tracker, GlobalProcessTracker globalTracker, uint currentMonth, string outputDetail, bool specificLocations) { // ESTIMATE ANNUAL LEAF CARBON FIXATION ASSUMING ENVIRONMENT THROUGHOUT THE YEAR IS THE SAME AS IN THIS MONTH // Calculate annual NPP double NPP = this.CalculateMiamiNPP(cellEnvironment["Temperature"].Average(), cellEnvironment["Precipitation"].Sum()); // Calculate fractional allocation to structural tissue double FracStruct = this.CalculateFracStruct(NPP); // Estimate monthly NPP based on seasonality layer NPP *= cellEnvironment["Seasonality"][currentMonth]; // Calculate leaf mortality rates double AnnualLeafMortRate; double MonthlyLeafMortRate; double TimeStepLeafMortRate; if (deciduous) { // Calculate annual deciduous leaf mortality AnnualLeafMortRate = this.CalculateDeciduousAnnualLeafMortality(cellEnvironment["Temperature"].Average()); // For deciduous plants monthly leaf mortality is weighted by temperature deviance from the average, to capture seasonal patterns double[] ExpTempDev = new double[12]; double SumExpTempDev = 0.0; double[] TempDev = new double[12]; double Weight; for (int i = 0; i < 12; i++) { TempDev[i] = cellEnvironment["Temperature"][i] - cellEnvironment["Temperature"].Average(); ExpTempDev[i] = Math.Exp(-TempDev[i] / 3); SumExpTempDev += ExpTempDev[i]; } Weight = ExpTempDev[currentMonth] / SumExpTempDev; MonthlyLeafMortRate = AnnualLeafMortRate * Weight; TimeStepLeafMortRate = MonthlyLeafMortRate * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); } else { // Calculate annual evergreen leaf mortality AnnualLeafMortRate = this.CalculateEvergreenAnnualLeafMortality(cellEnvironment["Temperature"].Average()); // For evergreen plants, leaf mortality is assumed to be equal throughout the year MonthlyLeafMortRate = AnnualLeafMortRate * (1.0 / 12.0); TimeStepLeafMortRate = MonthlyLeafMortRate * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); } // Calculate fine root mortality rate double AnnualFRootMort = this.CalculateFineRootMortalityRate(cellEnvironment["Temperature"][currentMonth]); // Calculate the NPP allocated to non-structural tissues double FracNonStruct = (1 - FracStruct); // Calculate the fractional allocation to leaves double FracLeaves = FracNonStruct * this.CalculateLeafFracAllocation(AnnualLeafMortRate, AnnualFRootMort); // Calculate the fractional allocation of NPP to evergreen plant matter double FracEvergreen = this.CalculateFracEvergreen(cellEnvironment["Fraction Year Frost"][0]); // Update NPP depending on whether the acting stock is deciduous or evergreen if (deciduous) { NPP *= (1 - FracEvergreen); } else { NPP *= FracEvergreen; } // Calculate the fire mortality rate double FireMortRate = this.CalculateFireMortalityRate(NPP, cellEnvironment["Fraction Year Fire"][0]); // Calculate the structural mortality rate double StMort = this.CalculateStructuralMortality(cellEnvironment["AET"][currentMonth] * 12); // Calculate leaf C fixation double LeafCFixation = NPP * FracLeaves; // Convert from carbon to leaf wet matter double WetMatterIncrement = this.ConvertToLeafWetMass(LeafCFixation, cellEnvironment["Cell Area"][0]); // Convert from the monthly time step used for this process to the global model time step unit WetMatterIncrement *= Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); // Add the leaf wet matter to the acting stock //gridCellStocks[actingStock].TotalBiomass += Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement); double NPPWetMatter = Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement); // If the processer tracker is enabled and output detail is high and the model is being run for specific locations, then track the biomass gained through primary production if (tracker.TrackProcesses && (outputDetail == "high") && specificLocations) { tracker.TrackPrimaryProductionTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement)); } if (globalTracker.TrackProcesses) { globalTracker.RecordNPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],(uint)actingStock[0], this.ConvertToLeafWetMass(NPP, cellEnvironment["Cell Area"][0]) * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month")/cellEnvironment["Cell Area"][0]); } // Calculate fractional leaf mortality double LeafMortFrac = 1 - Math.Exp(-TimeStepLeafMortRate); // Update the leaf stock biomass owing to the leaf mortality gridCellStocks[actingStock].TotalBiomass *= (1 - LeafMortFrac); NPPWetMatter *= (1 - LeafMortFrac); return (NPPWetMatter); }
/// <summary> /// Update the individual and reproductive body masses of the acting cohort according to the delta biomasses from the ecological processes /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">A process tracker</param> /// <param name="cellEnvironment">The cell environment</param> private void UpdateBiomass(GridCellCohortHandler gridCellCohorts, int[] actingCohort, Dictionary<string,Dictionary<string, double>> deltas, uint currentTimestep, ProcessTracker tracker, SortedList<string,double[]> cellEnvironment) { // Extract the biomass deltas from the sorted list of all deltas Dictionary<string, double> deltaBiomass = deltas["biomass"]; if (tracker.TrackProcesses) { // Calculate net growth of individuals in this cohort double growth = deltaBiomass["predation"] + deltaBiomass["herbivory"] + deltaBiomass["metabolism"]; tracker.TrackTimestepGrowth((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, gridCellCohorts[actingCohort].FunctionalGroupIndex, growth, deltaBiomass["metabolism"],deltaBiomass["predation"],deltaBiomass["herbivory"]); } // Get all keys from the biomass deltas sorted list string[] KeyStrings = deltaBiomass.Keys.ToArray(); // Variable to calculate net biomass change to check that cohort individual body mass will not become negative double NetBiomass = 0.0; // Loop over all biomass deltas foreach (string key in KeyStrings) { // Update net biomass change NetBiomass += deltaBiomass[key]; } double BiomassCheck=0.0; Boolean NetToBeApplied = true; // If cohort abundance is greater than zero, then check that the calculated net biomas will not make individual body mass become negative if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) > 0) { string output = "Biomass going negative, acting cohort: " + actingCohort[0].ToString() + ", " + actingCohort[1].ToString(); BiomassCheck = gridCellCohorts[actingCohort].IndividualBodyMass + NetBiomass; Debug.Assert((BiomassCheck).CompareTo(0.0) >= 0, output); } //Loop over all keys in the abundance deltas sorted list foreach (string key in KeyStrings) { // If cohort abundance is zero, then set cohort individual body mass to zero and reset the biomass delta to zero, // otherwise update cohort individual body mass and reset the biomass delta to zero if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) == 0) { gridCellCohorts[actingCohort].IndividualBodyMass = 0.0; deltaBiomass[key] = 0.0; } else { if (NetToBeApplied) { gridCellCohorts[actingCohort].IndividualBodyMass += NetBiomass; NetToBeApplied = false; } //gridCellCohorts[actingCohort].IndividualBodyMass += deltaBiomass[key]; deltaBiomass[key] = 0.0; } } // Check that individual body mass is still greater than zero Debug.Assert(gridCellCohorts[actingCohort].IndividualBodyMass.CompareTo(0.0) >= 0, "biomass < 0"); // If the current individual body mass is the largest that has been achieved by this cohort, then update the maximum achieved // body mass tracking variable for the cohort if (gridCellCohorts[actingCohort].IndividualBodyMass > gridCellCohorts[actingCohort].MaximumAchievedBodyMass) gridCellCohorts[actingCohort].MaximumAchievedBodyMass = gridCellCohorts[actingCohort].IndividualBodyMass; // Extract the reproductive biomass deltas from the sorted list of all deltas Dictionary<string, double> deltaReproductiveBiomass = deltas["reproductivebiomass"]; // Get all keys from the biomass deltas sorted list string[] KeyStrings2 = deltas["reproductivebiomass"].Keys.ToArray(); // Variable to calculate net reproductive biomass change to check that cohort individual body mass will not become negative double NetReproductiveBiomass = 0.0; // Loop over all reproductive biomass deltas foreach (string key in KeyStrings2) { // Update net reproductive biomass change NetReproductiveBiomass += deltaReproductiveBiomass[key]; } //Loop over all keys in the abundance deltas sorted list foreach (string key in KeyStrings2) { // If cohort abundance is zero, then set cohort reproductive body mass to zero and reset the biomass delta to zero, // otherwise update cohort reproductive body mass and reset the biomass delta to zero if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) == 0) { gridCellCohorts[actingCohort].IndividualReproductivePotentialMass = 0.0; deltaReproductiveBiomass[key] = 0.0; } else { gridCellCohorts[actingCohort].IndividualReproductivePotentialMass += deltaReproductiveBiomass[key]; deltaReproductiveBiomass[key] = 0.0; } } // Note that maturity time step is set in TReproductionBasic }
/// <summary> /// Run eating /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for eating</param> /// <param name="partial">Thread-locked variables</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used for the current model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { PreviousTrophicIndex = gridCellCohorts[actingCohort].TrophicIndex; //Reset this cohort's trohic index ready for calculation across its feeding this timetsstep gridCellCohorts[actingCohort].TrophicIndex = 0.0; // Get the nutrition source (herbivory, carnivory or omnivory) of the acting cohort string NutritionSource = madingleyCohortDefinitions.GetTraitNames("Nutrition source", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Switch to the appropriate eating process(es) given the cohort's nutrition source switch (NutritionSource) { case "herbivore": // Get the assimilation efficiency for herbivory for this cohort from the functional group definitions Implementations["revised herbivory"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the proportion of time spent eating for this cohort from the functional group definitions Implementations["revised herbivory"].ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; // Calculate the potential biomass available from herbivory if (cellEnvironment["Realm"][0] == 2.0) { Implementations["revised herbivory"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } else { Implementations["revised herbivory"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } // Run herbivory to apply changes in autotroph biomass from herbivory and add biomass eaten to the delta arrays Implementations["revised herbivory"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations, outputDetail, initialisation); break; case "carnivore": // Get the assimilation efficiency for predation for this cohort from the functional group definitions Implementations["revised predation"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); Implementations["revised predation"].ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; // Calculate the potential biomass available from predation if (cellEnvironment["Realm"][0] == 2.0) { Implementations["revised predation"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } else { Implementations["revised predation"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } // Run predation to apply changes in prey biomass from predation and add biomass eaten to the delta arrays Implementations["revised predation"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations, outputDetail, initialisation); break; case "omnivore": // Get the assimilation efficiency for predation for this cohort from the functional group definitions Implementations["revised predation"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the assimilation efficiency for herbivory for this cohort from the functional group definitions Implementations["revised herbivory"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the proportion of time spent eating and assign to both the herbivory and predation implementations double ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; Implementations["revised predation"].ProportionTimeEating = ProportionTimeEating; Implementations["revised herbivory"].ProportionTimeEating = ProportionTimeEating; // Calculate the potential biomass available from herbivory if (cellEnvironment["Realm"][0] == 2.0) { Implementations["revised herbivory"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } else { Implementations["revised herbivory"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } // Calculate the potential biomass available from predation if (cellEnvironment["Realm"][0] == 2.0) { Implementations["revised predation"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } else { Implementations["revised predation"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } // Calculate the total handling time for all expected kills from predation and expected plant matter eaten in herbivory TotalTimeToEatForOmnivores = Implementations["revised herbivory"].TimeUnitsToHandlePotentialFoodItems + Implementations["revised predation"].TimeUnitsToHandlePotentialFoodItems; // Assign this total time to the relevant variables in both herbviory and predation, so that actual amounts eaten are calculated correctly Implementations["revised herbivory"].TimeUnitsToHandlePotentialFoodItems = TotalTimeToEatForOmnivores; Implementations["revised predation"].TimeUnitsToHandlePotentialFoodItems = TotalTimeToEatForOmnivores; // Run predation to update prey cohorts and delta biomasses for the acting cohort Implementations["revised predation"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations, outputDetail, initialisation); // Run herbivory to update autotroph biomass and delta biomasses for the acting cohort Implementations["revised herbivory"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations, outputDetail, initialisation); break; default: // For nutrition source that are not supported, throw an error Debug.Fail("The model currently does not contain an eating model for nutrition source:" + NutritionSource); break; } // Check that the biomasses from predation and herbivory in the deltas is a number Debug.Assert(!double.IsNaN(deltas["biomass"]["predation"]), "BiomassFromEating is NaN"); Debug.Assert(!double.IsNaN(deltas["biomass"]["herbivory"]), "BiomassFromEating is NaN"); double biomassEaten = 0.0; if (madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex) > 0) { biomassEaten += (deltas["biomass"]["predation"] / madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex)); } if (madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex) > 0) { biomassEaten += (deltas["biomass"]["herbivory"] / madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex)); } if (biomassEaten > 0.0) { gridCellCohorts[actingCohort].TrophicIndex = 1 + (gridCellCohorts[actingCohort].TrophicIndex / (biomassEaten * gridCellCohorts[actingCohort].CohortAbundance)); } else { gridCellCohorts[actingCohort].TrophicIndex = PreviousTrophicIndex; } }
/// <summary> /// Run eating /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for eating</param> /// <param name="partial">Thread-locked variables</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used for the current model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { PreviousTrophicIndex = gridCellCohorts[actingCohort].TrophicIndex; //Reset this cohort's trohic index ready for calculation across its feeding this timetsstep gridCellCohorts[actingCohort].TrophicIndex = 0.0; // Get the nutrition source (herbivory, carnivory or omnivory) of the acting cohort string NutritionSource = madingleyCohortDefinitions.GetTraitNames("Nutrition source", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Switch to the appropriate eating process(es) given the cohort's nutrition source switch (NutritionSource) { case "herbivore": // Get the assimilation efficiency for herbivory for this cohort from the functional group definitions Implementations["revised herbivory"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the proportion of time spent eating for this cohort from the functional group definitions Implementations["revised herbivory"].ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; // Calculate the potential biomass available from herbivory if (cellEnvironment["Realm"][0] == 2.0) Implementations["revised herbivory"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); else Implementations["revised herbivory"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); // Run herbivory to apply changes in autotroph biomass from herbivory and add biomass eaten to the delta arrays Implementations["revised herbivory"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations,outputDetail, initialisation); break; case "carnivore": // Get the assimilation efficiency for predation for this cohort from the functional group definitions Implementations["revised predation"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); Implementations["revised predation"].ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; // Calculate the potential biomass available from predation if (cellEnvironment["Realm"][0] == 2.0) Implementations["revised predation"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); else Implementations["revised predation"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); // Run predation to apply changes in prey biomass from predation and add biomass eaten to the delta arrays Implementations["revised predation"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations,outputDetail, initialisation); break; case "omnivore": // Get the assimilation efficiency for predation for this cohort from the functional group definitions Implementations["revised predation"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the assimilation efficiency for herbivory for this cohort from the functional group definitions Implementations["revised herbivory"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the proportion of time spent eating and assign to both the herbivory and predation implementations double ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; Implementations["revised predation"].ProportionTimeEating = ProportionTimeEating; Implementations["revised herbivory"].ProportionTimeEating = ProportionTimeEating; // Calculate the potential biomass available from herbivory if (cellEnvironment["Realm"][0] == 2.0) Implementations["revised herbivory"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); else Implementations["revised herbivory"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); // Calculate the potential biomass available from predation if (cellEnvironment["Realm"][0] == 2.0) Implementations["revised predation"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); else Implementations["revised predation"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); // Calculate the total handling time for all expected kills from predation and expected plant matter eaten in herbivory TotalTimeToEatForOmnivores = Implementations["revised herbivory"].TimeUnitsToHandlePotentialFoodItems + Implementations["revised predation"].TimeUnitsToHandlePotentialFoodItems; // Assign this total time to the relevant variables in both herbviory and predation, so that actual amounts eaten are calculated correctly Implementations["revised herbivory"].TimeUnitsToHandlePotentialFoodItems = TotalTimeToEatForOmnivores; Implementations["revised predation"].TimeUnitsToHandlePotentialFoodItems = TotalTimeToEatForOmnivores; // Run predation to update prey cohorts and delta biomasses for the acting cohort Implementations["revised predation"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations,outputDetail, initialisation); // Run herbivory to update autotroph biomass and delta biomasses for the acting cohort Implementations["revised herbivory"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations,outputDetail, initialisation); break; default: // For nutrition source that are not supported, throw an error Debug.Fail("The model currently does not contain an eating model for nutrition source:" + NutritionSource); break; } // Check that the biomasses from predation and herbivory in the deltas is a number Debug.Assert(!double.IsNaN(deltas["biomass"]["predation"]), "BiomassFromEating is NaN"); Debug.Assert(!double.IsNaN(deltas["biomass"]["herbivory"]), "BiomassFromEating is NaN"); double biomassEaten = 0.0; if (madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex) > 0) { biomassEaten += (deltas["biomass"]["predation"] / madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex)); } if (madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex) > 0) { biomassEaten += (deltas["biomass"]["herbivory"]/madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex)); } if (biomassEaten > 0.0) { gridCellCohorts[actingCohort].TrophicIndex = 1 + (gridCellCohorts[actingCohort].TrophicIndex / (biomassEaten * gridCellCohorts[actingCohort].CohortAbundance)); } else { gridCellCohorts[actingCohort].TrophicIndex = PreviousTrophicIndex; } }
/// <summary> /// Generate new cohorts from reproductive potential mass /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment of the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param> /// <param name="partial">Thread-locked variables</param> /// <param name="iteroparous">Whether the acting cohort is iteroparous, as opposed to semelparous</param> /// <param name="currentMonth">The current model month</param> public void RunReproductionEvents(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker, ref ThreadLockedParallelVariables partial, bool iteroparous, uint currentMonth) { // Adult non-reproductive biomass lost by semelparous organisms double AdultMassLost; // Offspring cohort abundance double _OffspringCohortAbundance; // Mass ratio of body mass + reproductive mass to adult body mass double CurrentMassRatio; // Individual body mass including change this time step as a result of other ecological processes double BodyMassIncludingChangeThisTimeStep; // Offspring juvenile and adult body masses double[] OffspringJuvenileAndAdultBodyMasses = new double[2]; // Offspring cohort Cohort OffspringCohort; // Individual reproductive mass including change this time step as a result of other ecological processes double ReproductiveMassIncludingChangeThisTimeStep; // Calculate the biomass of an individual in this cohort including changes this time step from other ecological processes BodyMassIncludingChangeThisTimeStep = 0.0; foreach (var Biomass in deltas["biomass"]) { // Add the delta biomass to net biomass BodyMassIncludingChangeThisTimeStep += Biomass.Value; } BodyMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualBodyMass; // Calculate the reproductive biomass of an individual in this cohort including changes this time step from other ecological processes ReproductiveMassIncludingChangeThisTimeStep = 0.0; foreach (var ReproBiomass in deltas["reproductivebiomass"]) { // Add the delta reproductive biomass to net biomass ReproductiveMassIncludingChangeThisTimeStep += ReproBiomass.Value; } ReproductiveMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualReproductivePotentialMass; // Get the current ratio of total individual mass (including reproductive potential) to adult body mass CurrentMassRatio = (BodyMassIncludingChangeThisTimeStep + ReproductiveMassIncludingChangeThisTimeStep) / gridCellCohorts[actingCohort].AdultMass; // Must have enough mass to hit reproduction threshold criterion, and either (1) be in breeding season, or (2) be a marine cell (no breeding season in marine cells) if ((CurrentMassRatio > _MassRatioThreshold) && ((cellEnvironment["Breeding Season"][currentMonth] == 1.0) || ((cellEnvironment["Realm"][0] == 2.0)))) { // Iteroparous and semelparous organisms have different strategies if (iteroparous) { // Iteroparous organisms do not allocate any of their current non-reproductive biomass to reproduction AdultMassLost = 0.0; // Calculate the number of offspring that could be produced given the reproductive potential mass of individuals _OffspringCohortAbundance = gridCellCohorts[actingCohort].CohortAbundance * ReproductiveMassIncludingChangeThisTimeStep / gridCellCohorts[actingCohort].JuvenileMass; } else { // Semelparous organisms allocate a proportion of their current non-reproductive biomass (including the effects of other ecological processes) to reproduction AdultMassLost = _SemelparityAdultMassAllocation * BodyMassIncludingChangeThisTimeStep; // Calculate the number of offspring that could be produced given the reproductive potential mass of individuals _OffspringCohortAbundance = gridCellCohorts[actingCohort].CohortAbundance * (AdultMassLost + ReproductiveMassIncludingChangeThisTimeStep) / gridCellCohorts[actingCohort].JuvenileMass; } // Check that the abundance in the cohort to produce is greater than or equal to zero Debug.Assert(_OffspringCohortAbundance >= 0.0, "Offspring abundance < 0"); // Get the adult and juvenile masses of the offspring cohort OffspringJuvenileAndAdultBodyMasses = GetOffspringCohortProperties(gridCellCohorts, actingCohort, madingleyCohortDefinitions); // Update cohort abundance in case juvenile mass has been altered through 'evolution' _OffspringCohortAbundance = (_OffspringCohortAbundance * gridCellCohorts[actingCohort].JuvenileMass) / OffspringJuvenileAndAdultBodyMasses[0]; double TrophicIndex; switch (madingleyCohortDefinitions.GetTraitNames("nutrition source", actingCohort[0])) { case "herbivore": TrophicIndex = 2; break; case "omnivore": TrophicIndex = 2.5; break; case "carnivore": TrophicIndex = 3; break; default: Debug.Fail("Unexpected nutrition source trait value when assigning trophic index"); TrophicIndex = 0.0; break; } // Create the offspring cohort OffspringCohort = new Cohort((byte)actingCohort[0], OffspringJuvenileAndAdultBodyMasses[0], OffspringJuvenileAndAdultBodyMasses[1], OffspringJuvenileAndAdultBodyMasses[0], _OffspringCohortAbundance, Math.Exp(gridCellCohorts[actingCohort].LogOptimalPreyBodySizeRatio), (ushort)currentTimestep, gridCellCohorts[actingCohort].ProportionTimeActive, ref partial.NextCohortIDThreadLocked, TrophicIndex, tracker.TrackProcesses); // Add the offspring cohort to the grid cell cohorts array gridCellCohorts[actingCohort[0]].Add(OffspringCohort); // If track processes has been specified then add the new cohort to the process tracker if (tracker.TrackProcesses) tracker.RecordNewCohort((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, _OffspringCohortAbundance, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex, gridCellCohorts[actingCohort].CohortID, (uint)partial.NextCohortIDThreadLocked); // Subtract all of the reproductive potential mass of the parent cohort, which has been used to generate the new // cohort, from the delta reproductive potential mass and delta adult body mass deltas["reproductivebiomass"]["reproduction"] -= ReproductiveMassIncludingChangeThisTimeStep; deltas["biomass"]["reproduction"] -= AdultMassLost; } else { // Organism is not large enough, or it is not the breeding season, so take no action } }
/// <summary> /// Apply the changes from predation to prey cohorts, and update deltas for the predator cohort /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for predation</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail used in this model run</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations, string outputDetail, MadingleyModelInitialisation initialisation) { if (trackProcesses.TrackProcesses) { Track = (RandomNumberGenerator.GetUniform() > 0.975) ? true : false; } TempDouble = 0.0; // Temporary variable to hold the total time spent eating + 1. Saves an extra calculation in CalculateAbundanceEaten double TotalTimeUnitsToHandlePlusOne = TimeUnitsToHandlePotentialFoodItems + 1; // Loop over potential prey functional groups foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat) { // Loop over cohorts within the functional group for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++) { // Get the individual body mass of this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Calculate the actual abundance of prey eaten from this cohort if (_PotentialAbundanceEaten[FunctionalGroup][i] > 0) { // Calculate the actual abundance of prey eaten from this cohort _AbundancesEaten[FunctionalGroup][i] = CalculateAbundanceEaten(_PotentialAbundanceEaten[FunctionalGroup][i], _PredatorAbundanceMultipliedByTimeEating, TotalTimeUnitsToHandlePlusOne, gridCellCohorts[FunctionalGroup][i].CohortAbundance); } else { _AbundancesEaten[FunctionalGroup][i] = 0; } // Remove number of prey eaten from the prey cohort gridCellCohorts[FunctionalGroup][i].CohortAbundance -= _AbundancesEaten[FunctionalGroup][i]; gridCellCohorts[actingCohort].TrophicIndex += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] * gridCellCohorts[FunctionalGroup][i].TrophicIndex; // If the process tracker is set and output detail is set to high and the prey cohort has never been merged, // then track its mortality owing to predation if (trackProcesses.TrackProcesses) { if ((outputDetail == "high") && (gridCellCohorts[FunctionalGroup][i].CohortID.Count == 1) && AbundancesEaten[FunctionalGroup][i] > 0) { trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts [FunctionalGroup][i].BirthTimeStep, currentTimestep, gridCellCohorts[FunctionalGroup][i].IndividualBodyMass, gridCellCohorts[FunctionalGroup][i].AdultMass, gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex, gridCellCohorts[FunctionalGroup][i].CohortID[0], AbundancesEaten[FunctionalGroup][i], "predation"); } // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between // prey and predator if (specificLocations) { trackProcesses.RecordPredationMassFlow(currentTimestep, _BodyMassPrey, _BodyMassPredator, _BodyMassPrey * _AbundancesEaten[FunctionalGroup][i]); if (outputDetail == "high") { trackProcesses.TrackPredationTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex, gridCellCohorts[actingCohort].FunctionalGroupIndex, madingleyCohortDefinitions, (_AbundancesEaten[FunctionalGroup][i] * _BodyMassPrey), _BodyMassPredator, _BodyMassPrey, initialisation, cellEnvironment["Realm"][0] == 2.0); } } } // Check that the abundance eaten from this cohort is not negative // Commented out for the purposes of speed //Debug.Assert( _AbundancesEaten[FunctionalGroup][i].CompareTo(0.0) >= 0, // "Predation negative for this prey cohort" + actingCohort); // Create a temporary value to speed up the predation function // This is equivalent to the body mass of the prey cohort including reproductive potential mass, times the abundance eaten of the prey cohort, // divided by the abundance of the predator TempDouble += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] / _AbundancePredator; } } // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting (predator) cohort deltas["biomass"]["predation"] = TempDouble * _PredatorAssimilationEfficiency; // Move the biomass eaten but not assimilated by an individual into the organic matter pool deltas["organicpool"]["predation"] = TempDouble * _PredatorNonAssimilation * _AbundancePredator; // Check that the delta biomass from eating for the acting cohort is not negative //Debug.Assert(deltas["biomass"]["predation"] >= 0, "Predation yields negative biomass"); // Calculate the total biomass eaten by the acting (predator) cohort _TotalBiomassEatenByCohort = deltas["biomass"]["predation"] * _AbundancePredator; }
/// <summary> /// Initializes the ecosystem model /// </summary> /// <param name="initialisation">An instance of the model initialisation class</param> /// <param name="scenarioParameters">The parameters for the scenarios to run</param> /// <param name="scenarioIndex">The index of the scenario being run</param> /// <param name="outputFilesSuffix">The suffix to be applied to all outputs from this model run</param> /// <param name="globalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="simulation">The index of the simulation being run</param> public MadingleyModel(MadingleyModelInitialisation initialisation, ScenarioParameterInitialisation scenarioParameters, int scenarioIndex, string outputFilesSuffix, string globalModelTimeStepUnit, int simulation) { // Assign the properties for this model run AssignModelRunProperties(initialisation, scenarioParameters, scenarioIndex, outputFilesSuffix); // Set up list of global diagnostics SetUpGlobalDiagnosticsList(); // Set up the model grid SetUpModelGrid(initialisation, scenarioParameters, scenarioIndex, simulation); // Set up model outputs SetUpOutputs(initialisation, simulation, scenarioIndex); // Make the initial outputs InitialOutputs(outputFilesSuffix, initialisation, CurrentMonth); // Instance the array of process trackers ProcessTrackers = new ProcessTracker[_CellList.Count]; // Temporary variables Boolean varExists; // Set up process trackers for each grid cell for (int i = 0; i < _CellList.Count; i++) { ProcessTrackers[i] = new ProcessTracker(NumTimeSteps, EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, _CellList, initialisation.ProcessTrackingOutputs, initialisation.TrackProcesses, CohortFunctionalGroupDefinitions, EcosystemModelGrid.GlobalMissingValue, outputFilesSuffix, initialisation.OutputPath, initialisation.ModelMassBins, SpecificLocations, i, initialisation, EcosystemModelGrid.GetEnviroLayer("Realm", 0, _CellList[i][0], _CellList[i][1], out varExists) == 2.0, EcosystemModelGrid.LatCellSize, EcosystemModelGrid.LonCellSize); } // Set up a cross cell process tracker TrackCrossCellProcesses = new CrossCellProcessTracker(initialisation.TrackCrossCellProcesses, "DispersalData", initialisation.OutputPath, outputFilesSuffix); //Set up a global process tracker if (SpecificLocations) initialisation.TrackGlobalProcesses = false; TrackGlobalProcesses = new GlobalProcessTracker(NumTimeSteps, EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, _CellList, initialisation.ProcessTrackingOutputs, initialisation.TrackGlobalProcesses, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, EcosystemModelGrid.GlobalMissingValue, outputFilesSuffix, initialisation.OutputPath, initialisation.ModelMassBins, SpecificLocations, initialisation, EcosystemModelGrid.LatCellSize, EcosystemModelGrid.LonCellSize); //Set-up the instance of OutputModelState WriteModelState = new OutputModelState(initialisation, outputFilesSuffix, simulation); if (SpecificLocations) initialisation.RunRealm = ""; // Record the initial cohorts in the process trackers RecordInitialCohorts(); // Initialise the class for cross-grid-cell ecology MadingleyEcologyCrossGridCell = new EcologyCrossGridCell(); // Initialise the time step timer TimeStepTimer = new StopWatch(); EcologyTimer = new StopWatch(); OutputTimer = new StopWatch(); // Set the global model time step unit _GlobalModelTimeStepUnit = globalModelTimeStepUnit; // Initialise the utility functions Utilities = new UtilityFunctions(); // Initialise the climate change impacts class ClimateChangeSimulator = new ClimateChange(); // Initialise the harvesting impacts class HarvestingSimulator = new Harvesting(EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, (float)EcosystemModelGrid.LatCellSize); }
/// <summary> /// Run mortality /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for mortality</param> /// <param name="partial">Thread-locked variables</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level output detail being used for the current model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { // Variables to hold the mortality rates double MortalityRateBackground; double MortalityRateSenescence; double MortalityRateStarvation; // Variable to hold the total abundance lost to all forms of mortality double MortalityTotal; // Individual body mass including change this time step as a result of other ecological processes double BodyMassIncludingChangeThisTimeStep; // Individual reproductive mass including change this time step as a result of other ecological processes double ReproductiveMassIncludingChangeThisTimeStep; // Calculate the body mass of individuals in this cohort including mass gained through eating this time step, up to but not exceeding adult body mass for this cohort. // Should be fine because these deductions are made in the reproduction implementation, but use Math.Min to double check. BodyMassIncludingChangeThisTimeStep = 0.0; // Loop over all items in the biomass deltas foreach (var Biomass in deltas["biomass"]) { // Add the delta biomass to net biomass BodyMassIncludingChangeThisTimeStep += Biomass.Value; } BodyMassIncludingChangeThisTimeStep = Math.Min(gridCellCohorts[actingCohort].AdultMass, BodyMassIncludingChangeThisTimeStep + gridCellCohorts[actingCohort].IndividualBodyMass); // Temporary variable to hold net reproductive biomass change of individuals in this cohort as a result of other ecological processes ReproductiveMassIncludingChangeThisTimeStep = 0.0; // Loop over all items in the biomass deltas foreach (var Biomass in deltas["reproductivebiomass"]) { // Add the delta biomass to net biomass ReproductiveMassIncludingChangeThisTimeStep += Biomass.Value; } ReproductiveMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualReproductivePotentialMass; // Check to see if the cohort has already been killed by predation etc if ((BodyMassIncludingChangeThisTimeStep).CompareTo(0.0) <= 0) { // If individual body mass is not greater than zero, then all individuals become extinct MortalityTotal = gridCellCohorts[actingCohort].CohortAbundance; } else { // Calculate background mortality rate MortalityRateBackground = Implementations["basic background mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); // If the cohort has matured, then calculate senescence mortality rate, otherwise set rate to zero if (gridCellCohorts[actingCohort].MaturityTimeStep != uint.MaxValue) { MortalityRateSenescence = Implementations["basic senescence mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); } else { MortalityRateSenescence = 0.0; } // Calculate the starvation mortality rate based on individual body mass and maximum body mass ever // achieved by this cohort MortalityRateStarvation = Implementations["basic starvation mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); // Calculate the number of individuals that suffer mortality this time step from all sources of mortality MortalityTotal = (1 - Math.Exp(-MortalityRateBackground - MortalityRateSenescence - MortalityRateStarvation)) * gridCellCohorts[actingCohort].CohortAbundance; } // If the process tracker is on and output detail is set to high and this cohort has not been merged yet, then record // the number of individuals that have died if (trackProcesses.TrackProcesses && (outputDetail == "high") && (!gridCellCohorts[actingCohort].Merged)) { trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[actingCohort].BirthTimeStep, currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex, gridCellCohorts[actingCohort].CohortID[0], MortalityTotal, "sen/bg/starv"); } // Remove individuals that have died from the delta abundance for this cohort deltas["abundance"]["mortality"] = -MortalityTotal; // Add the biomass of individuals that have died to the delta biomass in the organic pool (including reproductive // potential mass, and mass gained through eating, and excluding mass lost through metabolism) deltas["organicpool"]["mortality"] = MortalityTotal * (BodyMassIncludingChangeThisTimeStep + ReproductiveMassIncludingChangeThisTimeStep); }
/// <summary> /// Update the properties of the acting cohort and of the environmental biomass pools after running the ecological processes for a cohort /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment of the current grid cell</param> /// <param name="deltas">The sorted list of deltas for the current grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">A process tracker</param> public void UpdateEcology(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker) { // Apply the results of within-cell ecological processes ApplyEcologicalProcessResults.UpdateAllEcology(gridCellCohorts, actingCohort, cellEnvironment, deltas, currentTimestep, tracker); }
/// <summary> /// Run ecological processes that operate on cohorts within a single grid cell /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">A sorted list of deltas to track changes in abundances and biomasses during the ecological processes</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="trackProcesses">An instance of the process tracker</param> /// <param name="partial">Thread-locked local variables</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used for this model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunWithinCellEcology(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial, Boolean specificLocations,string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { // RUN EATING _EatingFormulations["Basic eating"].RunEcologicalProcess(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, trackProcesses, ref partial, specificLocations, outputDetail, currentMonth, initialisation); // RUN METABOLISM - THIS TIME TAKE THE METABOLIC LOSS TAKING INTO ACCOUNT WHAT HAS BEEN INGESTED THROUGH EATING _MetabolismFormulations["Basic metabolism"].RunEcologicalProcess(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, trackProcesses, ref partial, specificLocations, outputDetail, currentMonth, initialisation); // RUN REPRODUCTION - TAKING INTO ACCOUNT NET BIOMASS CHANGES RESULTING FROM EATING AND METABOLISING _ReproductionFormulations["Basic reproduction"].RunEcologicalProcess(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, trackProcesses, ref partial, specificLocations, outputDetail, currentMonth, initialisation); // RUN MORTALITY - TAKING INTO ACCOUNT NET BIOMASS CHANGES RESULTING FROM EATING, METABOLISM AND REPRODUCTION _MortalityFormulations["Basic mortality"].RunEcologicalProcess(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, trackProcesses, ref partial, specificLocations, outputDetail, currentMonth, initialisation); }
/// <summary> /// Calculate the actual amount eaten in herbivory, apply the changes to the eaten autotroph stocks, and update deltas for the herbivore cohort /// </summary> /// <param name="gridCellCohorts">The cohorts in this grid cell</param> /// <param name="gridCellStocks">The stocks in this grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environmental conditions in this grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for herbivory</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used in this model run</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations, string outputDetail, MadingleyModelInitialisation initialisation) { EdibleScaling = 1.0; if (cellEnvironment["Realm"][0] == 1.0) EdibleScaling = 0.1; // Loop over autotroph functional groups that can be eaten foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat) { // Loop over stocks within the functional groups for (int i = 0; i < gridCellStocks[FunctionalGroup].Count; i++) { // Get the mass from this stock that is available for eating (assumes only 10% is edible in the terrestrial realm) EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass * EdibleScaling; // Calculate the biomass actually eaten from this stock by the acting cohort _BiomassesEaten[FunctionalGroup][i] = CalculateBiomassesEaten(_PotentialBiomassesEaten[FunctionalGroup][i], _TimeUnitsToHandlePotentialFoodItems, gridCellCohorts[actingCohort].CohortAbundance, EdibleMass); gridCellCohorts[actingCohort].TrophicIndex += _BiomassesEaten[FunctionalGroup][i]; // Remove the biomass eaten from the autotroph stock gridCellStocks[FunctionalGroup][i].TotalBiomass -= _BiomassesEaten[FunctionalGroup][i]; // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between // primary producer and herbivore if (specificLocations && trackProcesses.TrackProcesses) { trackProcesses.RecordHerbivoryMassFlow(currentTimestep, _BodyMassHerbivore, _BiomassesEaten[FunctionalGroup][i]); } // If track processes has been specified and the output detail level is set to high and the model is being run for specific locations, // then track the flow of mass between trophic levels if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations) { trackProcesses.TrackHerbivoryTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[actingCohort].FunctionalGroupIndex, madingleyCohortDefinitions, _BiomassesEaten[FunctionalGroup][i], _BodyMassHerbivore, initialisation, cellEnvironment["Realm"][0] == 2.0); } // Check that the biomass eaten is not a negative value // Commented out for purposes of speed //Debug.Assert(_BiomassesEaten[FunctionalGroup][i] >= 0, // "Herbivory negative for this herbivore cohort" + actingCohort); // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting cohort deltas["biomass"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * AssimilationEfficiency / gridCellCohorts[actingCohort].CohortAbundance; // Move the biomass eaten but not assimilated by an individual into the organic matter pool deltas["organicpool"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * (1 - AssimilationEfficiency); } // Check that the delta biomass from eating for the acting cohort is not negative // Commented out for the purposes of speed //Debug.Assert(deltas["biomass"]["herbivory"] >= 0, "Delta biomass from herbviory is negative"); // Calculate the total biomass eaten by the acting (herbivore) cohort _TotalBiomassEatenByCohort = deltas["biomass"]["herbivory"] * gridCellCohorts[actingCohort].CohortAbundance; } }
/// <summary> /// Run mortality /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for mortality</param> /// <param name="partial">Thread-locked variables</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level output detail being used for the current model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { // Variables to hold the mortality rates double MortalityRateBackground; double MortalityRateSenescence; double MortalityRateStarvation; // Variable to hold the total abundance lost to all forms of mortality double MortalityTotal; // Individual body mass including change this time step as a result of other ecological processes double BodyMassIncludingChangeThisTimeStep; // Individual reproductive mass including change this time step as a result of other ecological processes double ReproductiveMassIncludingChangeThisTimeStep; // Calculate the body mass of individuals in this cohort including mass gained through eating this time step, up to but not exceeding adult body mass for this cohort. // Should be fine because these deductions are made in the reproduction implementation, but use Math.Min to double check. BodyMassIncludingChangeThisTimeStep = 0.0; // Loop over all items in the biomass deltas foreach (var Biomass in deltas["biomass"]) { // Add the delta biomass to net biomass BodyMassIncludingChangeThisTimeStep += Biomass.Value; } BodyMassIncludingChangeThisTimeStep = Math.Min(gridCellCohorts[actingCohort].AdultMass, BodyMassIncludingChangeThisTimeStep + gridCellCohorts[actingCohort].IndividualBodyMass); // Temporary variable to hold net reproductive biomass change of individuals in this cohort as a result of other ecological processes ReproductiveMassIncludingChangeThisTimeStep = 0.0; // Loop over all items in the biomass deltas foreach (var Biomass in deltas["reproductivebiomass"]) { // Add the delta biomass to net biomass ReproductiveMassIncludingChangeThisTimeStep += Biomass.Value; } ReproductiveMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualReproductivePotentialMass; // Check to see if the cohort has already been killed by predation etc if ((BodyMassIncludingChangeThisTimeStep).CompareTo(0.0) <= 0) { // If individual body mass is not greater than zero, then all individuals become extinct MortalityTotal = gridCellCohorts[actingCohort].CohortAbundance; } else { // Calculate background mortality rate MortalityRateBackground = Implementations["basic background mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); // If the cohort has matured, then calculate senescence mortality rate, otherwise set rate to zero if (gridCellCohorts[actingCohort].MaturityTimeStep != uint.MaxValue) { MortalityRateSenescence = Implementations["basic senescence mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); } else { MortalityRateSenescence = 0.0; } // Calculate the starvation mortality rate based on individual body mass and maximum body mass ever // achieved by this cohort MortalityRateStarvation = Implementations["basic starvation mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); // Calculate the number of individuals that suffer mortality this time step from all sources of mortality MortalityTotal = (1 - Math.Exp(-MortalityRateBackground - MortalityRateSenescence - MortalityRateStarvation)) * gridCellCohorts[actingCohort].CohortAbundance; } // If the process tracker is on and output detail is set to high and this cohort has not been merged yet, then record // the number of individuals that have died if (trackProcesses.TrackProcesses && (outputDetail == "high") && (!gridCellCohorts[actingCohort].Merged)) { trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[actingCohort].BirthTimeStep, currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex, gridCellCohorts[actingCohort].CohortID[0], MortalityTotal, "sen/bg/starv"); } // Remove individuals that have died from the delta abundance for this cohort deltas["abundance"]["mortality"] = -MortalityTotal; // Add the biomass of individuals that have died to the delta biomass in the organic pool (including reproductive // potential mass, and mass gained through eating, and excluding mass lost through metabolism) deltas["organicpool"]["mortality"] = MortalityTotal * (BodyMassIncludingChangeThisTimeStep + ReproductiveMassIncludingChangeThisTimeStep); }
/// <summary> /// Apply the changes from predation to prey cohorts, and update deltas for the predator cohort /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for predation</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail used in this model run</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations, string outputDetail, MadingleyModelInitialisation initialisation) { if (trackProcesses.TrackProcesses) { Track = (RandomNumberGenerator.GetUniform() > 0.975) ? true : false; } TempDouble = 0.0; // Temporary variable to hold the total time spent eating + 1. Saves an extra calculation in CalculateAbundanceEaten double TotalTimeUnitsToHandlePlusOne = TimeUnitsToHandlePotentialFoodItems + 1; // Loop over potential prey functional groups foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat) { // Loop over cohorts within the functional group for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++) { // Get the individual body mass of this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Calculate the actual abundance of prey eaten from this cohort if (gridCellCohorts[FunctionalGroup][i].CohortAbundance > 0) { // Calculate the actual abundance of prey eaten from this cohort _AbundancesEaten[FunctionalGroup][i] = CalculateAbundanceEaten(_PotentialAbundanceEaten[FunctionalGroup][i], _PredatorAbundanceMultipliedByTimeEating, TotalTimeUnitsToHandlePlusOne, gridCellCohorts[FunctionalGroup][i].CohortAbundance); } else _AbundancesEaten[FunctionalGroup][i] = 0; // Remove number of prey eaten from the prey cohort gridCellCohorts[FunctionalGroup][i].CohortAbundance -= _AbundancesEaten[FunctionalGroup][i]; gridCellCohorts[actingCohort].TrophicIndex += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] * gridCellCohorts[FunctionalGroup][i].TrophicIndex; // If the process tracker is set and output detail is set to high and the prey cohort has never been merged, // then track its mortality owing to predation if (trackProcesses.TrackProcesses) { if ((outputDetail == "high") && (gridCellCohorts[FunctionalGroup][i].CohortID.Count == 1) && AbundancesEaten[FunctionalGroup][i] > 0) { trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts [FunctionalGroup][i].BirthTimeStep, currentTimestep, gridCellCohorts[FunctionalGroup][i].IndividualBodyMass, gridCellCohorts[FunctionalGroup][i].AdultMass, gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex, gridCellCohorts[FunctionalGroup][i].CohortID[0], AbundancesEaten[FunctionalGroup][i], "predation"); } // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between // prey and predator if (specificLocations) { trackProcesses.RecordPredationMassFlow(currentTimestep, _BodyMassPrey, _BodyMassPredator, _BodyMassPrey * _AbundancesEaten[FunctionalGroup][i]); if (outputDetail == "high") trackProcesses.TrackPredationTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex, gridCellCohorts[actingCohort].FunctionalGroupIndex, madingleyCohortDefinitions, (_AbundancesEaten[FunctionalGroup][i] * _BodyMassPrey), _BodyMassPredator, _BodyMassPrey, initialisation, cellEnvironment["Realm"][0] == 2.0); } } // Check that the abundance eaten from this cohort is not negative // Commented out for the purposes of speed //Debug.Assert( _AbundancesEaten[FunctionalGroup][i].CompareTo(0.0) >= 0, // "Predation negative for this prey cohort" + actingCohort); // Create a temporary value to speed up the predation function // This is equivalent to the body mass of the prey cohort including reproductive potential mass, times the abundance eaten of the prey cohort, // divided by the abundance of the predator TempDouble += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] / _AbundancePredator; } } // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting (predator) cohort deltas["biomass"]["predation"] = TempDouble * _PredatorAssimilationEfficiency; // Move the biomass eaten but not assimilated by an individual into the organic matter pool deltas["organicpool"]["predation"] = TempDouble * _PredatorNonAssimilation * _AbundancePredator; // Check that the delta biomass from eating for the acting cohort is not negative //Debug.Assert(deltas["biomass"]["predation"] >= 0, "Predation yields negative biomass"); // Calculate the total biomass eaten by the acting (predator) cohort _TotalBiomassEatenByCohort = deltas["biomass"]["predation"] * _AbundancePredator; }
/// <summary> /// Calculate the actual amount eaten in herbivory, apply the changes to the eaten autotroph stocks, and update deltas for the herbivore cohort /// </summary> /// <param name="gridCellCohorts">The cohorts in this grid cell</param> /// <param name="gridCellStocks">The stocks in this grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environmental conditions in this grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for herbivory</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used in this model run</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations, string outputDetail, MadingleyModelInitialisation initialisation) { EdibleScaling = 1.0; if (cellEnvironment["Realm"][0] == 1.0) { EdibleScaling = 0.1; } // Loop over autotroph functional groups that can be eaten foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat) { // Loop over stocks within the functional groups for (int i = 0; i < gridCellStocks[FunctionalGroup].Count; i++) { // Get the mass from this stock that is available for eating (assumes only 10% is edible in the terrestrial realm) EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass * EdibleScaling; // Calculate the biomass actually eaten from this stock by the acting cohort _BiomassesEaten[FunctionalGroup][i] = CalculateBiomassesEaten(_PotentialBiomassesEaten[FunctionalGroup][i], _TimeUnitsToHandlePotentialFoodItems, gridCellCohorts[actingCohort].CohortAbundance, EdibleMass); gridCellCohorts[actingCohort].TrophicIndex += _BiomassesEaten[FunctionalGroup][i]; // Remove the biomass eaten from the autotroph stock gridCellStocks[FunctionalGroup][i].TotalBiomass -= _BiomassesEaten[FunctionalGroup][i]; // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between // primary producer and herbivore if (specificLocations && trackProcesses.TrackProcesses) { trackProcesses.RecordHerbivoryMassFlow(currentTimestep, _BodyMassHerbivore, _BiomassesEaten[FunctionalGroup][i]); } // If track processes has been specified and the output detail level is set to high and the model is being run for specific locations, // then track the flow of mass between trophic levels if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations) { trackProcesses.TrackHerbivoryTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[actingCohort].FunctionalGroupIndex, madingleyCohortDefinitions, _BiomassesEaten[FunctionalGroup][i], _BodyMassHerbivore, initialisation, cellEnvironment["Realm"][0] == 2.0); } // Check that the biomass eaten is not a negative value // Commented out for purposes of speed //Debug.Assert(_BiomassesEaten[FunctionalGroup][i] >= 0, // "Herbivory negative for this herbivore cohort" + actingCohort); // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting cohort deltas["biomass"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * AssimilationEfficiency / gridCellCohorts[actingCohort].CohortAbundance; // Move the biomass eaten but not assimilated by an individual into the organic matter pool deltas["organicpool"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * (1 - AssimilationEfficiency); } // Check that the delta biomass from eating for the acting cohort is not negative // Commented out for the purposes of speed //Debug.Assert(deltas["biomass"]["herbivory"] >= 0, "Delta biomass from herbviory is negative"); // Calculate the total biomass eaten by the acting (herbivore) cohort _TotalBiomassEatenByCohort = deltas["biomass"]["herbivory"] * gridCellCohorts[actingCohort].CohortAbundance; } }