//Function to bin cohorts according to the mass bins defined for the catch data
        /// <summary>
        /// Bin cohorts according to the mass bins defined for the catch data
        /// Constructs a list of functional group and cohort indices falling within each mass bin
        /// as well as the total biomass available to be fished in each
        /// </summary>
        /// <param name="c">The grid cell cohorts</param>
        /// <param name="fishCatch">Fisheries catch data</param>
        public void BinCohorts(GridCellCohortHandler c, InputCatchData fishCatch, FunctionalGroupDefinitions cohortFGs)
        {
            int mb = 0;

            int[] FishFGs = cohortFGs.GetFunctionalGroupIndex("Endo/Ectotherm", "Ectotherm",false);

            for (int i = 0; i < BinnedCohorts.Length; i++)
            {
                BinnedCohorts[i] = new List<Tuple<int[], double>>();
            }

            foreach (int fg in FishFGs)
            {
                for (int i = 0; i < c[fg].Count(); i++)
                {
                    //Find the mass bin for this cohort
                    mb = fishCatch.MassBins.ToList().FindIndex(a => a >= c[fg,i].AdultMass);
                    if (mb < 0) mb = fishCatch.UnknownMassBinIndex - 1;

                    //Check if the current bodymass is greater than the proportion of the adult mass
                    if (c[fg, i].IndividualBodyMass >= c[fg, i].AdultMass * AdultMassProportionFished)
                    {
                        //Calculate the total biomass of this cohort
                        double CohortBiomass = (c[fg, i].IndividualBodyMass + c[fg, i].IndividualReproductivePotentialMass) *
                                                    c[fg, i].CohortAbundance;
                        //Add the indices and total biomass to the bins
                        BinnedCohorts[mb].Add(new Tuple<int[], double>(new int[] { fg, i }, CohortBiomass));
                        BinnedTotalModelBiomass[mb] += CohortBiomass;
                    }
                }
            }
        }
        /// <summary>
        /// Update the abundance of the acting cohort according to the delta abundances from the ecological processes
        /// </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="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        private void UpdateAbundance(GridCellCohortHandler gridCellCohorts, int[] actingCohort, Dictionary<string,Dictionary<string, double>> deltas)
        {
            // Extract the abundance deltas from the sorted list of all deltas
            Dictionary<string, double> deltaAbundance = deltas["abundance"];

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

            // Variable to calculate net abundance change to check that cohort abundance will not become negative
            double NetAbundanceChange = 0.0;

            // Loop over all abundance deltas
            foreach (var key in KeyStrings)
            {
                // Update net abundance change
                NetAbundanceChange += deltaAbundance[key];
            }
            // Check that cohort abundance will not become negative
            Debug.Assert((gridCellCohorts[actingCohort].CohortAbundance + NetAbundanceChange).CompareTo(0.0) >= 0, "Cohort abundance < 0");

            //Loop over all keys in the abundance deltas sorted list
            foreach (var key in KeyStrings)
            {
                // Update the abundance of the acting cohort
                gridCellCohorts[actingCohort].CohortAbundance += deltaAbundance[key];
                // Reset the current delta abundance to zero
                deltaAbundance[key] = 0.0;
            }
        }
        public void ApplyCatches(GridCellCohortHandler c, InputCatchData fishCatch, int latIndex, int lonIndex)
        {
            //Hold the total catch in each mass bin for this cell
            double[] BinnedCellCatch = new double[fishCatch.MassBins.Length];

            //TO DO: make the time division flexible according to the model timestep
            for (int mb = 0; mb < BinnedCellCatch.Length; mb++)
            {
                BinnedCellCatch[mb] = fishCatch.ModelGridCatch[latIndex, lonIndex, mb]/12.0;

                if (BinnedCellCatch[mb] > 0)
                {

                    if (BinnedTotalModelBiomass[mb] <= BinnedCellCatch[mb])
                    {
                        DefecitCatch[mb] = BinnedCellCatch[mb] - BinnedTotalModelBiomass[mb];
                        BinnedCellCatch[mb] = BinnedTotalModelBiomass[mb];
                    }

                    foreach (var v in BinnedCohorts[mb])
                    {
                        double Contribution = v.Item2 / BinnedTotalModelBiomass[mb];
                        double AbundanceCaught = Contribution * BinnedCellCatch[mb] / (c[v.Item1].IndividualBodyMass + c[v.Item1].IndividualReproductivePotentialMass);
                        c[v.Item1].CohortAbundance -= AbundanceCaught;
                    }
                }

            }
        }
