/// <summary>
 /// Apply all updates from the ecological processes to the properties of the acting cohort and to the environment
 /// </summary>
 /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
 /// <param name="actingCohort">The location of the acting cohort in the jagged array of grid cell cohorts</param>
 /// <param name="cellEnvironment">The environment in the current gird cell</param>
 /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
 /// <param name="currentTimestep">The current model time step</param>
 /// <param name="tracker">A process tracker</param>
 public void UpdateAllEcology(GridCellCohortHandler gridCellCohorts, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> 
     deltas, uint currentTimestep, ProcessTracker tracker)
 {
     // Apply cohort abundance changes
     UpdateAbundance(gridCellCohorts, actingCohort, deltas);
     // Apply cohort biomass changes
     UpdateBiomass(gridCellCohorts, actingCohort, deltas, currentTimestep, tracker, cellEnvironment);
     // Apply changes to the environmental biomass pools
     UpdatePools(cellEnvironment, deltas);
 }
        /// <summary>
        /// Convert NPP estimate into biomass of an autotroph stock
        /// </summary>
        /// <param name="cellEnvironment">The environment of the current grid cell</param>
        /// <param name="gridCellStockHandler">The stock handler for the current stock</param>
        /// <param name="actingStock">The location of the stock to add biomass to</param>
        /// <param name="terrestrialNPPUnits">The units of the terrestrial NPP data</param>
        /// <param name="oceanicNPPUnits">The units of the oceanic NPP data</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="GlobalModelTimeStepUnit">The time step unit used in the model</param>
        /// <param name="trackProcesses">Whether to output data describing the ecological processes</param>
        /// <param name="globalTracker">Whether to output data describing the global-scale environment</param>
        /// <param name="outputDetail">The level of output detail to use for the outputs</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="currentMonth">The current month in the model run</param>
        public void ConvertNPPToAutotroph(SortedList<string, double[]> cellEnvironment, GridCellStockHandler gridCellStockHandler, int[]
            actingStock, string terrestrialNPPUnits, string oceanicNPPUnits, uint currentTimestep, string GlobalModelTimeStepUnit,
            ProcessTracker trackProcesses, GlobalProcessTracker globalTracker, string outputDetail, bool specificLocations, uint currentMonth)
        {

            // Get NPP from the cell environment
            double NPP = cellEnvironment["NPP"][currentMonth];

            // If NPP is a mssing value then set to zero
            if (NPP == cellEnvironment["Missing Value"][0]) NPP = 0.0;

            // Check that this is an ocean cell
            if (cellEnvironment["Realm"][0] == 2.0)
            {
                // Check that the units of oceanic NPP are gC per m2 per day
                Debug.Assert(oceanicNPPUnits == "gC/m2/day", "Oceanic NPP data are not in the correct units for this formulation of the model");

                // Convert to g/cell/month
                NPP *= _MsqToKmSqConversion;

                // Multiply by cell area to get g/cell/day
                NPP *= cellEnvironment["Cell Area"][0];

                // Convert to g wet matter, assuming carbon content of phytoplankton is 10% of wet matter
                NPP *= _PhytoplanktonConversionRatio;

                // Finally convert to g/cell/month and add to the stock totalbiomass
                NPP *= Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "day");
                gridCellStockHandler[actingStock].TotalBiomass += NPP;

                if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations)
                {
                    trackProcesses.TrackPrimaryProductionTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                        NPP);
                }

                if (globalTracker.TrackProcesses)
                {
                    globalTracker.RecordNPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0],
                            NPP / cellEnvironment["Cell Area"][0]);
                }

                // If the biomass of the autotroph stock has been made less than zero (i.e. because of negative NPP) then reset to zero
                if (gridCellStockHandler[actingStock].TotalBiomass < 0.0)
                    gridCellStockHandler[actingStock].TotalBiomass = 0.0;
            }
            // Else if neither on land or in the ocean
            else
            {
                Debug.Fail("This is not a marine cell!");
                // Set the autotroph biomass to zero
                gridCellStockHandler[actingStock].TotalBiomass = 0.0;
            }
            Debug.Assert(gridCellStockHandler[actingStock].TotalBiomass >= 0.0, "stock negative");
        }
        /// <summary>
        /// Run reproduction
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param>
        /// <param name="currentTimeStep">The current model time step</param>
        /// <param name="processTracker">An instance of ProcessTracker to hold diagnostics for eating</param>
        /// <param name="partial">Thread-locked variables for the parallelised version</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level of output detail being used for this model run</param>
        /// <param name="currentMonth">The current model month</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, 
            int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string,Dictionary<string,double>> deltas , 
            FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions,
            uint currentTimeStep, ProcessTracker processTracker, ref ThreadLockedParallelVariables partial,
            Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation)
        {
            // Holds the reproductive strategy of a cohort
            bool _Iteroparous = madingleyCohortDefinitions.GetTraitNames("reproductive strategy", actingCohort[0])=="iteroparity";

            // Assign mass to reproductive potential
            Implementations["reproduction basic"].RunReproductiveMassAssignment(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas,
                madingleyCohortDefinitions, madingleyStockDefinitions, currentTimeStep, processTracker);

            // Run reproductive events. Note that we can't skip juveniles here as they could conceivably grow to adulthood and get enough biomass to reproduce in a single time step
            // due to other ecological processes
            Implementations["reproduction basic"].RunReproductionEvents(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment,
                    deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimeStep, processTracker, ref partial, _Iteroparous, currentMonth);
        }
        /// <summary>
        /// Run ecological processes that operate on stocks within a single grid cell
        /// </summary>
        ///<param name="gridCellStocks">The stocks in the current grid cell</param>
        ///<param name="actingStock">The acting stock</param>
        ///<param name="cellEnvironment">The stocks in the current grid cell</param>
        ///<param name="environmentalDataUnits">List of units associated with the environmental variables</param>
        ///<param name="humanNPPScenario">The human appropriation of NPP scenario to apply</param>
        ///<param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param>
        ///<param name="currentTimeStep">The current model time step</param>
        ///<param name="burninSteps">The number of time steps to spin the model up for before applying human impacts</param>
        ///<param name="impactSteps">The number of time steps to apply human impacts for</param>
        ///<param name="globalModelTimeStepUnit">The time step unit used in the model</param>
        ///<param name="trackProcesses">Whether to track properties of ecological processes</param>
        ///<param name="tracker">An instance of the ecological process tracker</param>
        ///<param name="globalTracker">An instance of the global process tracker</param>
        ///<param name="currentMonth">The current model month</param>
        ///<param name="outputDetail">The level of detail to use in outputs</param>
        ///<param name="specificLocations">Whether to run the model for specific locations</param>
        ///<param name="impactCell">Whether this cell should have human impacts applied</param>
        public void RunWithinCellEcology(GridCellStockHandler gridCellStocks, int[] actingStock, SortedList<string, double[]> cellEnvironment,
            SortedList<string, string> environmentalDataUnits, Madingley.Common.ScenarioParameter humanNPPScenario,
            FunctionalGroupDefinitions madingleyStockDefinitions,
            uint currentTimeStep, uint burninSteps, uint impactSteps, uint recoverySteps, uint instantStep, uint numInstantSteps, string globalModelTimeStepUnit, Boolean trackProcesses,
            ProcessTracker tracker,
            GlobalProcessTracker globalTracker, uint currentMonth,
            string outputDetail, bool specificLocations, Boolean impactCell)
        {
            if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "marine")
            {
                // Run the autotroph processor
                MarineNPPtoAutotrophStock.ConvertNPPToAutotroph(cellEnvironment, gridCellStocks, actingStock, environmentalDataUnits["LandNPP"],
                    environmentalDataUnits["OceanNPP"], currentTimeStep, globalModelTimeStepUnit, tracker, globalTracker, outputDetail, specificLocations, currentMonth);
            }
            else if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "terrestrial")
            {

                // Run the dynamic plant model to update the leaf stock for this time step
                double WetMatterNPP = DynamicPlantModel.UpdateLeafStock(cellEnvironment, gridCellStocks, actingStock, currentTimeStep, madingleyStockDefinitions.
                    GetTraitNames("leaf strategy", actingStock[0]).Equals("deciduous"), globalModelTimeStepUnit, tracker, globalTracker, currentMonth,
                    outputDetail, specificLocations);
                /// <summary>

                double fhanpp = HANPP.RemoveHumanAppropriatedMatter(WetMatterNPP, cellEnvironment, humanNPPScenario, gridCellStocks, actingStock,
                    currentTimeStep, burninSteps, impactSteps, recoverySteps, instantStep, numInstantSteps, impactCell, globalModelTimeStepUnit);

                // Apply human appropriation of NPP
                gridCellStocks[actingStock].TotalBiomass += WetMatterNPP * (1.0 - fhanpp);

                if (globalTracker.TrackProcesses)
                {
                    globalTracker.RecordHANPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0],
                        fhanpp);
                }

                if (gridCellStocks[actingStock].TotalBiomass < 0.0) gridCellStocks[actingStock].TotalBiomass = 0.0;

            }
            else
            {
                Debug.Fail("Stock must be classified as belonging to either the marine or terrestrial realm");
            }
        }
        /// <summary>
        /// Generate new cohorts from reproductive potential mass
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment of the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param>
        /// <param name="partial">Thread-locked variables</param>
        public void RunReproduction(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks,
            int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>>
            deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions,
            uint currentTimestep, ProcessTracker tracker, ref ThreadLockedParallelVariables partial)
        {
            // Check that the abundance in the cohort to produce is greater than or equal to zero
            Debug.Assert(_OffspringCohortAbundance >= 0.0, "Offspring abundance < 0");

            // Get the adult and juvenile masses of the cohort to produce
            double[] OffspringProperties = GetOffspringCohortProperties(gridCellCohorts, actingCohort,
                madingleyCohortDefinitions);

            // Update cohort abundance in case juvenile mass has been altered
            _OffspringCohortAbundance = (_OffspringCohortAbundance * gridCellCohorts[actingCohort].JuvenileMass) /
                OffspringProperties[0];

            //Create the offspring cohort
            Cohort OffspringCohort = new Cohort((byte)actingCohort[0],
                                                OffspringProperties[0],
                                                OffspringProperties[1],
                                                OffspringProperties[0],
                                                _OffspringCohortAbundance,
                                                (ushort)currentTimestep, ref partial.NextCohortIDThreadLocked);

            // Add the offspring cohort to the grid cell cohorts array
            gridCellCohorts[actingCohort[0]].Add(OffspringCohort);

            // If the cohort has never been merged with another cohort, then add it to the tracker for output as diagnostics
            if ((!gridCellCohorts[actingCohort].Merged) && tracker.TrackProcesses)
                tracker.RecordNewCohort((uint)cellEnvironment["LatIndex"][0],
                    (uint)cellEnvironment["LonIndex"][0], currentTimestep, _OffspringCohortAbundance,
                    gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex);

            // Subtract all of the reproductive potential mass of the parent cohort, which has been used to generate the new
            // cohort, from the delta reproductive potential mass
            deltas["reproductivebiomass"]["reproduction"] -= (gridCellCohorts[actingCohort].IndividualReproductivePotentialMass);

        }
        /// <summary>
        /// Run metabolism
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for metabolism</param>
        /// <param name="partial">Thread-locked variables</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level of output detail being used for the current model run</param>
        /// <param name="currentMonth">The current model month</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, 
            int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, 
            FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, 
            uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial,
            Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation)
        {
            double Realm = cellEnvironment["Realm"][0];
            if (madingleyCohortDefinitions.GetTraitNames("Heterotroph/Autotroph", gridCellCohorts[actingCohort].FunctionalGroupIndex) == "heterotroph")
            {
                if (madingleyCohortDefinitions.GetTraitNames("Endo/Ectotherm", gridCellCohorts[actingCohort].FunctionalGroupIndex) == "endotherm")
                {

                        Implementations["basic endotherm"].RunMetabolism(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, currentMonth);
                }
                else
                {
                        Implementations["basic ectotherm"].RunMetabolism(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, currentMonth);

                }

            }

            // If the process tracker is on and output detail is set to high and this cohort has not been merged yet, then record
            // the number of individuals that have died
            if (trackProcesses.TrackProcesses && (outputDetail == "high"))
            {

                trackProcesses.TrackTimestepMetabolism((uint)cellEnvironment["LatIndex"][0],
                                                (uint)cellEnvironment["LonIndex"][0],
                                                currentTimestep,
                                                gridCellCohorts[actingCohort].IndividualBodyMass,
                                                actingCohort[0],
                                                cellEnvironment["Temperature"][currentMonth],
                                                deltas["biomass"]["metabolism"]);

            }
        }
        /// <summary>
        /// Assigns ingested biomass from other ecological processes to reproductive potential mass
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param>
        public void RunReproductiveMassAssignment(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks,
            int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas,
            FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions,
            uint currentTimestep, ProcessTracker tracker)
        {
            // Biomass per individual in each cohort to be assigned to reproductive potential
            double _BiomassToAssignToReproductivePotential;

            // Net biomass change from other ecological functions this time step
            double NetBiomassFromOtherEcologicalFunctionsThisTimeStep;

            // Reset variable holding net biomass change of individuals in this cohort as a result of other ecological processes
            NetBiomassFromOtherEcologicalFunctionsThisTimeStep = 0.0;

            // Loop over all items in the biomass deltas
            foreach (var Biomass in deltas["biomass"])
            {
                // Add the delta biomass to net biomass
                NetBiomassFromOtherEcologicalFunctionsThisTimeStep += Biomass.Value;
            }

            // If individual body mass after the addition of the net biomass from processes this time step will yield a body mass 
            // greater than the adult body mass for this cohort, then assign the surplus to reproductive potential
            if ((gridCellCohorts[actingCohort].IndividualBodyMass + NetBiomassFromOtherEcologicalFunctionsThisTimeStep) > gridCellCohorts[actingCohort].AdultMass)
            {
                // Calculate the biomass for each individual in this cohort to be assigned to reproductive potential
                _BiomassToAssignToReproductivePotential = gridCellCohorts[actingCohort].IndividualBodyMass + NetBiomassFromOtherEcologicalFunctionsThisTimeStep -
                    gridCellCohorts[actingCohort].AdultMass;

                // Check that a positive biomass is to be assigned to reproductive potential
                Debug.Assert(_BiomassToAssignToReproductivePotential >= 0.0, "Assignment of negative reproductive potential mass");

                // If this is the first time reproductive potential mass has been assigned for this cohort, 
                // then set the maturity time step for this cohort as the current model time step
                if (gridCellCohorts[actingCohort].MaturityTimeStep == uint.MaxValue)
                {
                    gridCellCohorts[actingCohort].MaturityTimeStep = currentTimestep;

                    // Track the generation length for this cohort
                    if (tracker.TrackProcesses && (!gridCellCohorts[actingCohort].Merged))
                        tracker.TrackMaturity((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                            currentTimestep, gridCellCohorts[actingCohort].BirthTimeStep, gridCellCohorts[actingCohort].JuvenileMass,
                            gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex);
                }

                // Assign the specified mass to reproductive potential mass and remove it from individual biomass
                deltas["reproductivebiomass"]["reproduction"] += _BiomassToAssignToReproductivePotential;
                deltas["biomass"]["reproduction"] -= _BiomassToAssignToReproductivePotential;

            }
            else
            {
                // Cohort has not gained sufficient biomass to assign any to reproductive potential, so take no action
            }
        }
        /// <summary>
        /// Assigns biomass from body mass to reproductive potential mass
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param>
        public void AssignMassToReproductivePotential(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks,
            int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas,
            FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions,
            uint currentTimestep, ProcessTracker tracker)
        {
            // If this is the first time reproductive potential mass has been assigned for this cohort, 
            // then set the maturity time step for this cohort as the current model time step
            if (gridCellCohorts[actingCohort].MaturityTimeStep == uint.MaxValue)
            {
                gridCellCohorts[actingCohort].MaturityTimeStep = currentTimestep;

                // Track the generation length for this cohort
                if ((!gridCellCohorts[actingCohort].Merged) && tracker.TrackProcesses)
                    tracker.TrackMaturity((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                        currentTimestep, gridCellCohorts[actingCohort].BirthTimeStep, gridCellCohorts[actingCohort].JuvenileMass,
                        gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex);
            }

            // Assign the specified mass to reproductive potential mass and remove it from individual biomass
            deltas["reproductivebiomass"]["reproduction"] += _BiomassToAssignToReproductivePotential;
            deltas["biomass"]["reproduction"] -= _BiomassToAssignToReproductivePotential;

        }
        /// <summary>
        /// Update the leaf stock during a time step given the environmental conditions in the grid cell
        /// </summary>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingStock">The position of the acting stock in the array of grid cell stocks</param>
        /// <param name="currentTimeStep">The current model time step</param>
        /// <param name="deciduous">Whether the acting stock consists of deciduous leaves</param>
        /// <param name="GlobalModelTimeStepUnit">The time step unit used in the model</param>
        /// <param name="tracker">Whether to track properties of the ecological processes</param>
        /// <param name="globalTracker">Whether to output data describing the global environment</param>
        /// <param name="currentMonth">The current model month</param>
        /// <param name="outputDetail">The level of detail to use in model outputs</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        public double UpdateLeafStock(SortedList<string, double[]> cellEnvironment, GridCellStockHandler gridCellStocks, int[] actingStock,
            uint currentTimeStep, bool deciduous, string GlobalModelTimeStepUnit, ProcessTracker tracker, GlobalProcessTracker globalTracker, 
            uint currentMonth, string outputDetail, bool specificLocations)
        {
            // ESTIMATE ANNUAL LEAF CARBON FIXATION ASSUMING ENVIRONMENT THROUGHOUT THE YEAR IS THE SAME AS IN THIS MONTH

            // Calculate annual NPP
            double NPP = this.CalculateMiamiNPP(cellEnvironment["Temperature"].Average(), cellEnvironment["Precipitation"].Sum());

            // Calculate fractional allocation to structural tissue
            double FracStruct = this.CalculateFracStruct(NPP);

            // Estimate monthly NPP based on seasonality layer
            NPP *= cellEnvironment["Seasonality"][currentMonth];

            // Calculate leaf mortality rates
            double AnnualLeafMortRate;
            double MonthlyLeafMortRate;
            double TimeStepLeafMortRate;

            if (deciduous)
            {
                // Calculate annual deciduous leaf mortality
                AnnualLeafMortRate = this.CalculateDeciduousAnnualLeafMortality(cellEnvironment["Temperature"].Average());

                // For deciduous plants monthly leaf mortality is weighted by temperature deviance from the average, to capture seasonal patterns
                double[] ExpTempDev = new double[12];
                double SumExpTempDev = 0.0;
                double[] TempDev = new double[12];
                double Weight;
                for (int i = 0; i < 12; i++)
                {
                    TempDev[i] = cellEnvironment["Temperature"][i] - cellEnvironment["Temperature"].Average();
                    ExpTempDev[i] = Math.Exp(-TempDev[i] / 3);
                    SumExpTempDev += ExpTempDev[i];
                }
                Weight = ExpTempDev[currentMonth] / SumExpTempDev;
                MonthlyLeafMortRate = AnnualLeafMortRate * Weight;
                TimeStepLeafMortRate = MonthlyLeafMortRate * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month");
            }
            else
            {
                // Calculate annual evergreen leaf mortality
                AnnualLeafMortRate = this.CalculateEvergreenAnnualLeafMortality(cellEnvironment["Temperature"].Average());

                // For evergreen plants, leaf mortality is assumed to be equal throughout the year
                MonthlyLeafMortRate = AnnualLeafMortRate * (1.0 / 12.0);
                TimeStepLeafMortRate = MonthlyLeafMortRate * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month");
            }

            // Calculate fine root mortality rate
            double AnnualFRootMort = this.CalculateFineRootMortalityRate(cellEnvironment["Temperature"][currentMonth]);

            // Calculate the NPP allocated to non-structural tissues
            double FracNonStruct = (1 - FracStruct);

            // Calculate the fractional allocation to leaves
            double FracLeaves = FracNonStruct * this.CalculateLeafFracAllocation(AnnualLeafMortRate, AnnualFRootMort);

            // Calculate the fractional allocation of NPP to evergreen plant matter
            double FracEvergreen = this.CalculateFracEvergreen(cellEnvironment["Fraction Year Frost"][0]);

            // Update NPP depending on whether the acting stock is deciduous or evergreen
            if (deciduous)
            {
                NPP *= (1 - FracEvergreen);
            }
            else
            {
                NPP *= FracEvergreen;
            }

            // Calculate the fire mortality rate
            double FireMortRate = this.CalculateFireMortalityRate(NPP, cellEnvironment["Fraction Year Fire"][0]);

            // Calculate the structural mortality rate
            double StMort = this.CalculateStructuralMortality(cellEnvironment["AET"][currentMonth] * 12);

            // Calculate leaf C fixation
            double LeafCFixation = NPP * FracLeaves;

            // Convert from carbon to leaf wet matter
            double WetMatterIncrement = this.ConvertToLeafWetMass(LeafCFixation, cellEnvironment["Cell Area"][0]);

            // Convert from the monthly time step used for this process to the global model time step unit
            WetMatterIncrement *= Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month");

            // Add the leaf wet matter to the acting stock
            //gridCellStocks[actingStock].TotalBiomass += Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement);
            double NPPWetMatter = Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement);

            // If the processer tracker is enabled and output detail is high and the model is being run for specific locations, then track the biomass gained through primary production
            if (tracker.TrackProcesses && (outputDetail == "high") && specificLocations)
            {
                tracker.TrackPrimaryProductionTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                    Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement));

            }

            if (globalTracker.TrackProcesses)
            {
                globalTracker.RecordNPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],(uint)actingStock[0],
                    this.ConvertToLeafWetMass(NPP, cellEnvironment["Cell Area"][0]) *
                    Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month")/cellEnvironment["Cell Area"][0]);
            }
            // Calculate fractional leaf mortality
            double LeafMortFrac = 1 - Math.Exp(-TimeStepLeafMortRate);

            // Update the leaf stock biomass owing to the leaf mortality
            gridCellStocks[actingStock].TotalBiomass *= (1 - LeafMortFrac);
            NPPWetMatter *= (1 - LeafMortFrac);

            return (NPPWetMatter);
        }
        /// <summary>
        /// Update the individual and reproductive body masses of the acting cohort according to the delta biomasses from the ecological processes
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="tracker">A process tracker</param>
        /// <param name="cellEnvironment">The cell environment</param>
        private void UpdateBiomass(GridCellCohortHandler gridCellCohorts, int[] actingCohort, Dictionary<string,Dictionary<string, double>> deltas, 
            uint currentTimestep, ProcessTracker tracker, SortedList<string,double[]> cellEnvironment)
        {
            // Extract the biomass deltas from the sorted list of all deltas
            Dictionary<string, double> deltaBiomass = deltas["biomass"];

            if (tracker.TrackProcesses)
            {
                // Calculate net growth of individuals in this cohort
                double growth = deltaBiomass["predation"] + deltaBiomass["herbivory"] + deltaBiomass["metabolism"];
                tracker.TrackTimestepGrowth((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep,
                    gridCellCohorts[actingCohort].IndividualBodyMass, gridCellCohorts[actingCohort].FunctionalGroupIndex, growth, deltaBiomass["metabolism"],deltaBiomass["predation"],deltaBiomass["herbivory"]);

            }

            // Get all keys from the biomass deltas sorted list
            string[] KeyStrings = deltaBiomass.Keys.ToArray();

            // Variable to calculate net biomass change to check that cohort individual body mass will not become negative
            double NetBiomass = 0.0;

            // Loop over all biomass deltas
            foreach (string key in KeyStrings)
            {
                // Update net biomass change
                NetBiomass += deltaBiomass[key];
            }

            double BiomassCheck=0.0;
            Boolean NetToBeApplied = true;
            // If cohort abundance is greater than zero, then check that the calculated net biomas will not make individual body mass become negative
            if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) > 0)
            {
                string output = "Biomass going negative, acting cohort: " + actingCohort[0].ToString() + ", " + actingCohort[1].ToString();
                BiomassCheck = gridCellCohorts[actingCohort].IndividualBodyMass + NetBiomass;
                Debug.Assert((BiomassCheck).CompareTo(0.0) >= 0, output);
            }

            //Loop over all keys in the abundance deltas sorted list
            foreach (string key in KeyStrings)
            {
                // If cohort abundance is zero, then set cohort individual body mass to zero and reset the biomass delta to zero,
                // otherwise update cohort individual body mass and reset the biomass delta to zero
                if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) == 0)
                {
                    gridCellCohorts[actingCohort].IndividualBodyMass = 0.0;
                    deltaBiomass[key] = 0.0;
                }
                else
                {
                    if (NetToBeApplied)
                    {
                        gridCellCohorts[actingCohort].IndividualBodyMass += NetBiomass;
                        NetToBeApplied = false;
                    }

                    //gridCellCohorts[actingCohort].IndividualBodyMass += deltaBiomass[key];
                    deltaBiomass[key] = 0.0;
                }
            }

            // Check that individual body mass is still greater than zero
            Debug.Assert(gridCellCohorts[actingCohort].IndividualBodyMass.CompareTo(0.0) >= 0, "biomass < 0");

            // If the current individual body mass is the largest that has been achieved by this cohort, then update the maximum achieved
            // body mass tracking variable for the cohort
            if (gridCellCohorts[actingCohort].IndividualBodyMass > gridCellCohorts[actingCohort].MaximumAchievedBodyMass)
                gridCellCohorts[actingCohort].MaximumAchievedBodyMass = gridCellCohorts[actingCohort].IndividualBodyMass;

            // Extract the reproductive biomass deltas from the sorted list of all deltas
            Dictionary<string, double> deltaReproductiveBiomass = deltas["reproductivebiomass"];

            // Get all keys from the biomass deltas sorted list
            string[] KeyStrings2 = deltas["reproductivebiomass"].Keys.ToArray();

            // Variable to calculate net reproductive biomass change to check that cohort individual body mass will not become negative
            double NetReproductiveBiomass = 0.0;

            // Loop over all reproductive biomass deltas
            foreach (string key in KeyStrings2)
            {
                // Update net reproductive biomass change
                NetReproductiveBiomass += deltaReproductiveBiomass[key];
            }

            //Loop over all keys in the abundance deltas sorted list
            foreach (string key in KeyStrings2)
            {
                // If cohort abundance is zero, then set cohort reproductive body mass to zero and reset the biomass delta to zero,
                // otherwise update cohort reproductive body mass and reset the biomass delta to zero
                if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) == 0)
                {
                    gridCellCohorts[actingCohort].IndividualReproductivePotentialMass = 0.0;
                    deltaReproductiveBiomass[key] = 0.0;
                }
                else
                {
                    gridCellCohorts[actingCohort].IndividualReproductivePotentialMass += deltaReproductiveBiomass[key];
                    deltaReproductiveBiomass[key] = 0.0;
                }
            }

            // Note that maturity time step is set in TReproductionBasic
        }
