public static List <InputModelState> ConvertModelStates( Madingley.Common.ModelState modelState, Madingley.Common.Configuration c, Madingley.Common.Environment e) { var numLatCells = (UInt32)((e.TopLatitude - e.BottomLatitude) / e.CellSize); var numLonCells = (UInt32)((e.RightmostLongitude - e.LeftmostLongitude) / e.CellSize); // Set up a grid of grid cells var gridCellCohorts = new GridCellCohortHandler[numLatCells, numLonCells]; var gridCellStocks = new GridCellStockHandler[numLatCells, numLonCells]; gridCellCohorts[0, 0] = new GridCellCohortHandler(c.CohortFunctionalGroupDefinitions.Data.Count()); var cellList = e.FocusCells.ToArray(); var gridCells = modelState.GridCells.ToArray(); for (var ii = 0; ii < cellList.Count(); ii++) { var gridCell = gridCells[ii]; gridCellCohorts[cellList[ii].Item1, cellList[ii].Item2] = ConvertCohorts(gridCell.Cohorts); gridCellStocks[cellList[ii].Item1, cellList[ii].Item2] = ConvertStocks(gridCell.Stocks); } var inputModelState = new InputModelState(gridCellCohorts, gridCellStocks); return(new List <InputModelState>() { inputModelState }); }
/// <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> /// 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, Tuple <string, double, double> 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) { int ScenarioYear; if (currentTimeStep < burninSteps) { ScenarioYear = 0; } else { ScenarioYear = (int)Math.Floor((currentTimeStep - burninSteps) / 12.0); } 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); double fhanpp = HANPP.RemoveHumanAppropriatedMatter(WetMatterNPP, cellEnvironment, humanNPPScenario, gridCellStocks, actingStock, currentTimeStep, ScenarioYear, 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"); } }
public GridCell( GridCellCohortHandler GridCellCohorts, GridCellStockHandler GridCellStocks, SortedList <string, double[]> CellEnvironment, float Latitude, float Longitude) { this.GridCellCohorts = GridCellCohorts; this.GridCellStocks = GridCellStocks; this.CellEnvironment = CellEnvironment; this._Latitude = Latitude; this._Longitude = Longitude; }
/// <summary> /// Run metabolism for the acting 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 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 the stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="currentMonth">The current model month</param> public void RunMetabolism(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, uint currentMonth) { // Calculate metabolic loss for an individual and add the value to the delta biomass for metabolism deltas["biomass"]["metabolism"] = -CalculateIndividualMetabolicRate(gridCellCohorts[actingCohort].IndividualBodyMass, cellEnvironment["Temperature"][currentMonth] + _TemperatureUnitsConvert) * _DeltaT; // If metabolic loss is greater than individual body mass after herbivory and predation, then set equal to individual body mass deltas["biomass"]["metabolism"] = Math.Max(deltas["biomass"]["metabolism"], -(gridCellCohorts[actingCohort].IndividualBodyMass + deltas["biomass"]["predation"] + deltas["biomass"]["herbivory"])); // Add total metabolic loss for all individuals in the cohort to delta biomass for metabolism in the respiratory CO2 pool deltas["respiratoryCO2pool"]["metabolism"] = -deltas["biomass"]["metabolism"] * gridCellCohorts[actingCohort].CohortAbundance; }
/// <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> /// Calculate the potential biomass that could be gained through herbivory on each grid cell autotroph stock /// </summary> /// <param name="gridCellCohorts">The cohorts in the grid cell</param> /// <param name="gridCellStocks">The stocks in the grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment in the current 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> public void GetEatingPotentialMarine(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { // Set the total biomass eaten by the acting cohort to zero _TotalBiomassEatenByCohort = 0.0; // Get the individual body mass of the acting cohort _BodyMassHerbivore = gridCellCohorts[actingCohort].IndividualBodyMass; // Set the total number of units to handle all potential biomass eaten to zero _TimeUnitsToHandlePotentialFoodItems = 0.0; // Initialise the jagged arrays to hold the potential and actual biomass eaten in each of the grid cell autotroph stocks _BiomassesEaten = new double[gridCellStocks.Count][]; _PotentialBiomassesEaten = new double[gridCellStocks.Count][]; // Loop over rows in the jagged arrays and initialise each vector for (int i = 0; i < gridCellStocks.Count; i++) { _BiomassesEaten[i] = new double[gridCellStocks[i].Count]; _PotentialBiomassesEaten[i] = new double[gridCellStocks[i].Count]; } // Loop over functional groups that can be eaten foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat) { // Loop over stocks within the functional group for (int i = 0; i < gridCellStocks[FunctionalGroup].Count; i++) { // Get the mass from this stock that is available for eating (assumes all marine autotrophic organisms are edible) //EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass * 0.1; EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass; // Calculate the potential biomass eaten from this stock by the acting cohort _PotentialBiomassesEaten[FunctionalGroup][i] = CalculatePotentialBiomassEatenMarine(EdibleMass, _BodyMassHerbivore); // Add the time required to handle the potential biomass eaten from this stock to the cumulative total for all stocks _TimeUnitsToHandlePotentialFoodItems += _PotentialBiomassesEaten[FunctionalGroup][i] * CalculateHandlingTimeMarine(_BodyMassHerbivore); } } }
/// <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> /// Calculate the potential number of prey that could be gained through predation on each cohort in the 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="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> public void GetEatingPotentialTerrestrial(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { BinnedPreyDensities = new double[gridCellCohorts.Count, NumberOfBins]; // Set the total eaten by the acting cohort to zero _TotalBiomassEatenByCohort = 0.0; // Set the total number of units to handle all potential prey individuals eaten to zero _TimeUnitsToHandlePotentialFoodItems = 0.0; // Get the individual body mass of the acting (predator) cohort _BodyMassPredator = gridCellCohorts[actingCohort].IndividualBodyMass; // Get the abundance of the acting (predator) cohort _AbundancePredator = gridCellCohorts[actingCohort].CohortAbundance; // Pre-calculate individual values for this predator _SpecificPredatorKillRateConstant = _KillRateConstant * Math.Pow(_BodyMassPredator, (_KillRateConstantMassExponent)); _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep = _DeltaT * _ProportionOfTimeEating; _PredatorAssimilationEfficiency = _AssimilationEfficiency; _PredatorNonAssimilation = (1 - _AssimilationEfficiency); // When body sizes are less than one gram, we have a flat handling time relationship to stop small things have extraordinarily short handling times // if (_BodyMassPredator > 1.0) // { _ReferenceMassRatioScalingTerrestrial = HandlingTimeScalarTerrestrial * Math.Pow(_ReferenceMass / _BodyMassPredator, _HandlingTimeExponentTerrestrial); // } // else // { // _ReferenceMassRatioScalingTerrestrial = HandlingTimeScalarTerrestrial * _ReferenceMass / _BodyMassPredator; // } _PredatorAbundanceMultipliedByTimeEating = _AbundancePredator * _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep; _PredatorLogOptimalPreyBodySizeRatio = gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio; LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio = Math.Log(_BodyMassPredator) + _PredatorLogOptimalPreyBodySizeRatio; // Calculate the abundance of prey in each of the prey mass bins PopulateBinnedPreyAbundance(gridCellCohorts, actingCohort, FunctionalGroupIndicesToEat, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // 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 body mass of individuals in this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Get the bin number of this prey cohort PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Check whether the prey cohort still exists in the model (i.e. body mass > 0) if ((0 < PreyMassBinNumber) && (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0)) { // Calculate the potential abundance from this cohort eaten by the acting cohort _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledTerrestrial( gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup, _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio); // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] * CalculateHandlingTimeTerrestrial(_BodyMassPrey); } else { // Assign a potential abundance eaten of zero _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0; } } } // No cannibalism; do this outside the loop to speed up the calculations _TimeUnitsToHandlePotentialFoodItems -= PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] * CalculateHandlingTimeTerrestrial(_BodyMassPredator); PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] = 0.0; }
/// <summary> /// Calculate the potential number of prey that could be gained through predation on each cohort in the 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="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> public void GetEatingPotentialMarine(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { BinnedPreyDensities = new double[gridCellCohorts.Count, NumberOfBins]; // Set the total eaten by the acting cohort to zero _TotalBiomassEatenByCohort = 0.0; // Set the total number of units to handle all potential prey individuals eaten to zero _TimeUnitsToHandlePotentialFoodItems = 0.0; // Get the individual body mass of the acting (predator) cohort _BodyMassPredator = gridCellCohorts[actingCohort].IndividualBodyMass; // Get the abundance of the acting (predator) cohort _AbundancePredator = gridCellCohorts[actingCohort].CohortAbundance; // Pre-calculate individual values for this predator to speed things up _SpecificPredatorKillRateConstant = _KillRateConstant * Math.Pow(_BodyMassPredator, (_KillRateConstantMassExponent)); _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep = _DeltaT * _ProportionOfTimeEating; _PredatorAssimilationEfficiency = _AssimilationEfficiency; _PredatorNonAssimilation = (1 - _AssimilationEfficiency); _DietIsAllSpecial = madingleyCohortDefinitions.GetTraitNames("Diet", actingCohort[0]) == "allspecial"; _PredatorLogOptimalPreyBodySizeRatio = gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio; // If a filter feeder, then optimal body size is a value not a ratio: convert it to a ratio to ensure that all calculations work correctly if (_DietIsAllSpecial) { // Optimal body size is actually a value, not a ratio, so convert it to a ratio based on the present body size _PredatorLogOptimalPreyBodySizeRatio = Math.Log( Math.Exp(gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio) / gridCellCohorts[actingCohort[0]][actingCohort[1]].IndividualBodyMass); } // Calculate the reference mass scaling ratio _ReferenceMassRatioScalingMarine = HandlingTimeScalarMarine * Math.Pow(_ReferenceMass / _BodyMassPredator, _HandlingTimeExponentMarine); _PredatorAbundanceMultipliedByTimeEating = _AbundancePredator * _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep; LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio = Math.Log(_BodyMassPredator) + _PredatorLogOptimalPreyBodySizeRatio; // Calculate the abundance of prey in each of the prey mass bins PopulateBinnedPreyAbundance(gridCellCohorts, actingCohort, FunctionalGroupIndicesToEat, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Loop over potential prey functional groups foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) { // Eating operates differently for planktivores // This can certainly be sped up if (_DietIsAllSpecial) { // Loop over cohorts within the functional group for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++) { // Get the body mass of individuals in this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Get the bin number of this prey cohort PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Check whether // The prey cohort is within the feeding range of the predator // the prey cohort still exists in the model (i.e. body mass > 0) // Currently having whales etc eat everything, but preferentially feed on very small things (i.e. filter feeders) if ((_PlanktonFunctionalGroups[FunctionalGroup]) && (0 < PreyMassBinNumber) && (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0)) { // Calculate the potential abundance from this cohort eaten by the acting cohort _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledMarine( gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup, _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio); // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] * CalculateHandlingTimeMarine(_BodyMassPrey); } else { // Assign a potential abundance eaten of zero _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0; } } } else { // Loop over cohorts within the functional group for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++) { // Get the body mass of individuals in this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Get the bin number of this prey cohort PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Check whether // The prey cohort is within the feeding range of the predator // the prey cohort still exists in the model (i.e. body mass > 0) if ((0 < PreyMassBinNumber) && (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0)) { // Calculate the potential abundance from this cohort eaten by the acting cohort _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledMarine( gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup, _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio); // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] * CalculateHandlingTimeMarine(_BodyMassPrey); } else { // Assign a potential abundance eaten of zero _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0; } } } } // No cannibalism; do this outside the loop to speed up the calculations _TimeUnitsToHandlePotentialFoodItems -= PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] * CalculateHandlingTimeMarine(_BodyMassPredator); PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] = 0.0; }
/// <summary> /// Initialises predation implementation each time step /// </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="madingleyCohortDefinitions">The definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param> /// <remarks>This only works if: a) predation is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips /// It is critical to run this every time step</remarks> public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { // Get the functional group indices of all heterotroph cohorts (i.e. potential prey) _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "heterotroph", false); // Initialise the vector to hold the number of cohorts in each functional group at the start of the time step NumberCohortsPerFunctionalGroupNoNewCohorts = new int[gridCellCohorts.Count]; // Initialise the jagged arrays to hold the potential and actual numbers of prey eaten in each of the grid cell cohorts _AbundancesEaten = new double[gridCellCohorts.Count][]; _PotentialAbundanceEaten = new double[gridCellCohorts.Count][]; // Initialise the vector to identify carnivore cohorts _CarnivoreFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; _OmnivoreFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; _PlanktonFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; // Loop over rows in the jagged arrays, initialise each vector within the jagged arrays, and calculate the current number of cohorts in // each functional group for (int i = 0; i < gridCellCohorts.Count; i++) { // Calculate the current number of cohorts in this functional group int NumCohortsThisFG = gridCellCohorts[i].Count; NumberCohortsPerFunctionalGroupNoNewCohorts[i] = NumCohortsThisFG; // Initialise the jagged arrays _AbundancesEaten[i] = new double[NumberCohortsPerFunctionalGroupNoNewCohorts[i]]; _PotentialAbundanceEaten[i] = new double[NumberCohortsPerFunctionalGroupNoNewCohorts[i]]; } // Loop over functional groups that are potential prey and determine which are carnivores foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) { _CarnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "carnivore"; } foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) { _OmnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "omnivore"; } foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) { _PlanktonFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Mobility", FunctionalGroup) == "planktonic"; } }
/// <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> /// Initializes an implementation of 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="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="implementationKey">The name of the implementation of eating to initialize</param> /// <remarks>Eating needs to be initialized every time step</remarks> public void InitializeEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, string implementationKey) { // Initialize the implementation of the eating process Implementations[implementationKey].InitializeEatingPerTimeStep(gridCellCohorts, gridCellStocks, madingleyCohortDefinitions, madingleyStockDefinitions); }
/// <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); }
public InputModelState(string outputPath, string filename, ModelGrid ecosystemModelGrid, List <uint[]> cellList) { //Set the input state flag to be true _InputState = true; // Construct the string required to access the file using Scientific Dataset string _ReadFileString = "msds:nc?file=input/ModelStates/" + filename + ".nc&openMode=readOnly"; // Open the data file using Scientific Dataset DataSet StateDataSet = DataSet.Open(_ReadFileString); float[] Latitude = StateDataSet.GetData <float[]>("Latitude"); float[] Longitude = StateDataSet.GetData <float[]>("Longitude"); float[] CohortFunctionalGroup = StateDataSet.GetData <float[]>("Cohort Functional Group"); float[] Cohort = StateDataSet.GetData <float[]>("Cohort"); float[] StockFunctionalGroup = StateDataSet.GetData <float[]>("Stock Functional Group"); float[] Stock = StateDataSet.GetData <float[]>("Stock"); // Check that the longitudes and latitudes in the input state match the cell environment for (int la = 0; la < Latitude.Length; la++) { Debug.Assert(ecosystemModelGrid.GetCellEnvironment((uint)la, 0)["Latitude"][0] == Latitude[la], "Error: input-state grid doesn't match current model grid"); } for (int lo = 0; lo < Longitude.Length; lo++) { Debug.Assert(ecosystemModelGrid.GetCellEnvironment(0, (uint)lo)["Longitude"][0] == Longitude[lo], "Error: input-state grid doesn't match current model grid"); } List <double[, , ]> CohortJuvenileMass = new List <double[, , ]>(); List <double[, , ]> CohortAdultMass = new List <double[, , ]>(); List <double[, , ]> CohortIndividualBodyMass = new List <double[, , ]>(); List <double[, , ]> CohortCohortAbundance = new List <double[, , ]>(); List <double[, , ]> CohortLogOptimalPreyBodySizeRatio = new List <double[, , ]>(); List <double[, , ]> CohortBirthTimeStep = new List <double[, , ]>(); List <double[, , ]> CohortProportionTimeActive = new List <double[, , ]>(); List <double[, , ]> CohortTrophicIndex = new List <double[, , ]>(); double[,,,] tempData = new double[Latitude.Length, Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]; tempData = StateDataSet.GetData <double[, , , ]>("CohortJuvenileMass"); for (int la = 0; la < Latitude.Length; la++) { CohortJuvenileMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortJuvenileMass[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortAdultMass"); for (int la = 0; la < Latitude.Length; la++) { CohortAdultMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortAdultMass[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortIndividualBodyMass"); for (int la = 0; la < Latitude.Length; la++) { CohortIndividualBodyMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortIndividualBodyMass[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortCohortAbundance"); for (int la = 0; la < Latitude.Length; la++) { CohortCohortAbundance.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortCohortAbundance[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortLogOptimalPreyBodySizeRatio"); for (int la = 0; la < Latitude.Length; la++) { CohortLogOptimalPreyBodySizeRatio.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortLogOptimalPreyBodySizeRatio[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortBirthTimeStep"); for (int la = 0; la < Latitude.Length; la++) { CohortBirthTimeStep.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortBirthTimeStep[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortProportionTimeActive"); for (int la = 0; la < Latitude.Length; la++) { CohortProportionTimeActive.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortProportionTimeActive[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortTrophicIndex"); for (int la = 0; la < Latitude.Length; la++) { CohortTrophicIndex.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortTrophicIndex[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } _GridCellCohorts = new GridCellCohortHandler[Latitude.Length, Longitude.Length]; long temp = 0; for (int cell = 0; cell < cellList.Count; cell++) { _GridCellCohorts[cellList[cell][0], cellList[cell][1]] = new GridCellCohortHandler(CohortFunctionalGroup.Length); for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { _GridCellCohorts[cellList[cell][0], cellList[cell][1]][fg] = new List <Cohort>(); for (int c = 0; c < Cohort.Length; c++) { if (CohortCohortAbundance[(int)cellList[cell][0]][cellList[cell][1], fg, c] > 0.0) { Cohort TempCohort = new Cohort( (byte)fg, CohortJuvenileMass[(int)cellList[cell][0]][cellList[cell][1], fg, c], CohortAdultMass[(int)cellList[cell][0]][cellList[cell][1], fg, c], CohortIndividualBodyMass[(int)cellList[cell][0]][cellList[cell][1], fg, c], CohortCohortAbundance[(int)cellList[cell][0]][cellList[cell][1], fg, c], Math.Exp(CohortLogOptimalPreyBodySizeRatio[(int)cellList[cell][0]][cellList[cell][1], fg, c]), Convert.ToUInt16(CohortBirthTimeStep[(int)cellList[cell][0]][cellList[cell][1], fg, c]), CohortProportionTimeActive[(int)cellList[cell][0]][cellList[cell][1], fg, c], ref temp, CohortTrophicIndex[(int)cellList[cell][0]][cellList[cell][1], fg, c], false); _GridCellCohorts[cellList[cell][0], cellList[cell][1]][fg].Add(TempCohort); } } } } CohortJuvenileMass.RemoveRange(0, CohortJuvenileMass.Count); CohortAdultMass.RemoveRange(0, CohortAdultMass.Count); CohortIndividualBodyMass.RemoveRange(0, CohortIndividualBodyMass.Count); CohortCohortAbundance.RemoveRange(0, CohortCohortAbundance.Count); CohortLogOptimalPreyBodySizeRatio.RemoveRange(0, CohortLogOptimalPreyBodySizeRatio.Count); CohortBirthTimeStep.RemoveRange(0, CohortBirthTimeStep.Count); CohortProportionTimeActive.RemoveRange(0, CohortProportionTimeActive.Count); CohortTrophicIndex.RemoveRange(0, CohortTrophicIndex.Count); List <double[, , ]> StockIndividualBodyMass = new List <double[, , ]>(); List <double[, , ]> StockTotalBiomass = new List <double[, , ]>(); double[,,,] tempData2 = new double[Latitude.Length, Longitude.Length, StockFunctionalGroup.Length, Stock.Length]; tempData2 = StateDataSet.GetData <double[, , , ]>("StockIndividualBodyMass"); for (int la = 0; la < Latitude.Length; la++) { StockIndividualBodyMass.Add(new double[Longitude.Length, StockFunctionalGroup.Length, Stock.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < StockFunctionalGroup.Length; fg++) { for (int c = 0; c < Stock.Length; c++) { StockIndividualBodyMass[(int)cell[0]][cell[1], fg, c] = tempData2[ cell[0], cell[1], fg, c]; } } } tempData2 = StateDataSet.GetData <double[, , , ]>("StockTotalBiomass"); for (int la = 0; la < Latitude.Length; la++) { StockTotalBiomass.Add(new double[Longitude.Length, StockFunctionalGroup.Length, Stock.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < StockFunctionalGroup.Length; fg++) { for (int c = 0; c < Stock.Length; c++) { StockTotalBiomass[(int)cell[0]][cell[1], fg, c] = tempData2[ cell[0], cell[1], fg, c]; } } } _GridCellStocks = new GridCellStockHandler[Latitude.Length, Longitude.Length]; for (int cell = 0; cell < cellList.Count; cell++) { _GridCellStocks[cellList[cell][0], cellList[cell][1]] = new GridCellStockHandler(StockFunctionalGroup.Length); for (int fg = 0; fg < StockFunctionalGroup.Length; fg++) { _GridCellStocks[cellList[cell][0], cellList[cell][1]][fg] = new List <Stock>(); for (int c = 0; c < Stock.Length; c++) { if (StockTotalBiomass[(int)cellList[cell][0]][cellList[cell][1], fg, c] > 0.0) { Stock TempStock = new Stock( (byte)fg, StockIndividualBodyMass[(int)cellList[cell][0]][cellList[cell][1], fg, c], StockTotalBiomass[(int)cellList[cell][0]][cellList[cell][1], fg, c]); _GridCellStocks[cellList[cell][0], cellList[cell][1]][fg].Add(TempStock); } } } } }
/// <summary> /// Initialises herbivory implementation each time step /// </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="madingleyCohortDefinitions">The definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param> /// <remarks>This only works if: a) herbivory is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips /// It is critical to run this every time step</remarks> public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { // Get the functional group indices of all autotroph stocks _FunctionalGroupIndicesToEat = madingleyStockDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "Autotroph", false); }
/// <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> /// Remove human appropriated matter from the grid cell autotroph stocks /// </summary> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="humanNPPScenario">The type of NPP extraction to apply</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingStock">The position of the acting stock in the jagged array of grid cell stocks</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="burninSteps">The number of steps to run before impact is simulated</param> /// <param name="impactSteps">The number of time steps to apply the impact for (for 'temporary' scenarios)</param> /// <param name="impactCell">Whether this cell should have human impacts applied</param> /// <remarks>Scenario types are: 'no' = no removal; 'hanpp' = appropriated NPP estimate from input map; constant = constant appropriation after burn-in; /// temporary = constant after burn-in until specified time; value = proportion of plant biomass appropriated</remarks> public double RemoveHumanAppropriatedMatter(double wetMatterNPP, SortedList <string, double[]> cellEnvironment, Tuple <string, double, double> humanNPPScenario, GridCellStockHandler gridCellStocks, int[] actingStock, uint currentTimestep, uint burninSteps, uint impactSteps, uint recoverySteps, uint instantStep, uint numInstantStep, Boolean impactCell, string globalModelTimestepUnits) { double RemovalRate = 0.0; if (impactCell) { // Factor to convert NPP from units per m2 to units per km2 double m2Tokm2Conversion = 1000000.0; if (humanNPPScenario.Item1 == "hanpp") { if (currentTimestep > burninSteps) { // Loop over stocks in the grid cell and calculate the total biomass of all stocks double TotalAutotrophBiomass = 0.0; foreach (var stockFunctionalGroup in gridCellStocks) { for (int i = 0; i < stockFunctionalGroup.Count; i++) { TotalAutotrophBiomass += stockFunctionalGroup[i].TotalBiomass; } } // Get the total amount of NPP appropriated by humans from this cell double HANPP = cellEnvironment["HANPP"][0]; // If HANPP value is missing, then assume zero if (HANPP == cellEnvironment["Missing Value"][0]) { HANPP = 0.0; } HANPP *= cellEnvironment["Seasonality"][currentTimestep % 12]; // Allocate HANPP for this stock according to the proportion of total autotroph biomass that the stock represents if (TotalAutotrophBiomass == 0.0) { HANPP = 0.0; } else { HANPP *= (gridCellStocks[actingStock].TotalBiomass / TotalAutotrophBiomass); } // Convert gC/m2/month to gC/km2/month HANPP *= m2Tokm2Conversion; // Multiply by cell area (in km2) to get g/cell/day HANPP *= cellEnvironment["Cell Area"][0]; // Convert from gC to g dry matter double DryMatterAppropriated = HANPP * 2; // Convert from g dry matter to g wet matter double WetMatterAppropriated = DryMatterAppropriated * 2; //Calculate the rate of HANPP offtake if (wetMatterNPP.CompareTo(0.0) == 0) { RemovalRate = 0.0; } else { RemovalRate = Math.Min(1.0, WetMatterAppropriated / wetMatterNPP); } // Remove human appropriated autotroph biomass from total autotroph biomass //gridCellStocks[actingStock].TotalBiomass -= WetMatterAppropriated; //if (gridCellStocks[actingStock].TotalBiomass < 0.0) gridCellStocks[actingStock].TotalBiomass = 0.0; } } else if (humanNPPScenario.Item1 == "no") { // Do not remove any autotroph biomass } else if (humanNPPScenario.Item1 == "constant") { // If the burn-in period has been completed, then remove the specified constant // fraction from the acting autotroph stock if (currentTimestep > burninSteps) { //gridCellStocks[actingStock].TotalBiomass -= (gridCellStocks[actingStock].TotalBiomass * // humanNPPScenario.Item2); RemovalRate = humanNPPScenario.Item2; } } else if (humanNPPScenario.Item1 == "temporary") { // If the spin-up period has been completed and the period of impact has not elapsed, // then remove the specified constant fraction from the acting autotroph stock if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps))) { //gridCellStocks[actingStock].TotalBiomass -= (gridCellStocks[actingStock].TotalBiomass * // humanNPPScenario.Item2); RemovalRate = humanNPPScenario.Item2; } } else if (humanNPPScenario.Item1 == "escalating") { // If the spin-up period has been completed, then remove a proportion of plant matter // according to the number of time-steps that have elapsed since the spin-up ended if (currentTimestep > burninSteps) { //gridCellStocks[actingStock].TotalBiomass -= gridCellStocks[actingStock].TotalBiomass * // (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); RemovalRate = (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); } } else if (humanNPPScenario.Item1 == "temp-escalating") { // If the spin-up period has been completed and the period of impact has not elapsed, // then remove a proportion of plant matter // according to the number of time-steps that have elapsed since the spin-up ended if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps))) { //gridCellStocks[actingStock].TotalBiomass -= gridCellStocks[actingStock].TotalBiomass * // (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); RemovalRate = (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); } } else if (humanNPPScenario.Item1 == "temp-escalating-const-rate") { // If the spin-up period has been completed and the period of impact (specified by the third scenario element // has not elapsed, // then remove a proportion of plant matter // according to the number of time-steps that have elapsed since the spin-up ended int ConstImpactSteps = Convert.ToInt32(humanNPPScenario.Item3 * _Utilities.ConvertTimeUnits("year", globalModelTimestepUnits)); if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + ConstImpactSteps))) { //gridCellStocks[actingStock].TotalBiomass -= gridCellStocks[actingStock].TotalBiomass * // (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); RemovalRate = (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); } } else if (humanNPPScenario.Item1 == "temp-escalating-const-rate-duration") { // If the spin-up period has been completed and the period of impact (specified by the third scenario element // has not elapsed, // then remove a proportion of plant matter // according to the number of time-steps that have elapsed since the spin-up ended int ConstImpactSteps = Convert.ToInt32(humanNPPScenario.Item3 * _Utilities.ConvertTimeUnits("year", globalModelTimestepUnits)); if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps))) { //gridCellStocks[actingStock].TotalBiomass -= gridCellStocks[actingStock].TotalBiomass * // (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); RemovalRate = (Math.Min(1.0, Math.Min(((ConstImpactSteps / 12.0) * humanNPPScenario.Item2), (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2)))); } } else if (humanNPPScenario.Item1 == "temp-escalating-declining") { // If the spin-up period has been completed, then apply a level of harvesting // according to the number of time-steps that have elapsed since the spin-up ended if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps))) { //gridCellStocks[actingStock].TotalBiomass -= gridCellStocks[actingStock].TotalBiomass * // (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); RemovalRate = Math.Max(0.0, (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2)))); } else if ((currentTimestep > (burninSteps + impactSteps)) & (currentTimestep <= (burninSteps + impactSteps + recoverySteps))) { //gridCellStocks[actingStock].TotalBiomass -= gridCellStocks[actingStock].TotalBiomass * // (Math.Min(1.0, (((burninSteps + impactSteps + recoverySteps - currentTimestep) / 12.0) * humanNPPScenario.Item2))); //RemovalRate = (Math.Min(1.0, (((burninSteps + impactSteps + recoverySteps - currentTimestep) / 12.0) * humanNPPScenario.Item2))); RemovalRate = Math.Max(0.0, Math.Min(1.0, ((int)((impactSteps) - (currentTimestep - (burninSteps + impactSteps))) / 12.0) * humanNPPScenario.Item2)); } } else { Debug.Fail("There is no method for the human extraction of NPP scenario specified"); } } return(RemovalRate); }
/// <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> /// 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> /// Gets a state variable density for specified functional groups of specified entity types in a specified grid cell /// </summary> /// <param name="variableName">The name of the variable to get: 'biomass' or 'abundance'</param> /// <param name="traitValue">The functional group trait value to get data for</param> /// <param name="functionalGroups">The functional group indices to get the state variable for</param> /// <param name="latCellIndex">The latitudinal index of the cell</param> /// <param name="lonCellIndex">The longitudinal index of the cell</param> /// <param name="stateVariableType">The type of entity to return the state variable for: 'stock' or 'cohort'</param> /// <param name="modelInitialisation">The Madingley Model initialisation</param> /// <returns>The state variable density for specified functional groups of specified entity types in a specified grid cell</returns> public double GetStateVariableDensity(string variableName, string traitValue, int[] functionalGroups, uint latCellIndex, uint lonCellIndex, string stateVariableType, MadingleyModelInitialisation modelInitialisation) { double returnValue = 0.0; switch (stateVariableType.ToLower()) { case "cohort": GridCellCohortHandler TempCohorts = InternalGrid[latCellIndex, lonCellIndex].GridCellCohorts; switch (variableName.ToLower()) { case "biomass": if (traitValue != "Zooplankton (all)") { foreach (int f in functionalGroups) { foreach (var item in TempCohorts[f]) { returnValue += ((item.IndividualBodyMass + item.IndividualReproductivePotentialMass) * item.CohortAbundance); } } } else { foreach (int f in functionalGroups) { foreach (var item in TempCohorts[f]) { if (item.IndividualBodyMass <= modelInitialisation.PlanktonDispersalThreshold) { returnValue += ((item.IndividualBodyMass + item.IndividualReproductivePotentialMass) * item.CohortAbundance); } } } } break; case "abundance": if (traitValue != "Zooplankton (all)") { foreach (int f in functionalGroups) { foreach (var item in TempCohorts[f]) { returnValue += item.CohortAbundance; } } } else { foreach (int f in functionalGroups) { foreach (var item in TempCohorts[f]) { if (item.IndividualBodyMass <= modelInitialisation.PlanktonDispersalThreshold) { returnValue += item.CohortAbundance; } } } } break; default: Debug.Fail("For cohorts, state variable name must be either 'biomass' or 'abundance'"); break; } break; case "stock": GridCellStockHandler TempStocks = InternalGrid[latCellIndex, lonCellIndex].GridCellStocks; switch (variableName.ToLower()) { case "biomass": foreach (int f in functionalGroups) { foreach (var item in TempStocks[f]) { returnValue += item.TotalBiomass; } } break; default: Debug.Fail("For stocks, state variable name must be 'biomass'"); break; } break; default: Debug.Fail("State variable type must be either 'cohort' or 'stock'"); break; } return(returnValue / (InternalGrid[latCellIndex, lonCellIndex].CellEnvironment["Cell Area"][0])); }
/// <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> /// 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> /// Initialize an implementation of reproduction. This is only in here to satisfy the requirements of IEcologicalProcessWithinGridCells /// </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="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="implementationKey">The name of the reproduction implementation to initialize</param> public void InitializeEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, string implementationKey) { }
/// <summary> /// Initialises herbivory implementation each time step /// </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="madingleyCohortDefinitions">The definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <remarks>This only works if: a) herbivory is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips /// It is critical to run this every time step</remarks> public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, SortedList <string, double[]> cellEnvironment) { // Get the functional group indices of all autotroph stocks _FunctionalGroupIndicesToEat = madingleyStockDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "Autotroph", false); string[] realm = { "Terrestrial", "Marine" }; int realm_index = (int)cellEnvironment["Realm"][0] - 1; string Current_realm = realm[realm_index]; }
/// <summary> /// Initialize an implementation of mortality. This is only in here to satisfy the requirements of IEcologicalProcessAcrossGridCells /// </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="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="implementationKey">The name of the implementation of mortality to initialize</param> public void InitializeEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, string implementationKey, SortedList <string, double[]> cellEnvironmen) { }
/// <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> /// Initialises predation implementation each time step /// </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="madingleyCohortDefinitions">The definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <remarks>This only works if: a) predation is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips /// It is critical to run this every time step</remarks> public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, SortedList <string, double[]> cellEnvironment) { // Get the functional group indices of all heterotroph cohorts (i.e. potential prey) // _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "heterotroph", false); string [] realm = { "Terrestrial", "Marine" }; int realm_index = (int)cellEnvironment["Realm"][0] - 1; string Current_realm = realm[realm_index]; _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("realm", Current_realm, false); // Initialise the vector to hold the number of cohorts in each functional group at the start of the time step NumberCohortsPerFunctionalGroupNoNewCohorts = new int[gridCellCohorts.Count]; // Initialise the jagged arrays to hold the potential and actual numbers of prey eaten in each of the grid cell cohorts _AbundancesEaten = new double[gridCellCohorts.Count][]; _PotentialAbundanceEaten = new double[gridCellCohorts.Count][]; // Initialise the vector to identify carnivore cohorts _CarnivoreFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; _OmnivoreFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; _PlanktonFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; // Loop over rows in the jagged arrays, initialise each vector within the jagged arrays, and calculate the current number of cohorts in // each functional group for (int i = 0; i < gridCellCohorts.Count; i++) { // Calculate the current number of cohorts in this functional group int NumCohortsThisFG = gridCellCohorts[i].Count; NumberCohortsPerFunctionalGroupNoNewCohorts[i] = NumCohortsThisFG; // Initialise the jagged arrays _AbundancesEaten[i] = new double[NumberCohortsPerFunctionalGroupNoNewCohorts[i]]; _PotentialAbundanceEaten[i] = new double[NumberCohortsPerFunctionalGroupNoNewCohorts[i]]; } // Loop over functional groups that are potential prey and determine which are carnivores THIS IS COMMENTED OUT AS IT'S NOT USED //foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) // _CarnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "carnivore"; //foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) // _OmnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "omnivore"; //foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) // _PlanktonFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Mobility", FunctionalGroup) == "planktonic"; }