Example #4
0
        /// <summary>
        /// Apply the results of direct harvesting of animals to the cohorts in a grid cell
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="targetBiomass">The target biomass to be harvested</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        public void ApplyHarvesting(GridCellCohortHandler gridCellCohorts, double targetBiomass,
            SortedList<string, double[]> cellEnvironment, uint currentTimestep)
        {
            // Create variable to hold total available animal biomass
            double TotalAvailableBiomass = 0.0;
            double TotalBiomassTimesPreference = 0.0;
            double TotalBiomassHarvested = 0.0;

            // Create variable to hold estimate of preference for a given cohort
            double Preference;

            // Create variable to hold the biomass of a cohort actually harvested
            double BiomassHarvested;

            // Create jagged arrays mirroring the cohort handler to hold calculations
            double[][] AvailableBiomass = new double[gridCellCohorts.Count][];
            double[][] BiomassTimesPreference = new double[gridCellCohorts.Count][];

            // Loop over functional groups and initialise rows in the jagged arrays
            for (int i = 0; i < gridCellCohorts.Count; i++)
            {
                AvailableBiomass[i] = new double[gridCellCohorts[i].Count];
                BiomassTimesPreference[i] = new double[gridCellCohorts[i].Count];
            }

            // Convert target biomass from kg per km squared to g per cell
            targetBiomass *= 1000; //kg to g conversion
            targetBiomass *= cellEnvironment["Cell Area"][0];

            // Loop over cohorts and calculate available biomass and biomass times preference in each cohort, and total available biomass
            for (int fg = 0; fg < gridCellCohorts.Count; fg++)
            {
                for (int c = 0; c < gridCellCohorts[fg].Count; c++)
                {
                    TotalAvailableBiomass += (gridCellCohorts[fg][c].IndividualBodyMass * gridCellCohorts[fg][c].CohortAbundance);
                    AvailableBiomass[fg][c] = (gridCellCohorts[fg][c].IndividualBodyMass * gridCellCohorts[fg][c].CohortAbundance);
                    Preference = 1 / (1 + Math.Exp(-(-8 + 0.8 * Math.Log(gridCellCohorts[fg][c].IndividualBodyMass))));
                    BiomassTimesPreference[fg][c] = AvailableBiomass[fg][c] * Preference;
                    TotalBiomassTimesPreference += BiomassTimesPreference[fg][c];
                }
            }

            // Loop over cohorts again, and calculate and apply the actual amount of biomass harvested
            for (int fg = 0; fg < gridCellCohorts.Count; fg++)
            {
                for (int c = 0; c < gridCellCohorts[fg].Count; c++)
                {
                    BiomassHarvested = Math.Min(AvailableBiomass[fg][c], targetBiomass * BiomassTimesPreference[fg][c] / TotalBiomassTimesPreference);
                    TotalBiomassHarvested += BiomassHarvested;
                    gridCellCohorts[fg][c].CohortAbundance -= (BiomassHarvested / gridCellCohorts[fg][c].IndividualBodyMass);
                }
            }

            SyncHarvestWriter.WriteLine(
                   cellEnvironment["Latitude"][0] + "," +
                   cellEnvironment["Longitude"][0] + "," +
                    currentTimestep + "," +
                    TotalBiomassHarvested);
        }
 /// <summary>
 /// Initializes an implementation of eating
 /// </summary>
 /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
 /// <param name="gridCellStocks">The stocks in the current grid cell</param>
 /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
 /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param>
 /// <param name="implementationKey">The name of the implementation of eating to initialize</param>
 /// <param name="cellEnvironment">The environment in the current grid cell</param>
 /// <remarks>Eating needs to be initialized every time step</remarks>
 public void InitializeEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, 
     FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, 
     string implementationKey, SortedList<string, double[]> cellEnvironment)
 {
     // Initialize the implementation of the eating process
     Implementations[implementationKey].InitializeEatingPerTimeStep(gridCellCohorts, gridCellStocks,
         madingleyCohortDefinitions, madingleyStockDefinitions,cellEnvironment);
 }
 public InputModelState(
     GridCellCohortHandler[,] gridCellCohorts,
     GridCellStockHandler[,] gridCellStocks)
 {
     this._GridCellCohorts = gridCellCohorts;
     this._GridCellStocks = gridCellStocks;
     this._InputState = true;
 }
        /// <summary>
        /// Calculate the proportion of individuals in a cohort that die from starvation mortality each time step
        /// </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="bodyMassIncludingChangeThisTimeStep">Body mass including change from other ecological functions this time step; should not exceed adult mass</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>
        /// <returns>The proportion of individuals in the cohort that die from starvation mortality</returns>
        public double CalculateMortalityRate(GridCellCohortHandler gridCellCohorts, int[] actingCohort, double bodyMassIncludingChangeThisTimeStep, Dictionary<string, Dictionary<string, double>> deltas, uint currentTimestep)
        {
            // Calculate the starvation rate of the cohort given individual body masses compared to the maximum body
            // mass ever achieved
            double _MortalityRate = CalculateStarvationRate(gridCellCohorts, actingCohort, bodyMassIncludingChangeThisTimeStep, deltas);

            // Convert the mortality rate from formulation time step units to model time step units
            return _MortalityRate * DeltaT;
        }
 /// <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>
        /// Run metabolism for the acting cohort
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for the stock functional groups in the model</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="currentMonth">The current model month</param>
        public void RunMetabolism(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, 
            int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> 
            deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, 
            uint currentTimestep, uint currentMonth)
        {
            // Calculate metabolic loss for an individual and add the value to the delta biomass for metabolism
            deltas["biomass"]["metabolism"] = -CalculateIndividualMetabolicRate(gridCellCohorts[actingCohort].IndividualBodyMass,
                cellEnvironment["Temperature"][currentMonth] + _TemperatureUnitsConvert) * _DeltaT;

            // If metabolic loss is greater than individual body mass after herbivory and predation, then set equal to individual body mass
            deltas["biomass"]["metabolism"] = Math.Max(deltas["biomass"]["metabolism"],-(gridCellCohorts[actingCohort].IndividualBodyMass + deltas["biomass"]["predation"] + deltas["biomass"]["herbivory"]));

            // Add total metabolic loss for all individuals in the cohort to delta biomass for metabolism in the respiratory CO2 pool
            deltas["respiratoryCO2pool"]["metabolism"] = -deltas["biomass"]["metabolism"] * gridCellCohorts[actingCohort].CohortAbundance;
        }
        /// <summary>
        /// Calculate the rate of individuals in a cohort that die from senescence mortality in a model time step
        /// </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="bodyMassIncludingChangeThisTimeStep">The body mass of individuals in the acting cohort, including body mass change this time step through eating and mortality</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>
        /// <returns>The rate of individuals in the cohort that die from senescence mortality</returns>
        public double CalculateMortalityRate(GridCellCohortHandler gridCellCohorts, int[] actingCohort, 
            double bodyMassIncludingChangeThisTimeStep, Dictionary<string, Dictionary<string, double>> deltas, uint currentTimestep)
        {
            // Calculate the age (in model time steps) that the cohort reached maturity
            double TimeToMaturity = gridCellCohorts[actingCohort].MaturityTimeStep - gridCellCohorts[actingCohort].BirthTimeStep;

            // Calculate how many model time steps since the cohort reached maturity
            double AgePostMaturity = currentTimestep - gridCellCohorts[actingCohort].MaturityTimeStep;

            // Calculate the time since maturity as a fraction of the time that it took the cohort to reach maturity
            double FractionalAgePostMaturity = AgePostMaturity/(TimeToMaturity+1);

            // Calculate the mortality rate per mortality formulation time step as a function of the exponential of the previous fraction
            double AgeRelatedMortalityRate = _MortalityRate * Math.Exp(FractionalAgePostMaturity);

            // Convert the mortality rate from formulation time step units to model time step units
            return AgeRelatedMortalityRate * DeltaT;
        }
        /// <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);
        }
 public void RecordCohorts(uint lat, uint lon, uint currentTimeStep, GridCellCohortHandler cohorts)
 {
     foreach (var fg in cohorts)
     {
         foreach (Cohort c in fg)
         {
             SyncCohortWriter.WriteLine(
                 Convert.ToString(lat) + '\t' +
                 Convert.ToString(lon) + '\t' +
                 Convert.ToString(currentTimeStep) + '\t' +
                 Convert.ToString(c.FunctionalGroupIndex) + '\t' +
                 Convert.ToString(c.IndividualBodyMass) + '\t' +
                 Convert.ToString(c.CohortAbundance) + '\t' +
                 Convert.ToString(c.JuvenileMass) + '\t' +
                 Convert.ToString(c.AdultMass));
         }
         
     }
 }
        /// <summary>
        /// Initialises the feeding interactions matrix each timestep
        /// </summary>
        public void InitialiseInteractionsMatrix(GridCellCohortHandler gridCellCohorts)
        {
            int MaxCohorts = 0;
            foreach (var CohortList in gridCellCohorts)
            {
                if (CohortList.Count > MaxCohorts) MaxCohorts = CohortList.Count;
            }

            FeedingInteractionsMatrixPredation = new List<Tuple<int, int, double, double>>[gridCellCohorts.Count, MaxCohorts];
            FeedingInteractionsMatrixHerbivory = new List<Tuple<int, int, double, double>>[gridCellCohorts.Count, MaxCohorts];

            for (int i = 0; i < gridCellCohorts.Count; i++)
            {
                for (int c = 0; c < MaxCohorts; c++)
                {
                    FeedingInteractionsMatrixPredation[i, c] = new List<Tuple<int, int, double, double>>();
                    FeedingInteractionsMatrixHerbivory[i, c] = new List<Tuple<int, int, double, double>>();
                }
            }
        }
        /// <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>
        /// Initialises predation implementation each time step
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param>
        /// <remarks>This only works if: a) predation is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips
        /// It is critical to run this every time step</remarks>
        public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions)
        {
            // Get the functional group indices of all heterotroph cohorts (i.e. potential prey)
            _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "heterotroph", false);

            // Initialise the vector to hold the number of cohorts in each functional group at the start of the time step
            NumberCohortsPerFunctionalGroupNoNewCohorts = new int[gridCellCohorts.Count];

            // Initialise the jagged arrays to hold the potential and actual numbers of prey eaten in each of the grid cell cohorts
            _AbundancesEaten = new double[gridCellCohorts.Count][];
            _PotentialAbundanceEaten = new double[gridCellCohorts.Count][];

            // Initialise the vector to identify carnivore cohorts
            _CarnivoreFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length];
            _OmnivoreFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length];
            _PlanktonFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length];

            // Loop over rows in the jagged arrays, initialise each vector within the jagged arrays, and calculate the current number of cohorts in
            // each functional group
            for (int i = 0; i < gridCellCohorts.Count; i++)
            {
                // Calculate the current number of cohorts in this functional group
                int NumCohortsThisFG = gridCellCohorts[i].Count;
                NumberCohortsPerFunctionalGroupNoNewCohorts[i] = NumCohortsThisFG;
                // Initialise the jagged arrays
                _AbundancesEaten[i] = new double[NumberCohortsPerFunctionalGroupNoNewCohorts[i]];
                _PotentialAbundanceEaten[i] = new double[NumberCohortsPerFunctionalGroupNoNewCohorts[i]];
            }

            // Loop over functional groups that are potential prey and determine which are carnivores
            foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
                _CarnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "carnivore";

            foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
                _OmnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "omnivore";

            foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
                _PlanktonFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Mobility", FunctionalGroup) == "planktonic";
        }
        /// <summary>
        /// Calculate the potential number of prey that could be gained through predation on each cohort in the grid cell
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The acting cohort</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The functional group definitions for stocks  in the model</param>
        public void GetEatingPotentialTerrestrial(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort,
            SortedList<string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions
            madingleyStockDefinitions)
        {
            BinnedPreyDensities = new double[gridCellCohorts.Count, NumberOfBins];

            // Set the total eaten by the acting cohort to zero
            _TotalBiomassEatenByCohort = 0.0;

            // Set the total number of units to handle all potential prey individuals eaten to zero
            _TimeUnitsToHandlePotentialFoodItems = 0.0;

            // Get the individual body mass of the acting (predator) cohort
            _BodyMassPredator = gridCellCohorts[actingCohort].IndividualBodyMass;

            // Get the abundance of the acting (predator) cohort
            _AbundancePredator = gridCellCohorts[actingCohort].CohortAbundance;

            // Pre-calculate individual values for this predator
            _SpecificPredatorKillRateConstant = _KillRateConstant * Math.Pow(_BodyMassPredator, (_KillRateConstantMassExponent));
            _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep = _DeltaT * _ProportionOfTimeEating;
            _PredatorAssimilationEfficiency = _AssimilationEfficiency;
            _PredatorNonAssimilation = (1 - _AssimilationEfficiency);

            // When body sizes are less than one gram, we have a flat handling time relationship to stop small things have extraordinarily short handling times
              //  if (_BodyMassPredator > 1.0)
              //  {
                _ReferenceMassRatioScalingTerrestrial = HandlingTimeScalarTerrestrial * Math.Pow(_ReferenceMass / _BodyMassPredator, _HandlingTimeExponentTerrestrial);
              //  }
              //  else
              //  {
              //      _ReferenceMassRatioScalingTerrestrial = HandlingTimeScalarTerrestrial * _ReferenceMass / _BodyMassPredator;

            //            }
                _PredatorAbundanceMultipliedByTimeEating = _AbundancePredator * _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep;

                _PredatorLogOptimalPreyBodySizeRatio = gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio;

            LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio = Math.Log(_BodyMassPredator) + _PredatorLogOptimalPreyBodySizeRatio;

            // Calculate the abundance of prey in each of the prey mass bins
            PopulateBinnedPreyAbundance(gridCellCohorts, actingCohort, FunctionalGroupIndicesToEat, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio);

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

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

                    // Get the bin number of this prey cohort
                    PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio);

                    // Check whether the prey cohort still exists in the model (i.e. body mass > 0)
                    if ((0 < PreyMassBinNumber) && (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0))
                    {
                        // Calculate the potential abundance from this cohort eaten by the acting cohort
                        _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledTerrestrial(
                            gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup,
                            _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup],
                            _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio);

                        // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts
                        _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] *
                            CalculateHandlingTimeTerrestrial(_BodyMassPrey);
                    }
                    else
                    {
                        // Assign a potential abundance eaten of zero
                        _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0;
                    }
                }
            }

            // No cannibalism; do this outside the loop to speed up the calculations
            _TimeUnitsToHandlePotentialFoodItems -= PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] *
                CalculateHandlingTimeTerrestrial(_BodyMassPredator);
            PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] = 0.0;
        }
        /// <summary>
        /// Calculate the potential number of prey that could be gained through predation on each cohort in the grid cell
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="actingCohort">The acting cohort</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The functional group definitions for stocks  in the model</param>
        public void GetEatingPotentialMarine(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, 
            SortedList<string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions 
            madingleyStockDefinitions)
        {
            BinnedPreyDensities = new double[gridCellCohorts.Count, NumberOfBins];

            // Set the total eaten by the acting cohort to zero
            _TotalBiomassEatenByCohort = 0.0;

            // Set the total number of units to handle all potential prey individuals eaten to zero
            _TimeUnitsToHandlePotentialFoodItems = 0.0;

            // Get the individual body mass of the acting (predator) cohort
            _BodyMassPredator = gridCellCohorts[actingCohort].IndividualBodyMass;

            // Get the abundance of the acting (predator) cohort
            _AbundancePredator = gridCellCohorts[actingCohort].CohortAbundance;

            // Pre-calculate individual values for this predator to speed things up
            _SpecificPredatorKillRateConstant = _KillRateConstant * Math.Pow(_BodyMassPredator, (_KillRateConstantMassExponent));
            _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep = _DeltaT * _ProportionOfTimeEating;
            _PredatorAssimilationEfficiency = _AssimilationEfficiency;
            _PredatorNonAssimilation = (1 - _AssimilationEfficiency);

            _DietIsAllSpecial = madingleyCohortDefinitions.GetTraitNames("Diet", actingCohort[0]) == "allspecial";

            _PredatorLogOptimalPreyBodySizeRatio = gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio;

            // If a filter feeder, then optimal body size is a value not a ratio: convert it to a ratio to ensure that all calculations work correctly
            if (_DietIsAllSpecial)
            {
                // Optimal body size is actually a value, not a ratio, so convert it to a ratio based on the present body size
                _PredatorLogOptimalPreyBodySizeRatio = Math.Log(
                    Math.Exp(gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio) / gridCellCohorts[actingCohort[0]][actingCohort[1]].IndividualBodyMass);
            }

            // Calculate the reference mass scaling ratio
            _ReferenceMassRatioScalingMarine = HandlingTimeScalarMarine * Math.Pow(_ReferenceMass / _BodyMassPredator, _HandlingTimeExponentMarine);

            _PredatorAbundanceMultipliedByTimeEating = _AbundancePredator * _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep;

            LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio = Math.Log(_BodyMassPredator) + _PredatorLogOptimalPreyBodySizeRatio;

            // Calculate the abundance of prey in each of the prey mass bins
            PopulateBinnedPreyAbundance(gridCellCohorts, actingCohort, FunctionalGroupIndicesToEat, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio);

            // Loop over potential prey functional groups
            foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
            {
                // Eating operates differently for planktivores
                // This can certainly be sped up
                if (_DietIsAllSpecial)
                {
                    // Loop over cohorts within the functional group
                    for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++)
                    {
                        // Get the body mass of individuals in this cohort
                        _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass;

                        // Get the bin number of this prey cohort
                        PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio);

                        // Check whether
                        // The prey cohort is within the feeding range of the predator
                        // the prey cohort still exists in the model (i.e. body mass > 0)
                        // Currently having whales etc eat everything, but preferentially feed on very small things (i.e. filter feeders)
                    if ((_PlanktonFunctionalGroups[FunctionalGroup]) && (0 < PreyMassBinNumber) &&
                        (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0))
                        {
                            // Calculate the potential abundance from this cohort eaten by the acting cohort
                            _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledMarine(
                                gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup,
                                _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup],
                                _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio);

                            // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts
                            _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] *
                                CalculateHandlingTimeMarine(_BodyMassPrey);
                        }
                        else
                        {
                            // Assign a potential abundance eaten of zero
                            _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0;
                        }
                    }
                }
                else
                {

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

                        // Get the bin number of this prey cohort
                        PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio);

                        // Check whether
                        // The prey cohort is within the feeding range of the predator
                        // the prey cohort still exists in the model (i.e. body mass > 0)
                        if ((0 < PreyMassBinNumber) && (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0))
                        {
                            // Calculate the potential abundance from this cohort eaten by the acting cohort
                            _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledMarine(
                               gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup,
                               _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup],
                               _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio);

                            // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts
                            _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] *
                                CalculateHandlingTimeMarine(_BodyMassPrey);
                        }
                        else
                        {
                            // Assign a potential abundance eaten of zero
                            _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0;
                        }
                    }
                }

            }

            // No cannibalism; do this outside the loop to speed up the calculations
            _TimeUnitsToHandlePotentialFoodItems -= PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] *
                CalculateHandlingTimeMarine(_BodyMassPredator);
            PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] = 0.0;
        }
        private void RunWithinCellDispersalOnly(uint latCellIndex, uint lonCellIndex, ThreadLockedParallelVariables partial,
                   GridCellCohortHandler workingGridCellCohorts, GridCellStockHandler workingGridCellStocks)
        {
            // Merge cohorts. Requires cohorts to be identical, for testing purposes (remember that they don't grow etc)
            // SHOULD ONLY BE RUN FOR RESPONSIVE DISPERSAL TESTING
            //partial.Combinations = Merger.MergeForResponsiveDispersalOnly(workingGridCellCohorts);

            // Loop over cohorts and remove any whose abundance is below the extinction threshold
            for (int kk = 0; kk < CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); kk++)
            {
                // Create a list to hold the cohorts to remove
                List<int> CohortIndicesToRemove = new List<int>();

                // Loop through each cohort in the functional group
                for (int ll = 0; ll < workingGridCellCohorts[kk].Count; ll++)
                {
                    // If cohort abundance is less than the extinction threshold then add to the list for extinction
                    if (workingGridCellCohorts[kk][ll].CohortAbundance <= _ExtinctionThreshold)
                    {
                        CohortIndicesToRemove.Add(ll);

                        partial.Extinctions += 1;
                    }
                }

                // Note that we don't keep track of the organic biomass pool if running dispersal only, since there are cohorts with strange biomasses
                for (int ll = (CohortIndicesToRemove.Count - 1); ll >= 0; ll--)
                {
                    // Remove the extinct cohort from the list of cohorts
                    workingGridCellCohorts[kk].RemoveAt(CohortIndicesToRemove[ll]);
                }

            }

            // Write out the updated cohort numbers after all ecological processes have occured
            EcosystemModelGrid.SetGridCellCohorts(workingGridCellCohorts, latCellIndex, lonCellIndex);
        }
        private void RunWithinCellCohortEcology(uint latCellIndex, uint lonCellIndex, ThreadLockedParallelVariables partial, 
            GridCellCohortHandler workingGridCellCohorts, GridCellStockHandler workingGridCellStocks,string outputDetail, int cellIndex, MadingleyModelInitialisation initialisation)
        {
            // Local instances of classes
            EcologyCohort MadingleyEcologyCohort = new EcologyCohort();
            Activity CohortActivity = new Activity();
            CohortMerge CohortMerger = new CohortMerge(DrawRandomly);

            // A list of the original cohorts inside a particular grid cell
            int[] OriginalGridCellCohortsNumbers;
            // A vector to hold the order in which cohorts will act
            uint[] RandomCohortOrder;
            // A jagged array to keep track of cohorts that are being worked on
            uint[][] CohortIndices;
            // The location of the acting cohort
            int[] ActingCohort = new int[2];
            // Temporary local variables
            int EcosystemModelParallelTempval1;
            int EcosystemModelParallelTempval2;
            // Boolean to pass into function to get cell environmental data to check if the specified variable exists
            bool VarExists;
            // variable to track cohort number
            uint TotalCohortNumber = 0;

            // Fill in the array with the number of cohorts per functional group before ecological processes are run
            OriginalGridCellCohortsNumbers = new int[workingGridCellCohorts.Count];

            for (int i = 0; i < workingGridCellCohorts.Count; i++)
            {
                OriginalGridCellCohortsNumbers[i] = workingGridCellCohorts[i].Count;
            }

            // Initialize ecology for stocks and cohorts
            MadingleyEcologyCohort.InitializeEcology(EcosystemModelGrid.GetCellEnvironment(latCellIndex, lonCellIndex)["Cell Area"][0],
                _GlobalModelTimeStepUnit, DrawRandomly);

            // Create a jagged array indexed by functional groups to hold cohort indices
            CohortIndices = new uint[CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups()][];

            // Loop over functional groups
            for (int ll = 0; ll < CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); ll++)
            {
                // Dimension the number of columns in each row of the jagged array to equal number of gridCellCohorts in each functional group
                if (workingGridCellCohorts[ll] == null)
                {
                    CohortIndices[ll] = new uint[0];
                }
                else
                {
                    CohortIndices[ll] = new uint[workingGridCellCohorts[ll].Count()];
                }
                // Loop over gridCellCohorts in the functional group
                for (int kk = 0; kk < CohortIndices[ll].Count(); kk++)
                {
                    // Fill jagged array with indices for each cohort
                    CohortIndices[ll][kk] = TotalCohortNumber;
                    TotalCohortNumber += 1;
                }

            }

            if (DrawRandomly)
            {
                // Randomly order the cohort indices
                RandomCohortOrder = Utilities.RandomlyOrderedIndices(TotalCohortNumber);
            }
            else
            {
                RandomCohortOrder = Utilities.NonRandomlyOrderedCohorts(TotalCohortNumber, CurrentTimeStep);
            }

            // Diagnostic biological variables don't need to be reset every cohort, but rather every grid cell
            EcosystemModelParallelTempval2 = 0;

            // Initialise eating formulations
            MadingleyEcologyCohort.EatingFormulations["Basic eating"].InitializeEcologicalProcess(workingGridCellCohorts, workingGridCellStocks,
                CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, "revised predation");
            MadingleyEcologyCohort.EatingFormulations["Basic eating"].InitializeEcologicalProcess(workingGridCellCohorts, workingGridCellStocks
                , CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, "revised herbivory");

            // Loop over randomly ordered gridCellCohorts to implement biological functions
            for (int ll = 0; ll < RandomCohortOrder.Length; ll++)
            {

                // Locate the randomly chosen cohort within the array of lists of gridCellCohorts in the grid cell
                ActingCohort = Utilities.FindJaggedArrayIndex(RandomCohortOrder[ll], CohortIndices, TotalCohortNumber);

                // Perform all biological functions except dispersal (which is cross grid cell)
                if (workingGridCellCohorts[ActingCohort].CohortAbundance.CompareTo(_ExtinctionThreshold) > 0)
                {
                    // Calculate number of cohorts in this functional group in this grid cell before running ecology
                    EcosystemModelParallelTempval1 = workingGridCellCohorts[ActingCohort[0]].Count;

                    CohortActivity.AssignProportionTimeActive(workingGridCellCohorts[ActingCohort], EcosystemModelGrid.GetCellEnvironment(latCellIndex, lonCellIndex), CohortFunctionalGroupDefinitions, CurrentTimeStep, CurrentMonth);

                    // Run ecology
                    MadingleyEcologyCohort.RunWithinCellEcology(workingGridCellCohorts, workingGridCellStocks,
                        ActingCohort, EcosystemModelGrid.GetCellEnvironment(latCellIndex, lonCellIndex),
                        EcosystemModelGrid.GetCellDeltas(latCellIndex, lonCellIndex),
                        CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, CurrentTimeStep,
                        ProcessTrackers[cellIndex], ref partial, SpecificLocations,outputDetail, CurrentMonth, initialisation);

                    // Update the properties of the acting cohort
                    MadingleyEcologyCohort.UpdateEcology(workingGridCellCohorts, workingGridCellStocks, ActingCohort,
                        EcosystemModelGrid.GetCellEnvironment(latCellIndex, lonCellIndex), EcosystemModelGrid.GetCellDeltas(
                        latCellIndex, lonCellIndex), CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, CurrentTimeStep,
                        ProcessTrackers[cellIndex]);

                    // Add newly produced cohorts to the tracking variable
                    EcosystemModelParallelTempval2 += workingGridCellCohorts[ActingCohort[0]].Count - EcosystemModelParallelTempval1;

                    // Check that the mass of individuals in this cohort is still >= 0 after running ecology
                    Debug.Assert(workingGridCellCohorts[ActingCohort].IndividualBodyMass >= 0.0, "Biomass < 0 for this cohort");
                }

                // Check that the mass of individuals in this cohort is still >= 0 after running ecology
                Debug.Assert(workingGridCellCohorts[ActingCohort].IndividualBodyMass >= 0.0, "Biomass < 0 for this cohort");
            }

            // Update diagnostics of productions
            partial.Productions += EcosystemModelParallelTempval2;

            RunExtinction(latCellIndex, lonCellIndex, partial, workingGridCellCohorts, cellIndex);

            // Merge cohorts, if necessary
            if (workingGridCellCohorts.GetNumberOfCohorts() > initialisation.MaxNumberOfCohorts)
            {
                partial.Combinations = CohortMerger.MergeToReachThresholdFast(workingGridCellCohorts, workingGridCellCohorts.GetNumberOfCohorts(), initialisation.MaxNumberOfCohorts);

                //Run extinction a second time to remove those cohorts that have been set to zero abundance when merging
                RunExtinction(latCellIndex, lonCellIndex, partial, workingGridCellCohorts, cellIndex);
            }
            else
                partial.Combinations = 0;

            // Write out the updated cohort numbers after all ecological processes have occured
            EcosystemModelGrid.SetGridCellCohorts(workingGridCellCohorts, latCellIndex, lonCellIndex);
        }
        /// <summary>
        /// Carries out extinction on cohorts that have an abundance below a defined extinction threshold
        /// </summary>
        private void RunExtinction(uint latCellIndex, uint lonCellIndex, ThreadLockedParallelVariables partial,
            GridCellCohortHandler workingGridCellCohorts, int cellIndex)
        {
            bool VarExists;

            // Loop over cohorts and remove any whose abundance is below the extinction threshold
            for (int kk = 0; kk < CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); kk++)
            {
                // Create a list to hold the cohorts to remove
                List<int> CohortIndicesToRemove = new List<int>();

                // Loop through each cohort in the functional group
                for (int ll = 0; ll < workingGridCellCohorts[kk].Count; ll++)
                {
                    // If cohort abundance is less than the extinction threshold then add to the list for extinction
                    if (workingGridCellCohorts[kk][ll].CohortAbundance.CompareTo(_ExtinctionThreshold) <= 0 || workingGridCellCohorts[kk][ll].IndividualBodyMass.CompareTo(0.0) == 0)
                    {
                        CohortIndicesToRemove.Add(ll);

                        partial.Extinctions += 1;

                        // If track processes is set and output detail is set to high and the cohort being made extinct has never been merged,
                        // then output its mortality profile
                        if (ProcessTrackers[cellIndex].TrackProcesses && (InitialisationFileStrings["OutputDetail"] == "high") && (workingGridCellCohorts[kk][ll].CohortID.Count == 1))
                        {
                            ProcessTrackers[cellIndex].OutputMortalityProfile(workingGridCellCohorts[kk][ll].CohortID[0]);
                        }
                    }
                }

                // Code to add the biomass to the biomass pool and dispose of the cohort
                for (int ll = (CohortIndicesToRemove.Count - 1); ll >= 0; ll--)
                {
                    // Add biomass of the extinct cohort to the organic matter pool
                    EcosystemModelGrid.SetEnviroLayer("Organic Pool", 0, EcosystemModelGrid.GetEnviroLayer("Organic Pool", 0, latCellIndex, lonCellIndex, out VarExists) +
                        (workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].IndividualBodyMass + workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].IndividualReproductivePotentialMass) * workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].CohortAbundance, latCellIndex, lonCellIndex);
                    Debug.Assert(EcosystemModelGrid.GetEnviroLayer("Organic Pool", 0, latCellIndex, lonCellIndex, out VarExists) > 0, "Organic pool < 0");

                    if (ProcessTrackers[cellIndex].TrackProcesses && SpecificLocations == true)
                        ProcessTrackers[cellIndex].RecordExtinction(latCellIndex, lonCellIndex, CurrentTimeStep, workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].Merged, workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].CohortID);

                    // Remove the extinct cohort from the list of cohorts
                    workingGridCellCohorts[kk].RemoveAt(CohortIndicesToRemove[ll]);

                }

            }
        }
        /// <summary>
        /// Calculates the rate of starvation mortality given current body mass and the maximum body mass ever achieved. Note that metabolic costs are already included in the deltas passed in
        /// the body mass including change this time step, so no change in body mass should mean no starvation (as metabolic costs have already been met)
        /// </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="bodyMassIncludingChangeThisTimeStep">Body mass including change from other ecological functions this time step; should not exceed adult mass</param>
        /// <returns>The starvation mortality rate in mortality formulation time step units</returns>
        private double CalculateStarvationRate(GridCellCohortHandler gridCellCohorts, int[] actingCohort, double bodyMassIncludingChangeThisTimeStep, Dictionary<string, Dictionary<string, double>> deltas)
        {
            if (bodyMassIncludingChangeThisTimeStep < gridCellCohorts[actingCohort].MaximumAchievedBodyMass)
            {
                // Calculate the first part of the relationship between body mass and mortality rate
                double k = -(bodyMassIncludingChangeThisTimeStep - _LogisticInflectionPoint * gridCellCohorts[actingCohort].
                    MaximumAchievedBodyMass) / (_LogisticScalingParameter * gridCellCohorts[actingCohort].MaximumAchievedBodyMass);

                // Calculate mortality rate
                return _MaximumStarvationRate / (1 + Math.Exp(-k));
            }
            else
                return 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;
            }
        }
