/// <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> /// Calculate the proportion of time for which this cohort could be active and assign it to the cohort's properties /// </summary> /// <param name="actingCohort">The Cohort for which proportion of time active is being calculated</param> /// <param name="cellEnvironment">The environmental information for current grid cell</param> /// <param name="madingleyCohortDefinitions">Functional group definitions and code to interrogate the cohorts in current grid cell</param> /// <param name="currentTimestep">Current timestep index</param> /// <param name="currentMonth">Current month</param> public void AssignProportionTimeActive(Cohort actingCohort, SortedList <string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, uint currentTimestep, uint currentMonth) { double Realm = cellEnvironment["Realm"][0]; //Only work on heterotroph cohorts if (madingleyCohortDefinitions.GetTraitNames("Heterotroph/Autotroph", actingCohort.FunctionalGroupIndex) == "heterotroph") { //Check if this is an endotherm or ectotherm Boolean Endotherm = madingleyCohortDefinitions.GetTraitNames("Endo/Ectotherm", actingCohort.FunctionalGroupIndex) == "endotherm"; if (Endotherm) { //Assumes the whole timestep is suitable for endotherms to be active - actual time active is therefore the proportion specified for this functional group. actingCohort.ProportionTimeActive = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("proportion suitable time active", actingCohort.FunctionalGroupIndex); } else { //If ectotherm then use realm specific function if (Realm == 1.0) { actingCohort.ProportionTimeActive = CalculateProportionTimeSuitableTerrestrial(cellEnvironment, currentMonth, Endotherm) * madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("proportion suitable time active", actingCohort.FunctionalGroupIndex); } else { actingCohort.ProportionTimeActive = CalculateProportionTimeSuitableMarine(cellEnvironment, currentMonth, Endotherm) * madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("proportion suitable time active", actingCohort.FunctionalGroupIndex); } } } }
/// <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"); } }
/// <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 ecological processes that operate on stocks within a single grid cell /// </summary> ///<param name="gridCellStocks">The stocks in the current grid cell</param> ///<param name="actingStock">The acting stock</param> ///<param name="cellEnvironment">The stocks in the current grid cell</param> ///<param name="environmentalDataUnits">List of units associated with the environmental variables</param> ///<param name="humanNPPScenario">The human appropriation of NPP scenario to apply</param> ///<param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> ///<param name="currentTimeStep">The current model time step</param> ///<param name="burninSteps">The number of time steps to spin the model up for before applying human impacts</param> ///<param name="impactSteps">The number of time steps to apply human impacts for</param> ///<param name="globalModelTimeStepUnit">The time step unit used in the model</param> ///<param name="trackProcesses">Whether to track properties of ecological processes</param> ///<param name="tracker">An instance of the ecological process tracker</param> ///<param name="globalTracker">An instance of the global process tracker</param> ///<param name="currentMonth">The current model month</param> ///<param name="outputDetail">The level of detail to use in outputs</param> ///<param name="specificLocations">Whether to run the model for specific locations</param> ///<param name="impactCell">Whether this cell should have human impacts applied</param> public void RunWithinCellEcology(GridCellStockHandler gridCellStocks, int[] actingStock, SortedList<string, double[]> cellEnvironment, SortedList<string, string> environmentalDataUnits, Madingley.Common.ScenarioParameter humanNPPScenario, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimeStep, uint burninSteps, uint impactSteps, uint recoverySteps, uint instantStep, uint numInstantSteps, string globalModelTimeStepUnit, Boolean trackProcesses, ProcessTracker tracker, GlobalProcessTracker globalTracker, uint currentMonth, string outputDetail, bool specificLocations, Boolean impactCell) { if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "marine") { // Run the autotroph processor MarineNPPtoAutotrophStock.ConvertNPPToAutotroph(cellEnvironment, gridCellStocks, actingStock, environmentalDataUnits["LandNPP"], environmentalDataUnits["OceanNPP"], currentTimeStep, globalModelTimeStepUnit, tracker, globalTracker, outputDetail, specificLocations, currentMonth); } else if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "terrestrial") { // Run the dynamic plant model to update the leaf stock for this time step double WetMatterNPP = DynamicPlantModel.UpdateLeafStock(cellEnvironment, gridCellStocks, actingStock, currentTimeStep, madingleyStockDefinitions. GetTraitNames("leaf strategy", actingStock[0]).Equals("deciduous"), globalModelTimeStepUnit, tracker, globalTracker, currentMonth, outputDetail, specificLocations); /// <summary> double fhanpp = HANPP.RemoveHumanAppropriatedMatter(WetMatterNPP, cellEnvironment, humanNPPScenario, gridCellStocks, actingStock, currentTimeStep, burninSteps, impactSteps, recoverySteps, instantStep, numInstantSteps, impactCell, globalModelTimeStepUnit); // Apply human appropriation of NPP gridCellStocks[actingStock].TotalBiomass += WetMatterNPP * (1.0 - fhanpp); if (globalTracker.TrackProcesses) { globalTracker.RecordHANPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0], fhanpp); } if (gridCellStocks[actingStock].TotalBiomass < 0.0) gridCellStocks[actingStock].TotalBiomass = 0.0; } else { Debug.Fail("Stock must be classified as belonging to either the marine or terrestrial realm"); } }
/// <summary> /// 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 dispersal /// </summary> public void RunCrossGridCellEcologicalProcess(uint[] cellIndex, ModelGrid gridForDispersal, bool dispersalOnly, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentMonth) { // Create a temporary handler for grid cell cohorts GridCellCohortHandler WorkingGridCellCohorts; // Get the lat and lon indices uint ii = cellIndex[0]; uint jj = cellIndex[1]; // A boolean to check that the environmental layer exists bool varExists; // Check to see if the cell is marine double CellRealm = gridForDispersal.GetEnviroLayer("Realm", 0, ii, jj, out varExists); // Go through all of the cohorts in turn and see if they disperse WorkingGridCellCohorts = gridForDispersal.GetGridCellCohorts(ii, jj); // Loop through cohorts, and perform dispersal according to cohort type and status for (int kk = 0; kk < WorkingGridCellCohorts.Count; kk++) { // Work through the list of cohorts for (int ll = 0; ll < WorkingGridCellCohorts[kk].Count; ll++) { // Check to see if the cell is marine and the cohort type is planktonic if (CellRealm == 2.0 && ((madingleyCohortDefinitions.GetTraitNames("Mobility", WorkingGridCellCohorts[kk][ll].FunctionalGroupIndex) == "planktonic") || (WorkingGridCellCohorts[kk][ll].IndividualBodyMass <= PlanktonThreshold))) { // Run advective dispersal Implementations["basic advective dispersal"].RunDispersal(cellIndex, gridForDispersal, WorkingGridCellCohorts[kk][ll], kk, ll, currentMonth); } // Otherwise, if mature do responsive dispersal else if (WorkingGridCellCohorts[kk][ll].MaturityTimeStep < uint.MaxValue) { // Run diffusive dispersal Implementations["basic responsive dispersal"].RunDispersal(cellIndex, gridForDispersal, WorkingGridCellCohorts[kk][ll], kk, ll, currentMonth); } // If the cohort is immature, run diffusive dispersal else { Implementations["basic diffusive dispersal"].RunDispersal(cellIndex, gridForDispersal, WorkingGridCellCohorts[kk][ll], kk, ll, currentMonth); } } } }
/// <summary> /// Run reproduction /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimeStep">The current model time step</param> /// <param name="processTracker">An instance of ProcessTracker to hold diagnostics for eating</param> /// <param name="partial">Thread-locked variables for the parallelised version</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used for this model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimeStep, ProcessTracker processTracker, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { // Holds the reproductive strategy of a cohort bool _Iteroparous = madingleyCohortDefinitions.GetTraitNames("reproductive strategy", actingCohort[0]) == "iteroparity"; // Assign mass to reproductive potential Implementations["reproduction basic"].RunReproductiveMassAssignment(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimeStep, processTracker); // Run reproductive events. Note that we can't skip juveniles here as they could conceivably grow to adulthood and get enough biomass to reproduce in a single time step // due to other ecological processes Implementations["reproduction basic"].RunReproductionEvents(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimeStep, processTracker, ref partial, _Iteroparous, currentMonth); }
/// <summary> /// Run 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> /// Sets up the outputs associated with the high level of output detail /// </summary> /// <param name="ecosystemModelGrid">The model grid</param> /// <param name="cellIndices">The indices of active cells in the model grid</param> /// <param name="cellNumber">The index of the current cell in the list of active cells</param> /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="marineCell">Whether the current cell is a marine cell</param> private void SetUpHighLevelOutputs(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellNumber, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, Boolean marineCell) { // Create an SDS object for outputs by mass bin // MassBinsOutput = SDSCreator.CreateSDS("netCDF", "MassBins" + _OutputSuffix, _OutputPath); MassBinsOutputMemory = SDSCreator.CreateSDSInMemory(true); // Add relevant output variables to the mass bin output file string[] MassBinDimensions = { "Time step", "Mass bin" }; string[] DoubleMassBinDimensions = new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" }; if (OutputMetrics) { DataConverter.AddVariable(MassBinsOutputMemory, "Trophic Index Distribution", 2, new string[] {"Time step","Trophic Index Bins"}, ecosystemModelGrid.GlobalMissingValue, TimeSteps, Metrics.TrophicIndexBinValues); } if (marineCell) { foreach (string TraitValue in CohortTraitIndicesMarine.Keys) { DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps); } } else { foreach (string TraitValue in CohortTraitIndices.Keys) { DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps); } } // Create an SDS object in memory for tracked cohorts outputs // TrackedCohortsOutput = SDSCreator.CreateSDS("netCDF", "TrackedCohorts" + _OutputSuffix, _OutputPath); TrackedCohortsOutputMemory = SDSCreator.CreateSDSInMemory(true); // Initialise list to hold tracked cohorts TrackedCohorts = new List<uint>(); // Identify cohorts to track GridCellCohortHandler TempCohorts = null; bool FoundCohorts = false; // Get a local copy of the cohorts in the grid cell TempCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellNumber][0], cellIndices[cellNumber][1]); // Loop over functional groups and check whether any cohorts exist in this grid cell foreach (var CohortList in TempCohorts) { if (CohortList.Count > 0) { FoundCohorts = true; break; } } // If there are some cohorts in the grid cell, then setup the tracked cohorts if (FoundCohorts) { // Initialise stream writer to hold details of tracked cohorts StreamWriter sw = new StreamWriter(_OutputPath + "TrackedCohortProperties" + _OutputSuffix + ".txt"); sw.WriteLine("Output ID\tCohort ID\tFunctional group index\tNutrition source\tDiet\tRealm\tMobility\tJuvenile mass\tAdult mass"); // Counter for tracked cohorts int TrackedCohortCounter = 0; for (int i = 0; i < TempCohorts.Count; i++) { if (TempCohorts[i].Count > 0) { for (int j = 0; j < TempCohorts[i].Count; j++) { // Write out properties of the selected cohort sw.WriteLine(Convert.ToString(TrackedCohortCounter) + '\t' + Convert.ToString(TempCohorts[i][j].CohortID[0]) + '\t' + i + '\t' + cohortFunctionalGroupDefinitions.GetTraitNames("Nutrition source", i) + '\t' + cohortFunctionalGroupDefinitions. GetTraitNames("Diet", i) + '\t' + cohortFunctionalGroupDefinitions.GetTraitNames("Realm", i) + '\t' + cohortFunctionalGroupDefinitions.GetTraitNames("Mobility", i) + '\t' + TempCohorts[i][j].JuvenileMass + '\t' + TempCohorts[i][j].AdultMass); // Add the ID of the cohort to the list of tracked cohorts TrackedCohorts.Add(TempCohorts[i][j].CohortID[0]); // Increment the counter of tracked cohorts TrackedCohortCounter++; } } } // Generate an array of floating points to index the tracked cohorts in the output file float[] OutTrackedCohortIDs = new float[TrackedCohortCounter]; for (int i = 0; i < TrackedCohortCounter; i++) { OutTrackedCohortIDs[i] = i; } // Set up outputs for tracked cohorts string[] TrackedCohortsDimensions = { "Time step", "Cohort ID" }; // Add output variables for the tracked cohorts output DataConverter.AddVariable(TrackedCohortsOutputMemory, "Individual body mass", 2, TrackedCohortsDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, OutTrackedCohortIDs); DataConverter.AddVariable(TrackedCohortsOutputMemory, "Number of individuals", 2, TrackedCohortsDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, OutTrackedCohortIDs); // Dispose of the streamwriter sw.Dispose(); } // Get a list of all possible combinations of trait values as a jagged array string[][] TraitValueSearch; if (marineCell) TraitValueSearch = CalculateAllCombinations(CohortTraitValuesMarine[CohortTraits[0]], CohortTraitValuesMarine[CohortTraits[1]]); else TraitValueSearch = CalculateAllCombinations(CohortTraitValues[CohortTraits[0]], CohortTraitValues[CohortTraits[1]]); // Add the functional group indices of these trait combinations to the list of indices of the trait values to consider, // keyed with a concatenated version of the trait values string TraitValueJoin = ""; string[] TimeDimension = { "Time step" }; for (int i = 0; i < TraitValueSearch.Count(); i++) { TraitValueJoin = ""; foreach (string TraitValue in TraitValueSearch[i]) { TraitValueJoin += TraitValue + " "; } if (marineCell) { // Only add indices of marine functional groups int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(CohortTraits, TraitValueSearch[i], true); Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)]; for (int ii = 0; ii < TempIndices.GetLength(0); ii++) { if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Marine", StringComparison.OrdinalIgnoreCase)) { TempIndices2[ii] = true; } } // Extract only the indices which are marine int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(zz => TempIndices2[zz]).ToArray(); if (TempIndices3.Length > 0) { // Extract the values at these indices for (int ii = 0; ii < TempIndices3.Length; ii++) { TempIndices3[ii] = TempIndices[TempIndices3[ii]]; } // Add in the indices for this functional group and this realm CohortTraitIndices.Add(TraitValueJoin, TempIndices3); DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps); DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps); TotalBiomassDensitiesOut.Add(TraitValueJoin, 0.0); TotalDensitiesOut.Add(TraitValueJoin, 0.0); } } else { // Only add indices of terrestrial functional groups int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(CohortTraits, TraitValueSearch[i], true); Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)]; for (int ii = 0; ii < TempIndices.GetLength(0); ii++) { if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Terrestrial", StringComparison.OrdinalIgnoreCase)) { TempIndices2[ii] = true; } } // Extract only the indices which are terrestrial int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(zz => TempIndices2[zz]).ToArray(); if (TempIndices3.Length > 0) { // Extract the values at these indices for (int ii = 0; ii < TempIndices3.Length; ii++) { TempIndices3[ii] = TempIndices[TempIndices3[ii]]; } // Add in the indices for this functional group and this realm CohortTraitIndices.Add(TraitValueJoin, TempIndices3); DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps); DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps); DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps); TotalBiomassDensitiesOut.Add(TraitValueJoin, 0.0); TotalDensitiesOut.Add(TraitValueJoin, 0.0); } } } }
/// <summary> /// Seed grid cell with stocks, as specified in the model input files /// </summary> /// <param name="functionalGroups">A reference to the stock functional group handler</param> /// <param name="cellEnvironment">The environment in the grid cell</param> /// <param name="globalDiagnostics">A list of global diagnostic variables for the model grid</param> private void SeedGridCellStocks(ref FunctionalGroupDefinitions functionalGroups, ref SortedList<string, double[]> cellEnvironment, SortedList<string, double> globalDiagnostics) { // Set the seed for the random number generator from the system time RandomNumberGenerator.SetSeedFromSystemTime(); Stock NewStock; // Define local variables int[] FunctionalGroupsToUse; // Get the individual body masses for organisms in each stock functional group double[] IndividualMass = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("individual mass"); // Check which realm the cell is in if (cellEnvironment["Realm"][0] == 1.0 && _CellEnvironment["Precipitation"][0] != _CellEnvironment["Missing Value"][0] && _CellEnvironment["Temperature"][0] != _CellEnvironment["Missing Value"][0]) { // Get the indices of all terrestrial functional groups FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "terrestrial", true); } else if (cellEnvironment["Realm"][0] == 2.0 && _CellEnvironment["NPP"][0] != _CellEnvironment["Missing Value"][0]) { // Get the indices of all marine functional groups FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "marine", true); } else { // For cells without a realm designation, no functional groups will be used FunctionalGroupsToUse = new int[0]; } // Loop over all functional groups in the model for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++) { // Create a new list to hold the stocks in the grid cell _GridCellStocks[FunctionalGroup] = new List<Stock>(); // If it is a functional group that corresponds to the current realm, then seed the stock if (FunctionalGroupsToUse.Contains(FunctionalGroup)) { if (_CellEnvironment["Realm"][0] == 1.0) { // An instance of the terrestrial carbon model class RevisedTerrestrialPlantModel PlantModel = new RevisedTerrestrialPlantModel(); // Calculate predicted leaf mass at equilibrium for this stock double LeafMass = PlantModel.CalculateEquilibriumLeafMass(_CellEnvironment, functionalGroups.GetTraitNames("leaf strategy", FunctionalGroup) == "deciduous"); // Initialise the new stock with the relevant properties NewStock = new Stock((byte)FunctionalGroup, IndividualMass[FunctionalGroup], LeafMass); // Add the new stock to the list of grid cell stocks _GridCellStocks[FunctionalGroup].Add(NewStock); // Increment the variable tracking the total number of stocks in the model globalDiagnostics["NumberOfStocksInModel"]++; } else if (FunctionalGroupsToUse.Contains(FunctionalGroup)) { // Initialise the new stock with the relevant properties NewStock = new Stock((byte)FunctionalGroup, IndividualMass[FunctionalGroup], 1e12); // Add the new stock to the list of grid cell stocks _GridCellStocks[FunctionalGroup].Add(NewStock); // Increment the variable tracking the total number of stocks in the model globalDiagnostics["NumberOfStocksInModel"]++; } else { } } } }
/// <summary> /// Set up the necessary architecture for generating outputs arranged by trait value /// </summary> /// <param name="cohortFunctionalGroupDefinitions">Functional group definitions for cohorts in the model</param> /// <param name="stockFunctionalGroupDefinitions">Functional group definitions for stocks in the model</param> /// <param name="marineCell">Whether the current cell is a marine cell</param> private void InitialiseTraitBasedOutputs(FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, FunctionalGroupDefinitions stockFunctionalGroupDefinitions, Boolean marineCell) { // Define the cohort traits that will be used to separate outputs CohortTraits = new string[2] { "Nutrition source", "Endo/Ectotherm"}; // Declare a sorted dictionary to hold all unique trait values CohortTraitValues = new SortedDictionary<string, string[]>(); // Declare a sorted dictionary to hold all unique trait values for marine systems CohortTraitValuesMarine = new SortedDictionary<string, string[]>(); // Get the list of functional group indices corresponding to each unique trait value if (marineCell) { // Add all unique trait values to the sorted dictionary foreach (string Trait in CohortTraits) { CohortTraitValuesMarine.Add(Trait, cohortFunctionalGroupDefinitions.GetUniqueTraitValues(Trait)); } foreach (string Trait in CohortTraits) { foreach (string TraitValue in CohortTraitValuesMarine[Trait]) { // Only add indices of marine functional groups int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false); Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)]; for (int ii = 0; ii < TempIndices.GetLength(0); ii++) { if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Marine", StringComparison.OrdinalIgnoreCase)) { TempIndices2[ii] = true; } } // Extract only the indices which are marine int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(i => TempIndices2[i]).ToArray(); if (TempIndices3.Length > 0) { // Extract the values at these indices for (int ii = 0; ii < TempIndices3.Length; ii++) { TempIndices3[ii] = TempIndices[TempIndices3[ii]]; } // Add in the indices for this functional group and this realm CohortTraitIndicesMarine.Add(TraitValue, TempIndices3); } } } if (TrackMarineSpecifics) { // Add in the specific classes of zooplankton and baleen whales // There are functional groups representing obligate zooplankton string[] TempString = new string[1] { "Obligate zooplankton" }; CohortTraitValuesMarine.Add("Obligate zooplankton", TempString); CohortTraitIndicesMarine.Add("Obligate zooplankton", cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Mobility", "planktonic", false)); // Whales have a special dietary index TempString = new string[1] { "Baleen whales" }; CohortTraitValuesMarine.Add("Baleen whales", TempString); CohortTraitIndicesMarine.Add("Baleen whales", cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Diet", "allspecial", false)); // But we also want all zooplankton, including larval/juvenile stages of other cohorts int[] ZooplanktonIndices1 = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Mobility", "planktonic", false); // Then there are all of the other groups which may have planktonic juveniles. In the ModelGrid.cs class, these cohorts are checked to see // if they have a weight of less than the planktonic dispersal threshold. TempString = new string[3] { "Realm", "Endo/Ectotherm", "Mobility" }; string[] TempString2 = new string[3] { "marine", "ectotherm", "mobile" }; int[] ZooplanktonIndices2 = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(TempString, TempString2, true); CohortTraitIndicesMarine.Add("Zooplankton (all)", ZooplanktonIndices2.Concat(ZooplanktonIndices1).ToArray()); } // Add unique trait values to each of the lists that will contain output data arranged by trait value foreach (string TraitValue in CohortTraitIndicesMarine.Keys) { TotalBiomassDensitiesMarineOut.Add(TraitValue, 0.0); TotalDensitiesMarineOut.Add(TraitValue, 0.0); } } else { // Add all unique trait values to the sorted dictionary foreach (string Trait in CohortTraits) { CohortTraitValues.Add(Trait, cohortFunctionalGroupDefinitions.GetUniqueTraitValues(Trait)); } foreach (string Trait in CohortTraits) { foreach (string TraitValue in CohortTraitValues[Trait]) { // Only add indices of terrestrial functional groups int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false); Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)]; for (int ii = 0; ii < TempIndices.GetLength(0); ii++) { if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Terrestrial", StringComparison.OrdinalIgnoreCase)) { TempIndices2[ii] = true; } } // Extract only the indices which are terrestrial int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(i => TempIndices2[i]).ToArray(); if (TempIndices3.Length > 0) { // Extract the values at these indices for (int ii = 0; ii < TempIndices3.Length; ii++) { TempIndices3[ii] = TempIndices[TempIndices3[ii]]; } // Add in the indices for this functional group and this realm CohortTraitIndices.Add(TraitValue, TempIndices3); } } } // Add unique trait values to each of the lists that will contain output data arranged by trait value foreach (string TraitValue in CohortTraitIndices.Keys) { TotalBiomassDensitiesOut.Add(TraitValue, 0.0); TotalDensitiesOut.Add(TraitValue, 0.0); } } if (marineCell) { // Define the stock traits that will be used to separate outputs StockTraitsMarine = new string[1] { "Heterotroph/Autotroph"}; // Re-initialise the sorted dictionary to hold all unique trait values StockTraitValuesMarine = new SortedDictionary<string, string[]>(); // Add all unique stock trait values to the sorted dictionary foreach (string Trait in StockTraitsMarine) { StockTraitValuesMarine.Add(Trait, stockFunctionalGroupDefinitions.GetUniqueTraitValues(Trait)); } // Get the list of functional group indices corresponding to each unique marine trait value foreach (string Trait in StockTraitsMarine) { foreach (string TraitValue in StockTraitValuesMarine[Trait]) { StockTraitIndicesMarine.Add(TraitValue, stockFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false)); } } // Add unique trait values to each of the lists that will contain output data arranged by trait value foreach (string TraitValue in StockTraitIndicesMarine.Keys) { TotalBiomassDensitiesOut.Add(TraitValue, 0.0); } } else { // Define the stock traits that will be used to separate outputs StockTraits = new string[2] { "Heterotroph/Autotroph", "Leaf strategy" }; // Re-initialise the sorted dictionary to hold all unique trait values StockTraitValues = new SortedDictionary<string, string[]>(); // Add all unique marine stock trait values to the sorted dictionary foreach (string Trait in StockTraits) { StockTraitValues.Add(Trait, stockFunctionalGroupDefinitions.GetUniqueTraitValues(Trait)); } // Get the list of functional group indices corresponding to each unique trait value foreach (string Trait in StockTraits) { foreach (string TraitValue in StockTraitValues[Trait]) { StockTraitIndices.Add(TraitValue, stockFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false)); } } // Add unique trait values to each of the lists that will contain output data arranged by trait value foreach (string TraitValue in StockTraitIndices.Keys) { TotalBiomassDensitiesOut.Add(TraitValue, 0.0); } } }
/// <summary> /// Record the flow of biomass between trophic levels during predation /// </summary> /// <param name="latIndex">The latitudinal index of the current grid cell</param> /// <param name="lonIndex">The longitudinal index of the current grid cell</param> /// <param name="fromFunctionalGroup">The index of the functional group that the biomass is flowing from (i.e. the prey)</param> /// <param name="toFunctionalGroup">The index of the functional group that the biomass is flowing to (i.e. the predator)</param> /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions of cohorts in the model</param> /// <param name="massEaten">The total biomass eaten by the predator cohort</param> /// <param name="predatorBodyMass">The body mass of the predator doing the eating</param> /// <param name="preyBodyMass">The body mass of the prey being eaten</param> /// <param name="initialisation">The Madingley Model initialisation</param> /// <param name="MarineCell">Whether the current cell is a marine cell</param> public void RecordPredationTrophicFlow(uint latIndex, uint lonIndex, int fromFunctionalGroup, int toFunctionalGroup, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, double massEaten, double predatorBodyMass, double preyBodyMass, MadingleyModelInitialisation initialisation, Boolean MarineCell) { int fromIndex = 0; int toIndex = 0; if (initialisation.TrackMarineSpecifics && MarineCell) { // Get the trophic level index of the functional group that mass is flowing from switch (cohortFunctionalGroupDefinitions.GetTraitNames("nutrition source", fromFunctionalGroup)) { case "herbivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", fromFunctionalGroup)) { case "planktonic": fromIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", fromFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", fromFunctionalGroup)) { case "allspecial": fromIndex = 6; break; default: fromIndex = 1; break; } break; default: if (preyBodyMass <= initialisation.PlanktonDispersalThreshold) fromIndex = 5; else fromIndex = 1; break; } break; } break; case "omnivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", fromFunctionalGroup)) { case "planktonic": fromIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", fromFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", fromFunctionalGroup)) { case "allspecial": fromIndex = 6; break; default: fromIndex = 2; break; } break; default: if (preyBodyMass <= initialisation.PlanktonDispersalThreshold) fromIndex = 5; else fromIndex = 2; break; } break; } break; case "carnivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", fromFunctionalGroup)) { case "planktonic": fromIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", fromFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", fromFunctionalGroup)) { case "allspecial": fromIndex = 6; break; default: fromIndex = 3; break; } break; default: if (preyBodyMass <= initialisation.PlanktonDispersalThreshold) fromIndex = 5; else fromIndex = 3; break; } break; } break; default: Debug.Fail("Specified nutrition source is not supported"); break; } // Get the trophic level index of the functional group that mass is flowing to switch (cohortFunctionalGroupDefinitions.GetTraitNames("nutrition source", toFunctionalGroup)) { case "omnivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", toFunctionalGroup)) { case "planktonic": toIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", toFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", toFunctionalGroup)) { case "allspecial": toIndex = 6; break; default: toIndex = 2; break; } break; default: if (predatorBodyMass <= initialisation.PlanktonDispersalThreshold) toIndex = 5; else toIndex = 2; break; } break; } break; case "carnivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", toFunctionalGroup)) { case "planktonic": toIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", toFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", toFunctionalGroup)) { case "allspecial": toIndex = 6; break; default: toIndex = 3; break; } break; default: if (predatorBodyMass <= initialisation.PlanktonDispersalThreshold) toIndex = 5; else toIndex = 3; break; } break; } break; default: Debug.Fail("Specified nutrition source is not supported"); break; } } else { // Get the trophic level index of the functional group that mass is flowing from switch (cohortFunctionalGroupDefinitions.GetTraitNames("nutrition source", fromFunctionalGroup)) { case "herbivore": fromIndex = 1; break; case "omnivore": fromIndex = 2; break; case "carnivore": fromIndex = 3; break; default: Debug.Fail("Specified nutrition source is not supported"); break; } // Get the trophic level index of the functional group that mass is flowing to switch (cohortFunctionalGroupDefinitions.GetTraitNames("nutrition source", toFunctionalGroup)) { case "herbivore": toIndex = 1; break; case "omnivore": toIndex = 2; break; case "carnivore": toIndex = 3; break; default: Debug.Fail("Specified nutrition source is not supported"); break; } } // Add the flow of matter to the matrix of mass flows TrophicMassFlows[latIndex, lonIndex, fromIndex, toIndex] += massEaten; }
/// <summary> /// Seed grid cell with cohorts, as specified in the model input files /// </summary> /// <param name="functionalGroups">The functional group definitions for cohorts in the grid cell</param> /// <param name="cellEnvironment">The environment in the grid cell</param> /// <param name="globalDiagnostics">A list of global diagnostic variables</param> /// <param name="nextCohortID">YThe unique ID to assign to the next cohort produced</param> /// <param name="tracking">boolean to indicate if cohorts are to be tracked in this model</param> /// <param name="totalCellTerrestrialCohorts">The total number of cohorts to be seeded in each terrestrial grid cell</param> /// <param name="totalCellMarineCohorts">The total number of cohorts to be seeded in each marine grid cell</param> /// <param name="DrawRandomly">Whether the model is set to use random draws</param> /// <param name="ZeroAbundance">Set this parameter to 'true' if you want to seed the cohorts with zero abundance</param> private void SeedGridCellCohorts(ref FunctionalGroupDefinitions functionalGroups, ref SortedList<string, double[]> cellEnvironment, SortedList<string, double> globalDiagnostics, Int64 nextCohortID, Boolean tracking, double totalCellTerrestrialCohorts, double totalCellMarineCohorts, Boolean DrawRandomly, Boolean ZeroAbundance) { // Set the seed for the random number generator from the system time RandomNumberGenerator.SetSeedFromSystemTime(); // StreamWriter tempsw = new StreamWriter("C://Temp//adult_juvenile_masses.txt"); // tempsw.WriteLine("adult mass\tjuvenilemass"); // Define local variables double CohortJuvenileMass; double CohortAdultMassRatio; double CohortAdultMass; double ExpectedLnAdultMassRatio; int[] FunctionalGroupsToUse; double NumCohortsThisCell; double TotalNewBiomass =0.0; // Get the minimum and maximum possible body masses for organisms in each functional group double[] MassMinima = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("minimum mass"); double[] MassMaxima = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("maximum mass"); string[] NutritionSource = functionalGroups.GetTraitValuesAllFunctionalGroups("nutrition source"); double[] ProportionTimeActive = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("proportion suitable time active"); //Variable for altering the juvenile to adult mass ratio for marine cells when handling certain functional groups eg baleen whales double Scaling = 0.0; Int64 CohortIDIncrementer = nextCohortID; // Check which realm the cell is in if (cellEnvironment["Realm"][0] == 1.0) { // Get the indices of all terrestrial functional groups FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "terrestrial", true); NumCohortsThisCell = totalCellTerrestrialCohorts; } else { // Get the indices of all marine functional groups FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "marine", true); NumCohortsThisCell = totalCellMarineCohorts; } Debug.Assert(cellEnvironment["Realm"][0] > 0.0, "Missing realm for grid cell"); if (NumCohortsThisCell > 0) { // Loop over all functional groups in the model for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++) { // Create a new list to hold the cohorts in the grid cell _GridCellCohorts[FunctionalGroup] = new List<Cohort>(); // If it is a functional group that corresponds to the current realm, then seed cohorts if (FunctionalGroupsToUse.Contains(FunctionalGroup)) { // Loop over the initial number of cohorts double NumberOfCohortsInThisFunctionalGroup = 1.0; if (!ZeroAbundance) { NumberOfCohortsInThisFunctionalGroup = functionalGroups.GetBiologicalPropertyOneFunctionalGroup("initial number of gridcellcohorts", FunctionalGroup); } for (int jj = 0; jj < NumberOfCohortsInThisFunctionalGroup; jj++) { // Check whether the model is set to randomly draw the body masses of new cohorts if (DrawRandomly) { // Draw adult mass from a log-normal distribution with mean -6.9 and standard deviation 10.0, // within the bounds of the minimum and maximum body masses for the functional group CohortAdultMass = Math.Pow(10, (RandomNumberGenerator.GetUniform() * (Math.Log10(MassMaxima[FunctionalGroup]) - Math.Log10(50 * MassMinima[FunctionalGroup])) + Math.Log10(50 * MassMinima[FunctionalGroup]))); // Terrestrial and marine organisms have different optimal prey/predator body mass ratios if (cellEnvironment["Realm"][0] == 1.0) // Optimal prey body size 10% OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02)); else { if (functionalGroups.GetTraitNames("Diet", FunctionalGroup) == "allspecial") { // Note that for this group // it is actually (despite the name) not an optimal prey body size ratio, but an actual body size. // This is because it is invariant as the predator (filter-feeding baleen whale) grows. // See also the predation classes. OptimalPreyBodySizeRatio = Math.Max(0.00001, RandomNumberGenerator.GetNormal(0.0001, 0.1)); } else { // Optimal prey body size or marine organisms is 10% OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02)); } } // Draw from a log-normal distribution with mean 10.0 and standard deviation 5.0, then add one to obtain // the ratio of adult to juvenile body mass, and then calculate juvenile mass based on this ratio and within the // bounds of the minimum and maximum body masses for this functional group if (cellEnvironment["Realm"][0] == 1.0) { do { ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass); CohortAdultMassRatio = 1.0 + RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5); CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio; } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]); } // In the marine realm, have a greater difference between the adult and juvenile body masses, on average else { uint Counter = 0; Scaling = 0.2; // Use the scaling to deal with baleen whales not having such a great difference do { ExpectedLnAdultMassRatio = 2.5 + Scaling * Math.Log(CohortAdultMass); CohortAdultMassRatio = 1.0 + 10 * RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5); CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio; Counter++; if (Counter > 10) { Scaling -= 0.01; Counter = 0; } } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]); } } else { // Use the same seed for the random number generator every time RandomNumberGenerator.SetSeed((uint)(jj + 1), (uint)((jj + 1) * 3)); // Draw adult mass from a log-normal distribution with mean -6.9 and standard deviation 10.0, // within the bounds of the minimum and maximum body masses for the functional group CohortAdultMass = Math.Pow(10, (RandomNumberGenerator.GetUniform() * (Math.Log10(MassMaxima[FunctionalGroup]) - Math.Log10(50 * MassMinima[FunctionalGroup])) + Math.Log10(50 * MassMinima[FunctionalGroup]))); OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02)); // Draw from a log-normal distribution with mean 10.0 and standard deviation 5.0, then add one to obtain // the ratio of adult to juvenile body mass, and then calculate juvenile mass based on this ratio and within the // bounds of the minimum and maximum body masses for this functional group if (cellEnvironment["Realm"][0] == 1.0) { do { ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass); CohortAdultMassRatio = 1.0 + RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5); CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio; } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]); } // In the marine realm, have a greater difference between the adult and juvenile body masses, on average else { do { ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass); CohortAdultMassRatio = 1.0 + 10 * RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5); CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio; } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]); } } // An instance of Cohort to hold the new cohort Cohort NewCohort; //double NewBiomass = Math.Pow(0.2, (Math.Log10(CohortAdultMass))) * (1.0E9 * _CellEnvironment["Cell Area"][0]) / NumCohortsThisCell; // 3000*(0.6^log(mass)) gives individual cohort biomass density in g ha-1 // * 100 to give g km-2 // * cell area to give g grid cell //*3300/NumCohortsThisCell scales total initial biomass in the cell to some approximately reasonable mass double NewBiomass = (3300 / NumCohortsThisCell) * 100 * 3000 * Math.Pow(0.6, (Math.Log10(CohortJuvenileMass))) * (_CellEnvironment["Cell Area"][0]); TotalNewBiomass += NewBiomass; double NewAbund = 0.0; if (!ZeroAbundance) { NewAbund = NewBiomass / CohortJuvenileMass; } /* // TEMPORARILY MARINE ONLY if (cellEnvironment["Realm"][0] == 1) { NewAbund = 0.0; } */ double TrophicIndex; switch (NutritionSource[FunctionalGroup]) { 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; } // Initialise the new cohort with the relevant properties NewCohort = new Cohort((byte)FunctionalGroup, CohortJuvenileMass, CohortAdultMass, CohortJuvenileMass, NewAbund, OptimalPreyBodySizeRatio, (ushort)0, ProportionTimeActive[FunctionalGroup], ref CohortIDIncrementer,TrophicIndex, tracking); // Add the new cohort to the list of grid cell cohorts _GridCellCohorts[FunctionalGroup].Add(NewCohort); // TEMPORARY /* // Check whether the model is set to randomly draw the body masses of new cohorts if ((Longitude % 4 == 0) && (Latitude % 4 == 0)) { if (DrawRandomly) { CohortAdultMass = 100000; CohortJuvenileMass = 100000; } else { CohortAdultMass = 100000; CohortJuvenileMass = 100000; } // An instance of Cohort to hold the new cohort Cohort NewCohort; double NewBiomass = (1.0E7 * _CellEnvironment["Cell Area"][0]) / NumCohortsThisCell; double NewAbund = 0.0; NewAbund = 3000; // Initialise the new cohort with the relevant properties NewCohort = new Cohort((byte)FunctionalGroup, CohortJuvenileMass, CohortAdultMass, CohortJuvenileMass, NewAbund, (ushort)0, ref nextCohortID, tracking); // Add the new cohort to the list of grid cell cohorts _GridCellCohorts[FunctionalGroup].Add(NewCohort); } */ // Incrememt the variable tracking the total number of cohorts in the model globalDiagnostics["NumberOfCohortsInModel"]++; } } } } else { // Loop over all functional groups in the model for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++) { // Create a new list to hold the cohorts in the grid cell _GridCellCohorts[FunctionalGroup] = new List<Cohort>(); } } // tempsw.Dispose(); }
/// <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> /// Record the flow of biomass between trophic levels during predation /// </summary> /// <param name="latIndex">The latitudinal index of the current grid cell</param> /// <param name="lonIndex">The longitudinal index of the current grid cell</param> /// <param name="fromFunctionalGroup">The index of the functional group that the biomass is flowing from (i.e. the prey)</param> /// <param name="toFunctionalGroup">The index of the functional group that the biomass is flowing to (i.e. the predator)</param> /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions of cohorts in the model</param> /// <param name="massEaten">The total biomass eaten by the predator cohort</param> /// <param name="predatorBodyMass">The body mass of the predator doing the eating</param> /// <param name="preyBodyMass">The body mass of the prey being eaten</param> /// <param name="initialisation">The Madingley Model initialisation</param> /// <param name="MarineCell">Whether the current cell is a marine cell</param> public void RecordPredationTrophicFlow(uint latIndex, uint lonIndex, int fromFunctionalGroup, int toFunctionalGroup, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, double massEaten, double predatorBodyMass, double preyBodyMass, MadingleyModelInitialisation initialisation, Boolean MarineCell) { int fromIndex = 0; int toIndex = 0; if (initialisation.TrackMarineSpecifics && MarineCell) { // Get the trophic level index of the functional group that mass is flowing from switch (cohortFunctionalGroupDefinitions.GetTraitNames("nutrition source", fromFunctionalGroup)) { case "herbivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", fromFunctionalGroup)) { case "planktonic": fromIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", fromFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", fromFunctionalGroup)) { case "allspecial": fromIndex = 6; break; default: fromIndex = 1; break; } break; default: if (preyBodyMass <= initialisation.PlanktonDispersalThreshold) { fromIndex = 5; } else { fromIndex = 1; } break; } break; } break; case "omnivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", fromFunctionalGroup)) { case "planktonic": fromIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", fromFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", fromFunctionalGroup)) { case "allspecial": fromIndex = 6; break; default: fromIndex = 2; break; } break; default: if (preyBodyMass <= initialisation.PlanktonDispersalThreshold) { fromIndex = 5; } else { fromIndex = 2; } break; } break; } break; case "carnivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", fromFunctionalGroup)) { case "planktonic": fromIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", fromFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", fromFunctionalGroup)) { case "allspecial": fromIndex = 6; break; default: fromIndex = 3; break; } break; default: if (preyBodyMass <= initialisation.PlanktonDispersalThreshold) { fromIndex = 5; } else { fromIndex = 3; } break; } break; } break; default: Debug.Fail("Specified nutrition source is not supported"); break; } // Get the trophic level index of the functional group that mass is flowing to switch (cohortFunctionalGroupDefinitions.GetTraitNames("nutrition source", toFunctionalGroup)) { case "omnivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", toFunctionalGroup)) { case "planktonic": toIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", toFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", toFunctionalGroup)) { case "allspecial": toIndex = 6; break; default: toIndex = 2; break; } break; default: if (predatorBodyMass <= initialisation.PlanktonDispersalThreshold) { toIndex = 5; } else { toIndex = 2; } break; } break; } break; case "carnivore": switch (cohortFunctionalGroupDefinitions.GetTraitNames("mobility", toFunctionalGroup)) { case "planktonic": toIndex = 4; break; default: switch (cohortFunctionalGroupDefinitions.GetTraitNames("endo/ectotherm", toFunctionalGroup)) { case "endotherm": switch (cohortFunctionalGroupDefinitions.GetTraitNames("diet", toFunctionalGroup)) { case "allspecial": toIndex = 6; break; default: toIndex = 3; break; } break; default: if (predatorBodyMass <= initialisation.PlanktonDispersalThreshold) { toIndex = 5; } else { toIndex = 3; } break; } break; } break; default: Debug.Fail("Specified nutrition source is not supported"); break; } } else { // Get the trophic level index of the functional group that mass is flowing from switch (cohortFunctionalGroupDefinitions.GetTraitNames("nutrition source", fromFunctionalGroup)) { case "herbivore": fromIndex = 1; break; case "omnivore": fromIndex = 2; break; case "carnivore": fromIndex = 3; break; default: Debug.Fail("Specified nutrition source is not supported"); break; } // Get the trophic level index of the functional group that mass is flowing to switch (cohortFunctionalGroupDefinitions.GetTraitNames("nutrition source", toFunctionalGroup)) { case "herbivore": toIndex = 1; break; case "omnivore": toIndex = 2; break; case "carnivore": toIndex = 3; break; default: Debug.Fail("Specified nutrition source is not supported"); break; } } // Add the flow of matter to the matrix of mass flows TrophicMassFlows[latIndex, lonIndex, fromIndex, toIndex] += massEaten; }
/// <summary> /// Calculate the proportion of time for which this cohort could be active and assign it to the cohort's properties /// </summary> /// <param name="actingCohort">The Cohort for which proportion of time active is being calculated</param> /// <param name="cellEnvironment">The environmental information for current grid cell</param> /// <param name="madingleyCohortDefinitions">Functional group definitions and code to interrogate the cohorts in current grid cell</param> /// <param name="currentTimestep">Current timestep index</param> /// <param name="currentMonth">Current month</param> public void AssignProportionTimeActive(Cohort actingCohort, SortedList<string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions,uint currentTimestep, uint currentMonth) { double Realm = cellEnvironment["Realm"][0]; //Only work on heterotroph cohorts if (madingleyCohortDefinitions.GetTraitNames("Heterotroph/Autotroph", actingCohort.FunctionalGroupIndex) == "heterotroph") { //Check if this is an endotherm or ectotherm Boolean Endotherm = madingleyCohortDefinitions.GetTraitNames("Endo/Ectotherm", actingCohort.FunctionalGroupIndex) == "endotherm"; if (Endotherm) { //Assumes the whole timestep is suitable for endotherms to be active - actual time active is therefore the proportion specified for this functional group. actingCohort.ProportionTimeActive = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("proportion suitable time active", actingCohort.FunctionalGroupIndex); } else { //If ectotherm then use realm specific function if (Realm == 1.0) { actingCohort.ProportionTimeActive = CalculateProportionTimeSuitableTerrestrial(cellEnvironment, currentMonth, Endotherm) * madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("proportion suitable time active", actingCohort.FunctionalGroupIndex); } else { actingCohort.ProportionTimeActive = CalculateProportionTimeSuitableMarine(cellEnvironment, currentMonth, Endotherm) * madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("proportion suitable time active", actingCohort.FunctionalGroupIndex); } } } }
/// <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> /// 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> /// 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> /// 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> /// 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; } }