Example #11
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>
        /// Run eating
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for eating</param>
        /// <param name="partial">Thread-locked variables</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level of output detail being used for the current model run</param>
        /// <param name="currentMonth">The current model month</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, 
            GridCellStockHandler gridCellStocks, int[] actingCohort, 
            SortedList<string, double[]> cellEnvironment, 
            Dictionary<string, Dictionary<string, double>> deltas, 
            FunctionalGroupDefinitions madingleyCohortDefinitions, 
            FunctionalGroupDefinitions madingleyStockDefinitions, 
            uint currentTimestep, ProcessTracker trackProcesses, 
            ref ThreadLockedParallelVariables partial, Boolean specificLocations,
            string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation)
        {
            PreviousTrophicIndex = gridCellCohorts[actingCohort].TrophicIndex;
            //Reset this cohort's trohic index ready for calculation across its feeding this timetsstep
            gridCellCohorts[actingCohort].TrophicIndex = 0.0;

            // Get the nutrition source (herbivory, carnivory or omnivory) of the acting cohort
            string NutritionSource = madingleyCohortDefinitions.GetTraitNames("Nutrition source", gridCellCohorts[actingCohort].FunctionalGroupIndex);

            // Switch to the appropriate eating process(es) given the cohort's nutrition source
            switch (NutritionSource)
            {
                case "herbivore":

                    // Get the assimilation efficiency for herbivory for this cohort from the functional group definitions
                    Implementations["revised herbivory"].AssimilationEfficiency =
                        madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup
                        ("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex);

                    // Get the proportion of time spent eating for this cohort from the functional group definitions
                    Implementations["revised herbivory"].ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive;

                    // Calculate the potential biomass available from herbivory
                    if (cellEnvironment["Realm"][0] == 2.0)
                        Implementations["revised herbivory"].GetEatingPotentialMarine
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions);
                    else

                        Implementations["revised herbivory"].GetEatingPotentialTerrestrial
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions);

                    // Run herbivory to apply changes in autotroph biomass from herbivory and add biomass eaten to the delta arrays
                    Implementations["revised herbivory"].RunEating
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, deltas, madingleyCohortDefinitions,
                        madingleyStockDefinitions, trackProcesses,
                        currentTimestep, specificLocations,outputDetail, initialisation);

                    break;

                case "carnivore":

                    // Get the assimilation efficiency for predation for this cohort from the functional group definitions
                    Implementations["revised predation"].AssimilationEfficiency =
                        madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup
                        ("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex);

                    Implementations["revised predation"].ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive;

                    // Calculate the potential biomass available from predation
                    if (cellEnvironment["Realm"][0] == 2.0)
                        Implementations["revised predation"].GetEatingPotentialMarine
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions);
                    else
                        Implementations["revised predation"].GetEatingPotentialTerrestrial
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions);
                    // Run predation to apply changes in prey biomass from predation and add biomass eaten to the delta arrays
                    Implementations["revised predation"].RunEating
                        (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas,
                        madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses,
                        currentTimestep, specificLocations,outputDetail, initialisation);

                    break;

                case "omnivore":

                    // Get the assimilation efficiency for predation for this cohort from the functional group definitions
                    Implementations["revised predation"].AssimilationEfficiency =
                        madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup
                        ("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex);

                    // Get the assimilation efficiency for herbivory for this cohort from the functional group definitions
                    Implementations["revised herbivory"].AssimilationEfficiency =
                        madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup
                        ("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex);

                    // Get the proportion of time spent eating and assign to both the herbivory and predation implementations
                    double ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive;
                    Implementations["revised predation"].ProportionTimeEating = ProportionTimeEating;
                    Implementations["revised herbivory"].ProportionTimeEating = ProportionTimeEating;

                    // Calculate the potential biomass available from herbivory
                    if (cellEnvironment["Realm"][0] == 2.0)
                        Implementations["revised herbivory"].GetEatingPotentialMarine
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, madingleyCohortDefinitions,
                        madingleyStockDefinitions);
                    else
                        Implementations["revised herbivory"].GetEatingPotentialTerrestrial
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, madingleyCohortDefinitions,
                        madingleyStockDefinitions);

                    // Calculate the potential biomass available from predation
                    if (cellEnvironment["Realm"][0] == 2.0)
                        Implementations["revised predation"].GetEatingPotentialMarine
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, madingleyCohortDefinitions,
                        madingleyStockDefinitions);
                    else
                        Implementations["revised predation"].GetEatingPotentialTerrestrial
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, madingleyCohortDefinitions,
                        madingleyStockDefinitions);

                    // Calculate the total handling time for all expected kills from predation and expected plant matter eaten in herbivory
                    TotalTimeToEatForOmnivores =
                        Implementations["revised herbivory"].TimeUnitsToHandlePotentialFoodItems +
                        Implementations["revised predation"].TimeUnitsToHandlePotentialFoodItems;

                    // Assign this total time to the relevant variables in both herbviory and predation, so that actual amounts eaten are calculated correctly
                    Implementations["revised herbivory"].TimeUnitsToHandlePotentialFoodItems = TotalTimeToEatForOmnivores;
                    Implementations["revised predation"].TimeUnitsToHandlePotentialFoodItems = TotalTimeToEatForOmnivores;

                    // Run predation to update prey cohorts and delta biomasses for the acting cohort
                    Implementations["revised predation"].RunEating
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, deltas, madingleyCohortDefinitions,
                        madingleyStockDefinitions, trackProcesses,
                        currentTimestep, specificLocations,outputDetail, initialisation);

                    // Run herbivory to update autotroph biomass and delta biomasses for the acting cohort
                    Implementations["revised herbivory"].RunEating
                        (gridCellCohorts, gridCellStocks, actingCohort,
                        cellEnvironment, deltas, madingleyCohortDefinitions,
                        madingleyStockDefinitions, trackProcesses,
                        currentTimestep, specificLocations,outputDetail, initialisation);

                    break;

                default:

                    // For nutrition source that are not supported, throw an error
                    Debug.Fail("The model currently does not contain an eating model for nutrition source:" + NutritionSource);

                    break;

            }

            // Check that the biomasses from predation and herbivory in the deltas is a number
            Debug.Assert(!double.IsNaN(deltas["biomass"]["predation"]), "BiomassFromEating is NaN");
            Debug.Assert(!double.IsNaN(deltas["biomass"]["herbivory"]), "BiomassFromEating is NaN");

            double biomassEaten = 0.0;
            if (madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("carnivory assimilation",
                gridCellCohorts[actingCohort].FunctionalGroupIndex) > 0)
            {
                biomassEaten += (deltas["biomass"]["predation"] / madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("carnivory assimilation",
                gridCellCohorts[actingCohort].FunctionalGroupIndex));
            }
            if (madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("herbivory assimilation",
                gridCellCohorts[actingCohort].FunctionalGroupIndex) > 0)
            {
                biomassEaten += (deltas["biomass"]["herbivory"]/madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("herbivory assimilation",
                gridCellCohorts[actingCohort].FunctionalGroupIndex));
            }

            if (biomassEaten > 0.0)
            {
                gridCellCohorts[actingCohort].TrophicIndex = 1 +
                    (gridCellCohorts[actingCohort].TrophicIndex / (biomassEaten * gridCellCohorts[actingCohort].CohortAbundance));
            }
            else
            {
                gridCellCohorts[actingCohort].TrophicIndex = PreviousTrophicIndex;
            }
        }
        /// <summary>
        /// Generate new cohorts from reproductive potential mass
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment of the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param>
        /// <param name="partial">Thread-locked variables</param>
        /// <param name="iteroparous">Whether the acting cohort is iteroparous, as opposed to semelparous</param>
        /// <param name="currentMonth">The current model month</param>
        public void RunReproductionEvents(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks,
            int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>>
            deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions,
            uint currentTimestep, ProcessTracker tracker, ref ThreadLockedParallelVariables partial, bool iteroparous, uint currentMonth)
        {
            // Adult non-reproductive biomass lost by semelparous organisms
            double AdultMassLost;

            // Offspring cohort abundance
            double _OffspringCohortAbundance;

            // Mass ratio of body mass + reproductive mass to adult body mass
            double CurrentMassRatio;

            // Individual body mass including change this time step as a result of other ecological processes
            double BodyMassIncludingChangeThisTimeStep;

            // Offspring juvenile and adult body masses
            double[] OffspringJuvenileAndAdultBodyMasses = new double[2];

            // Offspring cohort
            Cohort OffspringCohort;

            // Individual reproductive mass including change this time step as a result of other ecological processes
            double ReproductiveMassIncludingChangeThisTimeStep;

            // Calculate the biomass of an individual in this cohort including changes this time step from other ecological processes  
            BodyMassIncludingChangeThisTimeStep = 0.0;

            foreach (var Biomass in deltas["biomass"])
            {
                // Add the delta biomass to net biomass
                BodyMassIncludingChangeThisTimeStep += Biomass.Value;

            }

            BodyMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualBodyMass;

            // Calculate the reproductive biomass of an individual in this cohort including changes this time step from other ecological processes  
            ReproductiveMassIncludingChangeThisTimeStep = 0.0;

            foreach (var ReproBiomass in deltas["reproductivebiomass"])
            {
                // Add the delta reproductive biomass to net biomass
                ReproductiveMassIncludingChangeThisTimeStep += ReproBiomass.Value;
            }

            ReproductiveMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualReproductivePotentialMass;

            // Get the current ratio of total individual mass (including reproductive potential) to adult body mass
            CurrentMassRatio = (BodyMassIncludingChangeThisTimeStep + ReproductiveMassIncludingChangeThisTimeStep) / gridCellCohorts[actingCohort].AdultMass;

            // Must have enough mass to hit reproduction threshold criterion, and either (1) be in breeding season, or (2) be a marine cell (no breeding season in marine cells)
            if ((CurrentMassRatio > _MassRatioThreshold) && ((cellEnvironment["Breeding Season"][currentMonth] == 1.0) || ((cellEnvironment["Realm"][0] == 2.0))))
            {
                // Iteroparous and semelparous organisms have different strategies
                if (iteroparous)
                {
                    // Iteroparous organisms do not allocate any of their current non-reproductive biomass to reproduction
                    AdultMassLost = 0.0;

                    // Calculate the number of offspring that could be produced given the reproductive potential mass of individuals
                    _OffspringCohortAbundance = gridCellCohorts[actingCohort].CohortAbundance * ReproductiveMassIncludingChangeThisTimeStep /
                        gridCellCohorts[actingCohort].JuvenileMass;
                }
                else
                {
                    // Semelparous organisms allocate a proportion of their current non-reproductive biomass (including the effects of other ecological processes) to reproduction
                    AdultMassLost = _SemelparityAdultMassAllocation * BodyMassIncludingChangeThisTimeStep;

                    // Calculate the number of offspring that could be produced given the reproductive potential mass of individuals
                    _OffspringCohortAbundance = gridCellCohorts[actingCohort].CohortAbundance * (AdultMassLost + ReproductiveMassIncludingChangeThisTimeStep) /
                        gridCellCohorts[actingCohort].JuvenileMass;
                }

                // Check that the abundance in the cohort to produce is greater than or equal to zero
                Debug.Assert(_OffspringCohortAbundance >= 0.0, "Offspring abundance < 0");

                // Get the adult and juvenile masses of the offspring cohort
                OffspringJuvenileAndAdultBodyMasses = GetOffspringCohortProperties(gridCellCohorts, actingCohort, madingleyCohortDefinitions);

                // Update cohort abundance in case juvenile mass has been altered through 'evolution'
                _OffspringCohortAbundance = (_OffspringCohortAbundance * gridCellCohorts[actingCohort].JuvenileMass) / OffspringJuvenileAndAdultBodyMasses[0];

                double TrophicIndex;
                switch (madingleyCohortDefinitions.GetTraitNames("nutrition source", actingCohort[0]))
                {
                    case "herbivore":
                        TrophicIndex = 2;
                        break;
                    case "omnivore":
                        TrophicIndex = 2.5;
                        break;
                    case "carnivore":
                        TrophicIndex = 3;
                        break;
                    default:
                        Debug.Fail("Unexpected nutrition source trait value when assigning trophic index");
                        TrophicIndex = 0.0;
                        break;
                }

                // Create the offspring cohort
                OffspringCohort = new Cohort((byte)actingCohort[0], OffspringJuvenileAndAdultBodyMasses[0], OffspringJuvenileAndAdultBodyMasses[1], OffspringJuvenileAndAdultBodyMasses[0],
                                                    _OffspringCohortAbundance, Math.Exp(gridCellCohorts[actingCohort].LogOptimalPreyBodySizeRatio),
                                                    (ushort)currentTimestep, gridCellCohorts[actingCohort].ProportionTimeActive, ref partial.NextCohortIDThreadLocked, TrophicIndex, tracker.TrackProcesses);

                // Add the offspring cohort to the grid cell cohorts array
                gridCellCohorts[actingCohort[0]].Add(OffspringCohort);

                // If track processes has been specified then add the new cohort to the process tracker 
                if (tracker.TrackProcesses)
                    tracker.RecordNewCohort((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                        currentTimestep, _OffspringCohortAbundance, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex,
                        gridCellCohorts[actingCohort].CohortID, (uint)partial.NextCohortIDThreadLocked);

                // Subtract all of the reproductive potential mass of the parent cohort, which has been used to generate the new
                // cohort, from the delta reproductive potential mass and delta adult body mass
                deltas["reproductivebiomass"]["reproduction"] -= ReproductiveMassIncludingChangeThisTimeStep;
                deltas["biomass"]["reproduction"] -= AdultMassLost;

            }
            else
            {
                // Organism is not large enough, or it is not the breeding season, so take no action
            }

        }
Example #14
0
        /// <summary>
        /// Apply the changes from predation to prey cohorts, and update deltas for the predator cohort
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The acting cohort</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param>
        /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for predation</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level of output detail used in this model run</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]>
                              cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions,
                              FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations,
                              string outputDetail, MadingleyModelInitialisation initialisation)
        {
            if (trackProcesses.TrackProcesses)
            {
                Track = (RandomNumberGenerator.GetUniform() > 0.975) ? true : false;
            }


            TempDouble = 0.0;

            // Temporary variable to hold the total time spent eating + 1. Saves an extra calculation in CalculateAbundanceEaten
            double TotalTimeUnitsToHandlePlusOne = TimeUnitsToHandlePotentialFoodItems + 1;

            // Loop over potential prey functional groups
            foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat)
            {
                // Loop over cohorts within the functional group
                for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++)
                {
                    // Get the individual body mass of this cohort
                    _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass;

                    // Calculate the actual abundance of prey eaten from this cohort
                    if (_PotentialAbundanceEaten[FunctionalGroup][i] > 0)
                    {
                        // Calculate the actual abundance of prey eaten from this cohort
                        _AbundancesEaten[FunctionalGroup][i] = CalculateAbundanceEaten(_PotentialAbundanceEaten[FunctionalGroup][i], _PredatorAbundanceMultipliedByTimeEating,
                                                                                       TotalTimeUnitsToHandlePlusOne, gridCellCohorts[FunctionalGroup][i].CohortAbundance);
                    }
                    else
                    {
                        _AbundancesEaten[FunctionalGroup][i] = 0;
                    }

                    // Remove number of prey eaten from the prey cohort
                    gridCellCohorts[FunctionalGroup][i].CohortAbundance -= _AbundancesEaten[FunctionalGroup][i];

                    gridCellCohorts[actingCohort].TrophicIndex += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] * gridCellCohorts[FunctionalGroup][i].TrophicIndex;

                    // If the process tracker is set and output detail is set to high and the prey cohort has never been merged,
                    // then track its mortality owing to predation
                    if (trackProcesses.TrackProcesses)
                    {
                        if ((outputDetail == "high") && (gridCellCohorts[FunctionalGroup][i].CohortID.Count == 1) &&
                            AbundancesEaten[FunctionalGroup][i] > 0)
                        {
                            trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts
                                                           [FunctionalGroup][i].BirthTimeStep, currentTimestep, gridCellCohorts[FunctionalGroup][i].IndividualBodyMass,
                                                           gridCellCohorts[FunctionalGroup][i].AdultMass, gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex,
                                                           gridCellCohorts[FunctionalGroup][i].CohortID[0], AbundancesEaten[FunctionalGroup][i], "predation");
                        }

                        // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between
                        // prey and predator
                        if (specificLocations)
                        {
                            trackProcesses.RecordPredationMassFlow(currentTimestep, _BodyMassPrey, _BodyMassPredator, _BodyMassPrey *
                                                                   _AbundancesEaten[FunctionalGroup][i]);

                            if (outputDetail == "high")
                            {
                                trackProcesses.TrackPredationTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                                                                         gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex, gridCellCohorts[actingCohort].FunctionalGroupIndex,
                                                                         madingleyCohortDefinitions, (_AbundancesEaten[FunctionalGroup][i] * _BodyMassPrey), _BodyMassPredator, _BodyMassPrey,
                                                                         initialisation, cellEnvironment["Realm"][0] == 2.0);
                            }
                        }
                    }


                    // Check that the abundance eaten from this cohort is not negative
                    // Commented out for the purposes of speed
                    //Debug.Assert( _AbundancesEaten[FunctionalGroup][i].CompareTo(0.0) >= 0,
                    //     "Predation negative for this prey cohort" + actingCohort);

                    // Create a temporary value to speed up the predation function
                    // This is equivalent to the body mass of the prey cohort including reproductive potential mass, times the abundance eaten of the prey cohort,
                    // divided by the abundance of the predator
                    TempDouble += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] / _AbundancePredator;
                }
            }



            // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting (predator) cohort
            deltas["biomass"]["predation"] = TempDouble * _PredatorAssimilationEfficiency;

            // Move the biomass eaten but not assimilated by an individual into the organic matter pool
            deltas["organicpool"]["predation"] = TempDouble * _PredatorNonAssimilation * _AbundancePredator;

            // Check that the delta biomass from eating for the acting cohort is not negative
            //Debug.Assert(deltas["biomass"]["predation"] >= 0, "Predation yields negative biomass");

            // Calculate the total biomass eaten by the acting (predator) cohort
            _TotalBiomassEatenByCohort = deltas["biomass"]["predation"] * _AbundancePredator;
        }
        /// <summary>
        /// Initializes the ecosystem model
        /// </summary>
        /// <param name="initialisation">An instance of the model initialisation class</param> 
        /// <param name="scenarioParameters">The parameters for the scenarios to run</param>
        /// <param name="scenarioIndex">The index of the scenario being run</param>
        /// <param name="outputFilesSuffix">The suffix to be applied to all outputs from this model run</param>
        /// <param name="globalModelTimeStepUnit">The time step unit used in the model</param>
        /// <param name="simulation">The index of the simulation being run</param>
        public MadingleyModel(MadingleyModelInitialisation initialisation, ScenarioParameterInitialisation scenarioParameters, int scenarioIndex,
            string outputFilesSuffix, string globalModelTimeStepUnit, int simulation)
        {
            // Assign the properties for this model run
            AssignModelRunProperties(initialisation, scenarioParameters, scenarioIndex, outputFilesSuffix);

            // Set up list of global diagnostics
            SetUpGlobalDiagnosticsList();

            // Set up the model grid
            SetUpModelGrid(initialisation, scenarioParameters, scenarioIndex, simulation);

            // Set up model outputs
            SetUpOutputs(initialisation, simulation, scenarioIndex);

            // Make the initial outputs
            InitialOutputs(outputFilesSuffix, initialisation, CurrentMonth);

            // Instance the array of process trackers
            ProcessTrackers = new ProcessTracker[_CellList.Count];

            // Temporary variables
            Boolean varExists;

            // Set up process trackers for each grid cell
            for (int i = 0; i < _CellList.Count; i++)
            {
                ProcessTrackers[i] = new ProcessTracker(NumTimeSteps,
                EcosystemModelGrid.Lats, EcosystemModelGrid.Lons,
                _CellList,
                initialisation.ProcessTrackingOutputs,
                initialisation.TrackProcesses,
                CohortFunctionalGroupDefinitions,
                EcosystemModelGrid.GlobalMissingValue,
                outputFilesSuffix,
                initialisation.OutputPath, initialisation.ModelMassBins,
                SpecificLocations, i, initialisation,
                EcosystemModelGrid.GetEnviroLayer("Realm", 0, _CellList[i][0], _CellList[i][1], out varExists) == 2.0,
                EcosystemModelGrid.LatCellSize,
                EcosystemModelGrid.LonCellSize);
            }

            // Set up a cross cell process tracker
            TrackCrossCellProcesses = new CrossCellProcessTracker(initialisation.TrackCrossCellProcesses, "DispersalData", initialisation.OutputPath, outputFilesSuffix);

            //Set up a global process tracker
            if (SpecificLocations) initialisation.TrackGlobalProcesses = false;

            TrackGlobalProcesses = new GlobalProcessTracker(NumTimeSteps,
                EcosystemModelGrid.Lats, EcosystemModelGrid.Lons,
                _CellList,
                initialisation.ProcessTrackingOutputs,
                initialisation.TrackGlobalProcesses,
                CohortFunctionalGroupDefinitions,
                StockFunctionalGroupDefinitions,
                EcosystemModelGrid.GlobalMissingValue,
                outputFilesSuffix,
                initialisation.OutputPath, initialisation.ModelMassBins,
                SpecificLocations, initialisation,
                EcosystemModelGrid.LatCellSize,
                EcosystemModelGrid.LonCellSize);

            //Set-up the instance of OutputModelState
            WriteModelState = new OutputModelState(initialisation, outputFilesSuffix, simulation);

            if (SpecificLocations) initialisation.RunRealm = "";

            // Record the initial cohorts in the process trackers
            RecordInitialCohorts();

            // Initialise the class for cross-grid-cell ecology
            MadingleyEcologyCrossGridCell = new EcologyCrossGridCell();

            // Initialise the time step timer
            TimeStepTimer = new StopWatch();
            EcologyTimer = new StopWatch();
            OutputTimer = new StopWatch();

            // Set the global model time step unit
            _GlobalModelTimeStepUnit = globalModelTimeStepUnit;

            // Initialise the utility functions
            Utilities = new UtilityFunctions();

            // Initialise the climate change impacts class
            ClimateChangeSimulator = new ClimateChange();

            // Initialise the harvesting impacts class
            HarvestingSimulator = new Harvesting(EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, (float)EcosystemModelGrid.LatCellSize);
        }
        /// <summary>
        /// Run mortality
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for mortality</param>
        /// <param name="partial">Thread-locked variables</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level output detail being used for the current model run</param>
        /// <param name="currentMonth">The current model month</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks,
            int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas,
            FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions,
            uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial,
            Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation)
        {

            // Variables to hold the mortality rates
            double MortalityRateBackground;
            double MortalityRateSenescence;
            double MortalityRateStarvation;

            // Variable to hold the total abundance lost to all forms of mortality
            double MortalityTotal;

            // Individual body mass including change this time step as a result of other ecological processes
            double BodyMassIncludingChangeThisTimeStep;

            // Individual reproductive mass including change this time step as a result of other ecological processes
            double ReproductiveMassIncludingChangeThisTimeStep;

            // Calculate the body mass of individuals in this cohort including mass gained through eating this time step, up to but not exceeding adult body mass for this cohort. 
            // Should be fine because these deductions are made in the reproduction implementation, but use Math.Min to double check.

            BodyMassIncludingChangeThisTimeStep = 0.0;

            // Loop over all items in the biomass deltas
            foreach (var Biomass in deltas["biomass"])
            {
                // Add the delta biomass to net biomass
                BodyMassIncludingChangeThisTimeStep += Biomass.Value;
            }
            BodyMassIncludingChangeThisTimeStep = Math.Min(gridCellCohorts[actingCohort].AdultMass, BodyMassIncludingChangeThisTimeStep + gridCellCohorts[actingCohort].IndividualBodyMass);

            // Temporary variable to hold net reproductive biomass change of individuals in this cohort as a result of other ecological processes
            ReproductiveMassIncludingChangeThisTimeStep = 0.0;

            // Loop over all items in the biomass deltas
            foreach (var Biomass in deltas["reproductivebiomass"])
            {
                // Add the delta biomass to net biomass
                ReproductiveMassIncludingChangeThisTimeStep += Biomass.Value;
            }

            ReproductiveMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualReproductivePotentialMass;

            // Check to see if the cohort has already been killed by predation etc
            if ((BodyMassIncludingChangeThisTimeStep).CompareTo(0.0) <= 0)
            {
                // If individual body mass is not greater than zero, then all individuals become extinct
                MortalityTotal = gridCellCohorts[actingCohort].CohortAbundance;
            }
            else
            {
                // Calculate background mortality rate
                MortalityRateBackground = Implementations["basic background mortality"].CalculateMortalityRate(gridCellCohorts,
                    actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep);

                // If the cohort has matured, then calculate senescence mortality rate, otherwise set rate to zero
                if (gridCellCohorts[actingCohort].MaturityTimeStep != uint.MaxValue)
                {
                    MortalityRateSenescence = Implementations["basic senescence mortality"].CalculateMortalityRate(gridCellCohorts,
                        actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep);
                }
                else
                {
                    MortalityRateSenescence = 0.0;
                }

                // Calculate the starvation mortality rate based on individual body mass and maximum body mass ever
                // achieved by this cohort
                MortalityRateStarvation = Implementations["basic starvation mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep);

                // Calculate the number of individuals that suffer mortality this time step from all sources of mortality
                MortalityTotal = (1 - Math.Exp(-MortalityRateBackground - MortalityRateSenescence -
                    MortalityRateStarvation)) * gridCellCohorts[actingCohort].CohortAbundance;
            }

            // If the process tracker is on and output detail is set to high and this cohort has not been merged yet, then record
            // the number of individuals that have died
            if (trackProcesses.TrackProcesses && (outputDetail == "high") && (!gridCellCohorts[actingCohort].Merged))
            {
                trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[actingCohort].BirthTimeStep,
                    currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, gridCellCohorts[actingCohort].AdultMass,
                    gridCellCohorts[actingCohort].FunctionalGroupIndex,
                    gridCellCohorts[actingCohort].CohortID[0], MortalityTotal, "sen/bg/starv");
            }

            // Remove individuals that have died from the delta abundance for this cohort
            deltas["abundance"]["mortality"] = -MortalityTotal;

            // Add the biomass of individuals that have died to the delta biomass in the organic pool (including reproductive 
            // potential mass, and mass gained through eating, and excluding mass lost through metabolism)
            deltas["organicpool"]["mortality"] = MortalityTotal * (BodyMassIncludingChangeThisTimeStep + ReproductiveMassIncludingChangeThisTimeStep);
        }
 /// <summary>
 /// Update the properties of the acting cohort and of the environmental biomass pools after running the ecological processes for a cohort
 /// </summary>
 /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
 /// <param name="gridCellStocks">The stocks in the current grid cell</param>
 /// <param name="actingCohort">The acting cohort</param>
 /// <param name="cellEnvironment">The environment of the current grid cell</param>
 /// <param name="deltas">The sorted list of deltas for the current grid cell</param>
 /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
 /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param>
 /// <param name="currentTimestep">The current model time step</param>
 /// <param name="tracker">A process tracker</param>
 public void UpdateEcology(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, 
     SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions 
     madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker)
 {
     // Apply the results of within-cell ecological processes
     ApplyEcologicalProcessResults.UpdateAllEcology(gridCellCohorts, actingCohort, cellEnvironment, deltas, currentTimestep, tracker);
 }
        /// <summary>
        /// Run ecological processes that operate on cohorts within a single grid cell
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The acting cohort</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">A sorted list of deltas to track changes in abundances and biomasses during the ecological processes</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="trackProcesses">An instance of the process tracker</param>
        /// <param name="partial">Thread-locked local variables</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level of output detail being used for this model run</param>
        /// <param name="currentMonth">The current model month</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunWithinCellEcology(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, 
            SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions 
            madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, 
            ref ThreadLockedParallelVariables partial, Boolean specificLocations,string outputDetail, uint currentMonth, 
            MadingleyModelInitialisation initialisation)
        {
            // RUN EATING
            _EatingFormulations["Basic eating"].RunEcologicalProcess(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment,
                deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, trackProcesses, ref partial,
                specificLocations, outputDetail, currentMonth, initialisation);

            // RUN METABOLISM - THIS TIME TAKE THE METABOLIC LOSS TAKING INTO ACCOUNT WHAT HAS BEEN INGESTED THROUGH EATING
            _MetabolismFormulations["Basic metabolism"].RunEcologicalProcess(gridCellCohorts, gridCellStocks, actingCohort,
                cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, trackProcesses, ref partial,
                specificLocations, outputDetail, currentMonth, initialisation);

            // RUN REPRODUCTION - TAKING INTO ACCOUNT NET BIOMASS CHANGES RESULTING FROM EATING AND METABOLISING
            _ReproductionFormulations["Basic reproduction"].RunEcologicalProcess(gridCellCohorts, gridCellStocks, actingCohort,
                cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, trackProcesses, ref partial,
                specificLocations, outputDetail, currentMonth, initialisation);

            // RUN MORTALITY - TAKING INTO ACCOUNT NET BIOMASS CHANGES RESULTING FROM EATING, METABOLISM AND REPRODUCTION
            _MortalityFormulations["Basic mortality"].RunEcologicalProcess(gridCellCohorts, gridCellStocks, actingCohort,
                cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, trackProcesses, ref partial,
                specificLocations, outputDetail, currentMonth, initialisation);
        }
        /// <summary>
        /// Calculate the actual amount eaten in herbivory, apply the changes to the eaten autotroph stocks, and update deltas for the herbivore cohort
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in this grid cell</param>
        /// <param name="gridCellStocks">The stocks in this grid cell</param>
        /// <param name="actingCohort">The acting cohort</param>
        /// <param name="cellEnvironment">The environmental conditions in this grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param>
        /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for herbivory</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level of output detail being used in this model run</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]>
            cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions,
            FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations,
            string outputDetail, MadingleyModelInitialisation initialisation)
        {

            EdibleScaling = 1.0;
            if (cellEnvironment["Realm"][0] == 1.0) EdibleScaling = 0.1;

            // Loop over autotroph functional groups that can be eaten
            foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat)
            {
                // Loop over stocks within the functional groups
                for (int i = 0; i < gridCellStocks[FunctionalGroup].Count; i++)
                {
                    // Get the mass from this stock that is available for eating (assumes only 10% is edible in the terrestrial realm)
                    EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass * EdibleScaling;

                    // Calculate the biomass actually eaten from this stock by the acting cohort
                    _BiomassesEaten[FunctionalGroup][i] = CalculateBiomassesEaten(_PotentialBiomassesEaten[FunctionalGroup][i],
                        _TimeUnitsToHandlePotentialFoodItems, gridCellCohorts[actingCohort].CohortAbundance, EdibleMass);

                    gridCellCohorts[actingCohort].TrophicIndex += _BiomassesEaten[FunctionalGroup][i];

                    // Remove the biomass eaten from the autotroph stock
                    gridCellStocks[FunctionalGroup][i].TotalBiomass -= _BiomassesEaten[FunctionalGroup][i];

                    // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between
                    // primary producer and herbivore
                    if (specificLocations && trackProcesses.TrackProcesses)
                    {
                        trackProcesses.RecordHerbivoryMassFlow(currentTimestep, _BodyMassHerbivore, _BiomassesEaten[FunctionalGroup][i]);
                    }

                    // If track processes has been specified and the output detail level is set to high and the model is being run for specific locations,
                    // then track the flow of mass between trophic levels
                    if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations)
                    {
                        trackProcesses.TrackHerbivoryTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                            gridCellCohorts[actingCohort].FunctionalGroupIndex, madingleyCohortDefinitions, _BiomassesEaten[FunctionalGroup][i], _BodyMassHerbivore, initialisation, cellEnvironment["Realm"][0] == 2.0);
                    }

                    // Check that the biomass eaten is not a negative value
                    // Commented out for purposes of speed
                    //Debug.Assert(_BiomassesEaten[FunctionalGroup][i] >= 0,
                    //    "Herbivory negative for this herbivore cohort" + actingCohort);

                    // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting cohort
                    deltas["biomass"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * AssimilationEfficiency / gridCellCohorts[actingCohort].CohortAbundance;

                    // Move the biomass eaten but not assimilated by an individual into the organic matter pool
                    deltas["organicpool"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * (1 - AssimilationEfficiency);
                }

                // Check that the delta biomass from eating for the acting cohort is not negative
                // Commented out for the purposes of speed
                //Debug.Assert(deltas["biomass"]["herbivory"] >= 0, "Delta biomass from herbviory is negative");

                // Calculate the total biomass eaten by the acting (herbivore) cohort
                _TotalBiomassEatenByCohort = deltas["biomass"]["herbivory"] * gridCellCohorts[actingCohort].CohortAbundance;
            }
        }
        /// <summary>
        /// Run mortality
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for mortality</param>
        /// <param name="partial">Thread-locked variables</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level output detail being used for the current model run</param>
        /// <param name="currentMonth">The current model month</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks,
                                         int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas,
                                         FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions,
                                         uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial,
                                         Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation)
        {
            // Variables to hold the mortality rates
            double MortalityRateBackground;
            double MortalityRateSenescence;
            double MortalityRateStarvation;

            // Variable to hold the total abundance lost to all forms of mortality
            double MortalityTotal;

            // Individual body mass including change this time step as a result of other ecological processes
            double BodyMassIncludingChangeThisTimeStep;

            // Individual reproductive mass including change this time step as a result of other ecological processes
            double ReproductiveMassIncludingChangeThisTimeStep;

            // Calculate the body mass of individuals in this cohort including mass gained through eating this time step, up to but not exceeding adult body mass for this cohort.
            // Should be fine because these deductions are made in the reproduction implementation, but use Math.Min to double check.

            BodyMassIncludingChangeThisTimeStep = 0.0;

            // Loop over all items in the biomass deltas
            foreach (var Biomass in deltas["biomass"])
            {
                // Add the delta biomass to net biomass
                BodyMassIncludingChangeThisTimeStep += Biomass.Value;
            }
            BodyMassIncludingChangeThisTimeStep = Math.Min(gridCellCohorts[actingCohort].AdultMass, BodyMassIncludingChangeThisTimeStep + gridCellCohorts[actingCohort].IndividualBodyMass);

            // Temporary variable to hold net reproductive biomass change of individuals in this cohort as a result of other ecological processes
            ReproductiveMassIncludingChangeThisTimeStep = 0.0;

            // Loop over all items in the biomass deltas
            foreach (var Biomass in deltas["reproductivebiomass"])
            {
                // Add the delta biomass to net biomass
                ReproductiveMassIncludingChangeThisTimeStep += Biomass.Value;
            }

            ReproductiveMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualReproductivePotentialMass;

            // Check to see if the cohort has already been killed by predation etc
            if ((BodyMassIncludingChangeThisTimeStep).CompareTo(0.0) <= 0)
            {
                // If individual body mass is not greater than zero, then all individuals become extinct
                MortalityTotal = gridCellCohorts[actingCohort].CohortAbundance;
            }
            else
            {
                // Calculate background mortality rate
                MortalityRateBackground = Implementations["basic background mortality"].CalculateMortalityRate(gridCellCohorts,
                                                                                                               actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep);

                // If the cohort has matured, then calculate senescence mortality rate, otherwise set rate to zero
                if (gridCellCohorts[actingCohort].MaturityTimeStep != uint.MaxValue)
                {
                    MortalityRateSenescence = Implementations["basic senescence mortality"].CalculateMortalityRate(gridCellCohorts,
                                                                                                                   actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep);
                }
                else
                {
                    MortalityRateSenescence = 0.0;
                }

                // Calculate the starvation mortality rate based on individual body mass and maximum body mass ever
                // achieved by this cohort
                MortalityRateStarvation = Implementations["basic starvation mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep);

                // Calculate the number of individuals that suffer mortality this time step from all sources of mortality
                MortalityTotal = (1 - Math.Exp(-MortalityRateBackground - MortalityRateSenescence -
                                               MortalityRateStarvation)) * gridCellCohorts[actingCohort].CohortAbundance;
            }

            // If the process tracker is on and output detail is set to high and this cohort has not been merged yet, then record
            // the number of individuals that have died
            if (trackProcesses.TrackProcesses && (outputDetail == "high") && (!gridCellCohorts[actingCohort].Merged))
            {
                trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[actingCohort].BirthTimeStep,
                                               currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, gridCellCohorts[actingCohort].AdultMass,
                                               gridCellCohorts[actingCohort].FunctionalGroupIndex,
                                               gridCellCohorts[actingCohort].CohortID[0], MortalityTotal, "sen/bg/starv");
            }

            // Remove individuals that have died from the delta abundance for this cohort
            deltas["abundance"]["mortality"] = -MortalityTotal;

            // Add the biomass of individuals that have died to the delta biomass in the organic pool (including reproductive
            // potential mass, and mass gained through eating, and excluding mass lost through metabolism)
            deltas["organicpool"]["mortality"] = MortalityTotal * (BodyMassIncludingChangeThisTimeStep + ReproductiveMassIncludingChangeThisTimeStep);
        }
        /// <summary>
        /// Apply the changes from predation to prey cohorts, and update deltas for the predator cohort
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The acting cohort</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param>
        /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for predation</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level of output detail used in this model run</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]>
            cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions,
            FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations,
            string outputDetail, MadingleyModelInitialisation initialisation)
        {
            if (trackProcesses.TrackProcesses)
            {
                Track = (RandomNumberGenerator.GetUniform() > 0.975) ? true : false;
            }

            TempDouble = 0.0;

            // Temporary variable to hold the total time spent eating + 1. Saves an extra calculation in CalculateAbundanceEaten
            double TotalTimeUnitsToHandlePlusOne = TimeUnitsToHandlePotentialFoodItems + 1;

            // Loop over potential prey functional groups
            foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat)
            {

                // Loop over cohorts within the functional group
                for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++)
                {
                    // Get the individual body mass of this cohort
                    _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass;

                     // Calculate the actual abundance of prey eaten from this cohort
                    if (gridCellCohorts[FunctionalGroup][i].CohortAbundance > 0)
                    {

                     // Calculate the actual abundance of prey eaten from this cohort
                    _AbundancesEaten[FunctionalGroup][i] = CalculateAbundanceEaten(_PotentialAbundanceEaten[FunctionalGroup][i], _PredatorAbundanceMultipliedByTimeEating,
                        TotalTimeUnitsToHandlePlusOne, gridCellCohorts[FunctionalGroup][i].CohortAbundance);

                    }
                    else
                        _AbundancesEaten[FunctionalGroup][i] = 0;

                    // Remove number of prey eaten from the prey cohort
                    gridCellCohorts[FunctionalGroup][i].CohortAbundance -= _AbundancesEaten[FunctionalGroup][i];

                    gridCellCohorts[actingCohort].TrophicIndex += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] * gridCellCohorts[FunctionalGroup][i].TrophicIndex;

                    // If the process tracker is set and output detail is set to high and the prey cohort has never been merged,
                    // then track its mortality owing to predation
                    if (trackProcesses.TrackProcesses)
                    {
                        if ((outputDetail == "high") && (gridCellCohorts[FunctionalGroup][i].CohortID.Count == 1)
                        && AbundancesEaten[FunctionalGroup][i] > 0)
                        {
                            trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts
                                [FunctionalGroup][i].BirthTimeStep, currentTimestep, gridCellCohorts[FunctionalGroup][i].IndividualBodyMass,
                                gridCellCohorts[FunctionalGroup][i].AdultMass, gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex,
                                gridCellCohorts[FunctionalGroup][i].CohortID[0], AbundancesEaten[FunctionalGroup][i], "predation");
                        }

                        // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between
                        // prey and predator
                        if (specificLocations)
                        {
                            trackProcesses.RecordPredationMassFlow(currentTimestep, _BodyMassPrey, _BodyMassPredator, _BodyMassPrey *
                                _AbundancesEaten[FunctionalGroup][i]);

                        if (outputDetail == "high")
                            trackProcesses.TrackPredationTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                                gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex, gridCellCohorts[actingCohort].FunctionalGroupIndex,
                                madingleyCohortDefinitions, (_AbundancesEaten[FunctionalGroup][i] * _BodyMassPrey), _BodyMassPredator, _BodyMassPrey,
                                initialisation, cellEnvironment["Realm"][0] == 2.0);

                        }

                    }

                    // Check that the abundance eaten from this cohort is not negative
                    // Commented out for the purposes of speed
                    //Debug.Assert( _AbundancesEaten[FunctionalGroup][i].CompareTo(0.0) >= 0,
                    //     "Predation negative for this prey cohort" + actingCohort);

                    // Create a temporary value to speed up the predation function
                    // This is equivalent to the body mass of the prey cohort including reproductive potential mass, times the abundance eaten of the prey cohort,
                    // divided by the abundance of the predator
                    TempDouble += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] / _AbundancePredator;

                }
            }

            // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting (predator) cohort
            deltas["biomass"]["predation"] = TempDouble * _PredatorAssimilationEfficiency;

            // Move the biomass eaten but not assimilated by an individual into the organic matter pool
            deltas["organicpool"]["predation"] = TempDouble * _PredatorNonAssimilation * _AbundancePredator;

            // Check that the delta biomass from eating for the acting cohort is not negative
            //Debug.Assert(deltas["biomass"]["predation"] >= 0, "Predation yields negative biomass");

            // Calculate the total biomass eaten by the acting (predator) cohort
            _TotalBiomassEatenByCohort = deltas["biomass"]["predation"] * _AbundancePredator;
        }
        /// <summary>
        /// Calculate the actual amount eaten in herbivory, apply the changes to the eaten autotroph stocks, and update deltas for the herbivore cohort
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in this grid cell</param>
        /// <param name="gridCellStocks">The stocks in this grid cell</param>
        /// <param name="actingCohort">The acting cohort</param>
        /// <param name="cellEnvironment">The environmental conditions in this grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param>
        /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for herbivory</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="outputDetail">The level of output detail being used in this model run</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]>
                              cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions,
                              FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations,
                              string outputDetail, MadingleyModelInitialisation initialisation)
        {
            EdibleScaling = 1.0;
            if (cellEnvironment["Realm"][0] == 1.0)
            {
                EdibleScaling = 0.1;
            }

            // Loop over autotroph functional groups that can be eaten
            foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat)
            {
                // Loop over stocks within the functional groups
                for (int i = 0; i < gridCellStocks[FunctionalGroup].Count; i++)
                {
                    // Get the mass from this stock that is available for eating (assumes only 10% is edible in the terrestrial realm)
                    EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass * EdibleScaling;

                    // Calculate the biomass actually eaten from this stock by the acting cohort
                    _BiomassesEaten[FunctionalGroup][i] = CalculateBiomassesEaten(_PotentialBiomassesEaten[FunctionalGroup][i],
                                                                                  _TimeUnitsToHandlePotentialFoodItems, gridCellCohorts[actingCohort].CohortAbundance, EdibleMass);

                    gridCellCohorts[actingCohort].TrophicIndex += _BiomassesEaten[FunctionalGroup][i];

                    // Remove the biomass eaten from the autotroph stock
                    gridCellStocks[FunctionalGroup][i].TotalBiomass -= _BiomassesEaten[FunctionalGroup][i];

                    // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between
                    // primary producer and herbivore
                    if (specificLocations && trackProcesses.TrackProcesses)
                    {
                        trackProcesses.RecordHerbivoryMassFlow(currentTimestep, _BodyMassHerbivore, _BiomassesEaten[FunctionalGroup][i]);
                    }

                    // If track processes has been specified and the output detail level is set to high and the model is being run for specific locations,
                    // then track the flow of mass between trophic levels
                    if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations)
                    {
                        trackProcesses.TrackHerbivoryTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],
                                                                 gridCellCohorts[actingCohort].FunctionalGroupIndex, madingleyCohortDefinitions, _BiomassesEaten[FunctionalGroup][i], _BodyMassHerbivore, initialisation, cellEnvironment["Realm"][0] == 2.0);
                    }

                    // Check that the biomass eaten is not a negative value
                    // Commented out for purposes of speed
                    //Debug.Assert(_BiomassesEaten[FunctionalGroup][i] >= 0,
                    //    "Herbivory negative for this herbivore cohort" + actingCohort);

                    // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting cohort
                    deltas["biomass"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * AssimilationEfficiency / gridCellCohorts[actingCohort].CohortAbundance;

                    // Move the biomass eaten but not assimilated by an individual into the organic matter pool
                    deltas["organicpool"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * (1 - AssimilationEfficiency);
                }

                // Check that the delta biomass from eating for the acting cohort is not negative
                // Commented out for the purposes of speed
                //Debug.Assert(deltas["biomass"]["herbivory"] >= 0, "Delta biomass from herbviory is negative");

                // Calculate the total biomass eaten by the acting (herbivore) cohort
                _TotalBiomassEatenByCohort = deltas["biomass"]["herbivory"] * gridCellCohorts[actingCohort].CohortAbundance;
            }
        }