Example #24
0
        /// <summary>
        /// Remove individuals lost from cohorts through direct harvesting of animals
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="harvestingScenario">The scenario of direct harvesting of animals to apply</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 the harvesting scenario</param>
        /// <param name="impactSteps">The number of time steps to apply the scenario for</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <param name="impactCell">The index of the cell, within the list of all cells to run, to apply the scenario for</param>
        public void RemoveHarvestedIndividuals(GridCellCohortHandler gridCellCohorts,
            Madingley.Common.ScenarioParameter harvestingScenario, uint currentTimestep, uint burninSteps, uint impactSteps, uint totalSteps,
            SortedList<string, double[]> cellEnvironment, Boolean impactCell, string globalModelTimestepUnits, FunctionalGroupDefinitions cohortFGs)
        {
            if (impactCell)
            {

                //If this is marine cell
                if (cellEnvironment["Realm"][0] == 2.0)
                {
                    if (harvestingScenario.ParamString == "no")
                    {
                        // Do not apply any harvesting
                    }
                    else if (harvestingScenario.ParamString == "constant")
                    {
                        double TargetBiomass;
                        if (FisheriesCatch != null)
                        {
                            TargetBiomass = (1000 *
                            FisheriesCatch.ModelGridCatchTotal[Convert.ToInt32(cellEnvironment["LatIndex"][0]), Convert.ToInt32(cellEnvironment["LonIndex"][0])])
                            / 12.0;
                        }
                        else
                        {
                            TargetBiomass = harvestingScenario.ParamDouble1;
                        }
                        // If the burn-in period has been completed, then apply
                        // the harvesting scenario
                        if (currentTimestep > burninSteps)
                        {
                            ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep);
                        }
                    }
                    else if (harvestingScenario.ParamString == "fish-catch")
                    {
                        //Initialise an instance of ApplyFishingCatches for this cell
                        if (currentTimestep == burninSteps)
                            ApplyCatches[Convert.ToInt32(cellEnvironment["LatIndex"][0]),
                            Convert.ToInt32(cellEnvironment["LonIndex"][0])] = new ApplyFishingCatches(FisheriesCatch);

                        if (currentTimestep > burninSteps)
                        {
                            //Bin the cohorts of the current cell
                            ApplyCatches[Convert.ToInt32(cellEnvironment["LatIndex"][0]),
                            Convert.ToInt32(cellEnvironment["LonIndex"][0])].BinCohorts(gridCellCohorts, FisheriesCatch, cohortFGs);
                            //Now remove the catch
                            ApplyCatches[Convert.ToInt32(cellEnvironment["LatIndex"][0]),
                            Convert.ToInt32(cellEnvironment["LonIndex"][0])].ApplyCatches(gridCellCohorts, FisheriesCatch,
                                Convert.ToInt32(cellEnvironment["LatIndex"][0]),
                                Convert.ToInt32(cellEnvironment["LonIndex"][0]));
                        }
                    }

                }
                else
                {

                    if (harvestingScenario.ParamString == "no")
                    {
                        // Do not apply any harvesting
                    }
                    else if (harvestingScenario.ParamString == "constant")
                    {
                        // If the burn-in period has been completed, then apply
                        // the harvesting scenario
                        if (currentTimestep > burninSteps)
                        {
                            ApplyHarvesting(gridCellCohorts, harvestingScenario.ParamDouble1, cellEnvironment, currentTimestep);
                        }
                    }
                    else if (harvestingScenario.ParamString == "temporary")
                    {
                        // If the burn-in period has been completed and the period of impact has not elapsed,
                        // then apply the harvesting scenario
                        if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps)))
                        {
                            ApplyHarvesting(gridCellCohorts, harvestingScenario.ParamDouble1, cellEnvironment, currentTimestep);
                        }
                    }
                    else if (harvestingScenario.ParamString == "escalating")
                    {
                        // If the spin-up period has been completed, then apply a level of harvesting
                        // according to the number of time-steps that have elapsed since the spin-up ended
                        if (currentTimestep > burninSteps)
                        {
                            // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up
                            double TargetBiomass = (Math.Min(50000, (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.ParamDouble1)));

                            // Apply the harvesting scenario using the calculated target biomass
                            ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep);
                        }

                    }
                    else if (harvestingScenario.ParamString == "temp-escalating-declining")
                    {
                        // If the spin-up period has been completed, then apply a level of harvesting
                        // according to the number of time-steps that have elapsed since the spin-up ended
                        if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps)))
                        {
                            // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up
                            double TargetBiomass = (Math.Min(50000, (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.ParamDouble1)));

                            // Apply the harvesting scenario using the calculated target biomass
                            ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep);
                        }
                        else if (currentTimestep > (burninSteps + impactSteps))
                        {
                            // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up
                            double TargetBiomass = (Math.Min(50000, ((-(totalSteps - currentTimestep) / 12.0) * harvestingScenario.ParamDouble1)));

                            // Apply the harvesting scenario using the calculated target biomass
                            ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep);
                        }

                    }
                    else if (harvestingScenario.ParamString == "temp-escalating")
                    {
                        // If the spin-up period has been completed and the period of impact has not elapsed,
                        // then remove a proportion of plant matter
                        // according to the number of time-steps that have elapsed since the spin-up ended
                        if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps)))
                        {
                            // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up
                            double TargetBiomass = (Math.Min(50000, (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.ParamDouble1)));

                            // Apply the harvesting scenarion using the calculated target biomass
                            ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep);
                        }
                    }
                    else if (harvestingScenario.ParamString == "temp-escalating-const-rate")
                    {
                        // If the spin-up period has been completed and the period of impact (specified by the third scenario element
                        // has not elapsed,
                        // then remove a portion of  plant matter
                        // according to the number of time-steps that have elapsed since the spin-up ended

                        int ConstImpactSteps = Convert.ToInt32(harvestingScenario.ParamDouble2 * Utilities.ConvertTimeUnits("year", globalModelTimestepUnits));

                        if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + ConstImpactSteps)))
                        {
                            // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up
                            double TargetBiomass = (Math.Min(200000, (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.ParamDouble1)));

                            // Apply the harvesting scenarion using the calculated target biomass
                            ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep);
                        }
                    }
                    else if (harvestingScenario.ParamString == "temp-escalating-const-rate-duration")
                    {
                        // If the spin-up period has been completed and the period of impact (specified by the third scenario element
                        // has not elapsed,
                        // then remove a proportion of plant matter
                        // according to the number of time-steps that have elapsed since the spin-up ended

                        int ConstImpactSteps = Convert.ToInt32(harvestingScenario.ParamDouble2 * Utilities.ConvertTimeUnits("year", globalModelTimestepUnits));

                        if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps)))
                        {
                            //gridCellStocks[actingStock].TotalBiomass -= gridCellStocks[actingStock].TotalBiomass *
                            //    (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2)));

                            double TargetBiomass = (Math.Min(200000,
                                            Math.Min(((ConstImpactSteps / 12.0) * harvestingScenario.ParamDouble1),
                                            (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.ParamDouble1))));

                            // Apply the harvesting scenarion using the calculated target biomass
                            ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep);
                        }
                    }
                    else
                    {
                        Debug.Fail("There is no method for the harvesting scenario specified");
                    }

                }
            }
        }
        /// <summary>
        /// Constructor for a grid cell; creates cell and reads in environmental data
        /// </summary>
        /// <param name="latitude">The latitude of the grid cell</param>
        /// <param name="latIndex">The latitudinal index of the grid cell</param>
        /// <param name="longitude">The longitude of the grid cell</param>
        /// <param name="lonIndex">The longitudinal index of the grid cell</param>
        /// <param name="latCellSize">The latitudinal dimension of the grid cell</param>
        /// <param name="lonCellSize">The longitudinal dimension of the grid cell</param>
        /// <param name="dataLayers">A list of environmental data variables in the model</param>
        /// <param name="missingValue">The missing value to be applied to all data in the grid cell</param>
        /// <param name="cohortFunctionalGroups">The definitions for cohort functional groups in the model</param>
        /// <param name="stockFunctionalGroups">The definitions for stock functional groups in the model</param>
        /// <param name="globalDiagnostics">A list of global diagnostic variables for the model grid</param>
        /// <param name="tracking">Whether process-tracking is enabled</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        public GridCell(float latitude, uint latIndex, float longitude, uint lonIndex, float latCellSize, float lonCellSize, 
            SortedList<string, EnviroData> dataLayers, double missingValue, FunctionalGroupDefinitions cohortFunctionalGroups, 
            FunctionalGroupDefinitions stockFunctionalGroups, SortedList<string, double> globalDiagnostics,Boolean tracking,
            bool specificLocations)
        {
            // Boolean to track when environmental data are missing
            Boolean EnviroMissingValue;

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

            // Temporary vector for holding initial values of grid cell properties
            double[] tempVector;

            // Initialise deltas sorted list
            _Deltas = new Dictionary<string, Dictionary<string, double>>();

            // Initialize delta abundance sorted list with appropriate processes
            Dictionary<string, double> DeltaAbundance = new Dictionary<string, double>();
            DeltaAbundance.Add("mortality", 0.0);

            // Add delta abundance sorted list to deltas sorted list
            _Deltas.Add("abundance", DeltaAbundance);

            // Initialize delta biomass sorted list with appropriate processes
            Dictionary<string, double> DeltaBiomass = new Dictionary<string, double>();
            DeltaBiomass.Add("metabolism", 0.0);
            DeltaBiomass.Add("predation", 0.0);
            DeltaBiomass.Add("herbivory", 0.0);
            DeltaBiomass.Add("reproduction", 0.0);

            // Add delta biomass sorted list to deltas sorted list
            _Deltas.Add("biomass", DeltaBiomass);

            // Initialize delta reproductive biomass vector with appropriate processes
            Dictionary<string, double> DeltaReproductiveBiomass = new Dictionary<string, double>();
            DeltaReproductiveBiomass.Add("reproduction", 0.0);

            // Add delta reproduction sorted list to deltas sorted list
            _Deltas.Add("reproductivebiomass", DeltaReproductiveBiomass);

            // Initialize organic pool delta vector with appropriate processes
            Dictionary<string, double> DeltaOrganicPool = new Dictionary<string, double>();
            DeltaOrganicPool.Add("herbivory", 0.0);
            DeltaOrganicPool.Add("predation", 0.0);
            DeltaOrganicPool.Add("mortality", 0.0);

            // Add delta organic pool sorted list to deltas sorted list
            _Deltas.Add("organicpool", DeltaOrganicPool);

            // Initialize respiratory CO2 pool delta vector with appropriate processes
            Dictionary<string, double> DeltaRespiratoryCO2Pool = new Dictionary<string, double>();
            DeltaRespiratoryCO2Pool.Add("metabolism", 0.0);

            // Add delta respiratory CO2 pool to deltas sorted list
            _Deltas.Add("respiratoryCO2pool", DeltaRespiratoryCO2Pool);

            // Set the grid cell values of latitude, longitude and missing value as specified
            _Latitude = latitude;
            _Longitude = longitude;

            // Initialise list of environmental data layer values
            _CellEnvironment = new SortedList<string, double[]>();

            //Add the latitude and longitude
            tempVector = new double[1];
            tempVector[0] = latitude;
            _CellEnvironment.Add("Latitude", tempVector);
            tempVector = new double[1];
            tempVector[0] = longitude;
            _CellEnvironment.Add("Longitude", tempVector);

            // Add an organic matter pool to the cell environment to track organic biomass not held by animals or plants with an initial value of 0
            tempVector = new double[1];
            tempVector[0] = 0.0;
            _CellEnvironment.Add("Organic Pool", tempVector);

            // Add a repsiratory CO2 pool to the cell environment with an initial value of 0
            tempVector = new double[1];
            tempVector[0] = 0.0;
            _CellEnvironment.Add("Respiratory CO2 Pool", tempVector);

            // Add the grid cell area (in km2) to the cell environment with an initial value of 0
            tempVector = new double[1];
            // Calculate the area of this grid cell
            tempVector[0] = Utilities.CalculateGridCellArea(latitude, lonCellSize, latCellSize);
            // Add it to the cell environment
            _CellEnvironment.Add("Cell Area", tempVector);

            //Add the latitude and longitude indices
            tempVector = new double[1];
            tempVector[0] = latIndex;
            _CellEnvironment.Add("LatIndex", tempVector);
            tempVector = new double[1];
            tempVector[0] = lonIndex;
            _CellEnvironment.Add("LonIndex", tempVector);

            // Add the missing value of data in the grid cell to the cell environment
            tempVector = new double[1];
            tempVector[0] = missingValue;
            _CellEnvironment.Add("Missing Value", tempVector);

            // Loop through environmental data layers and extract values for this grid cell
            // Also standardise missing values

            // Loop over variables in the list of environmental data
            foreach (string LayerName in dataLayers.Keys)
            {
                // Initiliase the temporary vector of values to be equal to the number of time intervals in the environmental variable
                tempVector = new double[dataLayers[LayerName].NumTimes];
                // Loop over the time intervals in the environmental variable
                for (int hh = 0; hh < dataLayers[LayerName].NumTimes; hh++)
                {
                    // Add the value of the environmental variable at this time interval to the temporary vector
                    tempVector[hh] = dataLayers[LayerName].GetValue(_Latitude, _Longitude, (uint)hh, out EnviroMissingValue,latCellSize,lonCellSize);
                    // If the environmental variable is a missing value, then change the value to equal the standard missing value for this cell
                    if (EnviroMissingValue)
                        tempVector[hh] = missingValue;
                }
                // Add the values of the environmental variables to the cell environment, with the name of the variable as the key
                _CellEnvironment.Add(LayerName, tempVector);
            }

            if (_CellEnvironment.ContainsKey("LandSeaMask"))
            {
                if (_CellEnvironment["LandSeaMask"][0].CompareTo(0.0) == 0)
                {
                    if (ContainsData(_CellEnvironment["OceanTemp"], _CellEnvironment["Missing Value"][0]))
                    {
                        //This is a marine cell
                        tempVector = new double[1];
                        tempVector[0] = 2.0;
                        _CellEnvironment.Add("Realm", tempVector);

                        _CellEnvironment.Add("NPP", _CellEnvironment["OceanNPP"]);
                        _CellEnvironment.Add("DiurnalTemperatureRange", _CellEnvironment["OceanDTR"]);
                        if (_CellEnvironment.ContainsKey("Temperature"))
                        {
                            if(_CellEnvironment.ContainsKey("SST"))
                            {
                                _CellEnvironment["Temperature"] = _CellEnvironment["SST"];
                            }
                            else
                            {
                            }
                        }
                        else
                        {
                            _CellEnvironment.Add("Temperature", _CellEnvironment["SST"]);
                        }

                    }
                    else
                    {
                        //This is a freshwater cell and in this model formulation is characterised as belonging to the terrestrial realm
                        tempVector = new double[1];
                        tempVector[0] = 2.0;
                        _CellEnvironment.Add("Realm", tempVector);

                        _CellEnvironment.Add("NPP", _CellEnvironment["LandNPP"]);
                        _CellEnvironment.Add("DiurnalTemperatureRange", _CellEnvironment["LandDTR"]);
                    }
                }
                else
                {
                    //This is a land cell
                    tempVector = new double[1];
                    tempVector[0] = 1.0;
                    _CellEnvironment.Add("Realm", tempVector);

                    _CellEnvironment.Add("NPP", _CellEnvironment["LandNPP"]);
                    _CellEnvironment.Add("DiurnalTemperatureRange", _CellEnvironment["LandDTR"]);

                }
            }
            else
            {
                Debug.Fail("No land sea mask defined - a mask is required to initialise appropriate ecology");
            }

            //Calculate and add the standard deviation of monthly temperature as a measure of seasonality
            //Also calculate and add the annual mean temperature for this cell
            tempVector = new double[12];
            double[] sdtemp = new double[12];
            double[] meantemp = new double[12];

            tempVector = _CellEnvironment["Temperature"];

            double Average = tempVector.Average();
            meantemp[0] = Average;
            double SumOfSquaresDifferences = tempVector.Select(val => (val - Average) * (val - Average)).Sum();
            sdtemp[0] = Math.Sqrt(SumOfSquaresDifferences / tempVector.Length);

            _CellEnvironment.Add("SDTemperature", sdtemp);
            _CellEnvironment.Add("AnnualTemperature", meantemp);

            //Remove unrequired cell environment layers
            if (_CellEnvironment.ContainsKey("LandNPP")) _CellEnvironment.Remove("LandNPP");
            if (_CellEnvironment.ContainsKey("LandDTR")) _CellEnvironment.Remove("LandDTR");
            if (_CellEnvironment.ContainsKey("OceanNPP")) _CellEnvironment.Remove("OceanNPP");
            if (_CellEnvironment.ContainsKey("OceanDTR")) _CellEnvironment.Remove("OceanDTR");
            if (_CellEnvironment.ContainsKey("SST")) _CellEnvironment.Remove("SST");

            // CREATE NPP SEASONALITY LAYER
            _CellEnvironment.Add("Seasonality", CalculateNPPSeasonality(_CellEnvironment["NPP"], _CellEnvironment["Missing Value"][0]));

            // Calculate other climate variables from temperature and precipitation
            // Declare an instance of the climate variables calculator
            ClimateVariablesCalculator CVC = new ClimateVariablesCalculator();

            // Calculate the fraction of the year that experiences frost
            double[] NDF = new double[1];
            NDF[0] = CVC.GetNDF(_CellEnvironment["FrostDays"], _CellEnvironment["Temperature"],_CellEnvironment["Missing Value"][0]);
            _CellEnvironment.Add("Fraction Year Frost", NDF);

            double[] frostMonthly = new double[12];
            frostMonthly[0] = Math.Min(_CellEnvironment["FrostDays"][0] / 31.0, 1.0);
            frostMonthly[1] = Math.Min(_CellEnvironment["FrostDays"][1] / 28.0, 1.0);
            frostMonthly[2] = Math.Min(_CellEnvironment["FrostDays"][2] / 31.0, 1.0);
            frostMonthly[3] = Math.Min(_CellEnvironment["FrostDays"][3] / 30.0, 1.0);
            frostMonthly[4] = Math.Min(_CellEnvironment["FrostDays"][4] / 31.0, 1.0);
            frostMonthly[5] = Math.Min(_CellEnvironment["FrostDays"][5] / 30.0, 1.0);
            frostMonthly[6] = Math.Min(_CellEnvironment["FrostDays"][6] / 31.0, 1.0);
            frostMonthly[7] = Math.Min(_CellEnvironment["FrostDays"][7] / 31.0, 1.0);
            frostMonthly[8] = Math.Min(_CellEnvironment["FrostDays"][8] / 30.0, 1.0);
            frostMonthly[9] = Math.Min(_CellEnvironment["FrostDays"][9] / 31.0, 1.0);
            frostMonthly[10] = Math.Min(_CellEnvironment["FrostDays"][10] / 30.0, 1.0);
            frostMonthly[11] = Math.Min(_CellEnvironment["FrostDays"][11] / 31.0, 1.0);

            _CellEnvironment.Add("Fraction Month Frost", frostMonthly);
            _CellEnvironment.Remove("FrostDays");

            // Calculate AET and the fractional length of the fire season
            Tuple<double[], double, double> TempTuple = new Tuple<double[], double, double>(new double[12], new double(), new double());
            TempTuple = CVC.MonthlyActualEvapotranspirationSoilMoisture(_CellEnvironment["AWC"][0], _CellEnvironment["Precipitation"], _CellEnvironment["Temperature"]);
            _CellEnvironment.Add("AET", TempTuple.Item1);
            _CellEnvironment.Add("Fraction Year Fire", new double[1] { TempTuple.Item3 / 360 });

            // Designate a breeding season for this grid cell, where a month is considered to be part of the breeding season if its NPP is at
            // least 80% of the maximum NPP throughout the whole year
            double[] BreedingSeason = new double[12];
            for (int i = 0; i < 12; i++)
            {
                if ((_CellEnvironment["Seasonality"][i] / _CellEnvironment["Seasonality"].Max()) > 0.5)
                {
                    BreedingSeason[i] = 1.0;
                }
                else
                {
                    BreedingSeason[i] = 0.0;
                }
            }
            _CellEnvironment.Add("Breeding Season", BreedingSeason);

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

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

            }

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

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

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

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

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

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

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

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

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

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

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

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

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

            // Note that maturity time step is set in TReproductionBasic
        }
        /// <summary>
        /// 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>
        /// 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>
        /// Create the matrix of prey abundances in each weight bin
        /// </summary>
        /// <param name="gridCellCohorts">Cohorts in this grid cell</param>
        /// <param name="actingCohort">The predator cohort</param>
        /// <param name="functionalGroupIndicesToEat">The functional groups which this predator eats</param>
        /// <param name="logOptimalPreyBodySizeRatio">The (log-transformed) optimal ratio of prey to predator body mass</param>
        private void PopulateBinnedPreyAbundance(GridCellCohortHandler gridCellCohorts, int[] actingCohort, int[] functionalGroupIndicesToEat,
            double logPredatorMassPlusLogPredatorOptimalBodySizeRatio)
        {
            int BinNumber = 0;

            // Loop through prey functional groups
            foreach (var fg in functionalGroupIndicesToEat)
            {
                foreach (var cohort in gridCellCohorts[fg])
                {
                    // Calculate the difference between the actual body size ratio and the optimal ratio,
                    // and then divide by the standard deviation in log ratio space to determine in
                    // which bin to assign the prey item.
                    BinNumber = GetBinNumber(cohort.IndividualBodyMass, logPredatorMassPlusLogPredatorOptimalBodySizeRatio);

                    if((0 < BinNumber) && (BinNumber < NumberOfBins))
                    {
                        BinnedPreyDensities[fg, BinNumber] += cohort.CohortAbundance / _CellAreaHectares;
                    }
                }
            }
        }
        /// <summary>
        /// Assign the juvenile and adult masses of the new cohort to produce
        /// </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="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param>
        /// <returns>A vector containing the juvenile and adult masses of the cohort to be produced</returns>
        private double[] GetOffspringCohortProperties(GridCellCohortHandler gridCellCohorts, int[] actingCohort, FunctionalGroupDefinitions madingleyCohortDefinitions)
        {
            // A two-element vector holding adult and juvenile body masses in elements zero and one respectively
             double[] _CohortJuvenileAdultMasses = new double[2];

            // Determine whether offspring cohort 'evolves' in terms of adult and juvenile body masses
            if (RandomNumberGenerator.GetUniform() > _MassEvolutionProbabilityThreshold)
            {
                // Determine the new juvenile body mass
                _CohortJuvenileAdultMasses[0] = Math.Max(RandomNumberGenerator.GetNormal(gridCellCohorts[actingCohort].JuvenileMass, _MassEvolutionStandardDeviation * gridCellCohorts[actingCohort].JuvenileMass),
                    madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("Minimum mass",actingCohort[0]));

                // Determine the new adult body mass
                _CohortJuvenileAdultMasses[1] = Math.Min(RandomNumberGenerator.GetNormal(gridCellCohorts[actingCohort].AdultMass, _MassEvolutionStandardDeviation * gridCellCohorts[actingCohort].AdultMass),
                    madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("Maximum mass", actingCohort[0]));
            }
            // If not, it just gets the same values as the parent cohort
            else
            {
                // Assign masses to the offspring cohort that are equal to those of the parent cohort
                _CohortJuvenileAdultMasses[0] = gridCellCohorts[actingCohort].JuvenileMass;
                _CohortJuvenileAdultMasses[1] = gridCellCohorts[actingCohort].AdultMass;
            }

            // Return the vector of adult and juvenile masses
            return _CohortJuvenileAdultMasses;
        }