//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;
                    }
                }
            }
        }
        //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>
        /// Initialises herbivory implementation each time step
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <remarks>This only works if: a) herbivory is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips
        /// It is critical to run this every time step</remarks>
        public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, SortedList <string, double[]>
                                                cellEnvironment)
        {
            // Get the functional group indices of all autotroph stocks
            _FunctionalGroupIndicesToEat = madingleyStockDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "Autotroph", false);

            string[] realm = { "Terrestrial", "Marine" };

            int    realm_index   = (int)cellEnvironment["Realm"][0] - 1;
            string Current_realm = realm[realm_index];
        }
Esempio n. 4
0
        /// <summary>
        /// Initialises predation implementation each time step
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <remarks>This only works if: a) predation is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips
        /// It is critical to run this every time step</remarks>
        public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, SortedList <string, double[]>
                                                cellEnvironment)
        {
            // Get the functional group indices of all heterotroph cohorts (i.e. potential prey)

            // _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "heterotroph", false);

            string [] realm = { "Terrestrial", "Marine" };

            int    realm_index   = (int)cellEnvironment["Realm"][0] - 1;
            string Current_realm = realm[realm_index];

            _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("realm", Current_realm, false);


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

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

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

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

            // Loop over functional groups that are potential prey and determine which are carnivores THIS IS COMMENTED OUT AS IT'S NOT USED
            //foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
            //    _CarnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "carnivore";

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

            //foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
            //    _PlanktonFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Mobility", FunctionalGroup) == "planktonic";
        }
Esempio n. 5
0
        /// <summary>
        /// Set up the necessary architecture for generating outputs arranged by trait value
        /// </summary>
        /// <param name="cohortFunctionalGroupDefinitions">Functional group definitions for cohorts in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">Functional group definitions for stocks in the model</param>
        private void InitialiseTraitBasedOutputs(FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, FunctionalGroupDefinitions
                                                 stockFunctionalGroupDefinitions)
        {
            // Define the cohort traits that will be used to separate outputs
            CohortTraits = new string[3] {
                "Nutrition source", "Endo/Ectotherm", "Reproductive strategy"
            };

            // Declare a sorted dictionary to hold all unique trait values
            CohortTraitValues = new SortedDictionary <string, string[]>();

            // Add all unique trait values to the sorted dictionary
            foreach (string Trait in CohortTraits)
            {
                CohortTraitValues.Add(Trait, cohortFunctionalGroupDefinitions.GetUniqueTraitValues(Trait));
            }

            // Get the list of functional group indices corresponding to each unique trait value
            foreach (string Trait in CohortTraits)
            {
                foreach (string TraitValue in CohortTraitValues[Trait])
                {
                    CohortTraitIndices.Add(TraitValue, cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false));
                }
            }

            // Define the stock traits that will be used to separate outputs
            StockTraits = new string[2] {
                "Heterotroph/Autotroph", "Leaf strategy"
            };

            // Re-initialise the sorted dictionary to hold all unique trait values
            StockTraitValues = new SortedDictionary <string, string[]>();

            // Add all unique stock trait values to the sorted dictionary
            foreach (string Trait in StockTraits)
            {
                StockTraitValues.Add(Trait, stockFunctionalGroupDefinitions.GetUniqueTraitValues(Trait));
            }

            // Get the list of functional group indices corresponding to each unique trait value
            foreach (string Trait in StockTraits)
            {
                foreach (string TraitValue in StockTraitValues[Trait])
                {
                    StockTraitIndices.Add(TraitValue, stockFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false));
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Initialises predation implementation each time step
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param>
        /// <remarks>This only works if: a) predation is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips
        /// It is critical to run this every time step</remarks>
        public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions)
        {
            // Get the functional group indices of all heterotroph cohorts (i.e. potential prey)
            _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "heterotroph", false);

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

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

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

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

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

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

            foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
            {
                _PlanktonFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Mobility", FunctionalGroup) == "planktonic";
            }
        }
        /// <summary>
        /// Seed the stocks and cohorts for all active cells in the model grid
        /// </summary>
        /// <param name="cellIndices">A list of the active cells in the model grid</param>
        /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">The functional group definitions for stocks in the model</param>
        /// <param name="globalDiagnostics">A list of global diagnostic variables</param>
        /// <param name="nextCohortID">The ID number to be assigned to the next produced cohort</param>
        /// <param name="tracking">Whether process-tracking is enabled</param>
        /// <param name="DrawRandomly">Whether the model is set to use a random draw</param>
        /// <param name="dispersalOnly">Whether to run dispersal only (i.e. to turn off all other ecological processes</param>
        /// <param name="dispersalOnlyType">For dispersal only runs, the type of dispersal to apply</param>
        public void SeedGridCellStocksAndCohorts(List<uint[]> cellIndices, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions,
            FunctionalGroupDefinitions stockFunctionalGroupDefinitions, SortedList<string, double> globalDiagnostics, ref Int64 nextCohortID,
            Boolean tracking, Boolean DrawRandomly, Boolean dispersalOnly, string dispersalOnlyType, Boolean runCellsInParallel)
        {
            Console.WriteLine("Seeding grid cell stocks and cohorts:");

            //Work out how many cohorts are to be seeded in each grid cell - split by realm as different set of cohorts initialised by realm
            int TotalTerrestrialCellCohorts = 0;
            int TotalMarineCellCohorts = 0;

            int[] TerrestrialFunctionalGroups = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Terrestrial", false);
            if (TerrestrialFunctionalGroups == null)
            {
                TotalTerrestrialCellCohorts = 0;
            }
            else
            {
                foreach (int F in TerrestrialFunctionalGroups)
                {
                    TotalTerrestrialCellCohorts += (int)cohortFunctionalGroupDefinitions.GetBiologicalPropertyOneFunctionalGroup("Initial number of GridCellCohorts", F);
                }
            }

            int[] MarineFunctionalGroups = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Marine", false);
            if (MarineFunctionalGroups == null)
            {
                TotalMarineCellCohorts = 0;
            }
            else
            {
                foreach (int F in MarineFunctionalGroups)
                {
                    TotalMarineCellCohorts += (int)cohortFunctionalGroupDefinitions.GetBiologicalPropertyOneFunctionalGroup("Initial number of GridCellCohorts", F);
                }
            }

            // Now loop through and determine the starting CohortID number for each cell. This allows the seeding to be done in parallel.
            Int64[] StartingCohortsID = new Int64[cellIndices.Count];
            StartingCohortsID[0] = nextCohortID;
            for (int kk = 1; kk < cellIndices.Count; kk++)
            {
                if (InternalGrid[cellIndices[kk - 1][0], cellIndices[kk - 1][1]].CellEnvironment["Realm"][0] == 1)
                {
                    // Terrestrial cell
                    StartingCohortsID[kk] = StartingCohortsID[kk - 1] + TotalTerrestrialCellCohorts;
                }
                else
                {
                    // Marine cell
                    StartingCohortsID[kk] = StartingCohortsID[kk - 1] + TotalMarineCellCohorts;
                }
            }
            int Count = 0;
            if (runCellsInParallel)
            {
                Parallel.For(0, cellIndices.Count, (ii, loopState) =>
                {

                    if (dispersalOnly)
                    {
                        if (dispersalOnlyType == "diffusion")
                        {
                            // Diffusive dispersal

                            if ((cellIndices[ii][0] == 90) && (cellIndices[ii][1] == 180))
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, false);
                            }
                            else if ((cellIndices[ii][0] == 95) && (cellIndices[ii][1] == 110))
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, false);
                            }
                            else
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, true);
                            }
                            Console.Write("\rGrid Cell: {0} of {1}", ii++, cellIndices.Count);
                        }
                        else if (dispersalOnlyType == "advection")
                        {
                            // Advective dispersal
                            /*
                            if ((cellIndices[ii][0] == 58) && (cellIndices[ii][1] == 225))
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, false);
                            }
                            else if ((cellIndices[ii][0] == 95) && (cellIndices[ii][1] == 110))
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, false);
                            }
                            else
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, true);
                            }
                            */
                            if (InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].CellEnvironment["Realm"][0] == 1.0)
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(
                                    cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, globalDiagnostics,
                                    StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                    DrawRandomly, true);
                            }
                            else
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(
                                    cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, globalDiagnostics,
                                    StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                    DrawRandomly, false);
                            }
                            Console.Write("\rGrid Cell: {0} of {1}", ii++, cellIndices.Count);
                        }
                        else if (dispersalOnlyType == "responsive")
                        {
                            // Responsive dispersal

                            InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                            stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                            DrawRandomly, true);

                        }
                        else
                        {
                            Debug.Fail("Dispersal only type not recognized from initialisation file");
                        }
                        Count++;
                    }

                    else
                    {
                        InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(
                            cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, globalDiagnostics,
                            StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                            DrawRandomly, false);
                        Count++;
                    }
                    Console.Write("\rGrid Cell: {0} of {1}", Count, cellIndices.Count);
                }
                );
            }
            else
            {
                for (int ii = 0; ii < cellIndices.Count; ii++)
                {

                    if (dispersalOnly)
                    {
                        if (dispersalOnlyType == "diffusion")
                        {
                            // Diffusive dispersal

                            if ((cellIndices[ii][0] == 90) && (cellIndices[ii][1] == 180))
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, false);
                            }
                            else if ((cellIndices[ii][0] == 95) && (cellIndices[ii][1] == 110))
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, false);
                            }
                            else
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, true);
                            }
                            Console.Write("\rGrid Cell: {0} of {1}", ii++, cellIndices.Count);
                        }
                        else if (dispersalOnlyType == "advection")
                        {
                            // Advective dispersal
                            /*
                            if ((cellIndices[ii][0] == 58) && (cellIndices[ii][1] == 225))
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, false);
                            }
                            else if ((cellIndices[ii][0] == 95) && (cellIndices[ii][1] == 110))
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, false);
                            }
                            else
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                                stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                DrawRandomly, true);
                            }
                            */
                            if (InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].CellEnvironment["Realm"][0] == 1.0)
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(
                                    cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, globalDiagnostics,
                                    StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                    DrawRandomly, true);
                            }
                            else
                            {
                                InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(
                                    cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, globalDiagnostics,
                                    StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                                    DrawRandomly, false);
                            }
                            Console.Write("\rGrid Cell: {0} of {1}", ii++, cellIndices.Count);
                        }
                        else if (dispersalOnlyType == "responsive")
                        {
                            // Responsive dispersal

                            InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(cohortFunctionalGroupDefinitions,
                            stockFunctionalGroupDefinitions, globalDiagnostics, StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                            DrawRandomly, true);

                        }
                        else
                        {
                            Debug.Fail("Dispersal only type not recognized from initialisation file");
                        }
                        Count++;
                    }

                    else
                    {
                        InternalGrid[cellIndices[ii][0], cellIndices[ii][1]].SeedGridCellCohortsAndStocks(
                            cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, globalDiagnostics,
                            StartingCohortsID[ii], tracking, TotalTerrestrialCellCohorts, TotalMarineCellCohorts,
                            DrawRandomly, false);
                        Count++;
                    }
                    Console.Write("\rGrid Cell: {0} of {1}", Count, cellIndices.Count);
                }

            }
                Console.WriteLine("");
                Console.WriteLine("");

                if (InternalGrid[cellIndices[cellIndices.Count - 1][0], cellIndices[cellIndices.Count - 1][1]].CellEnvironment["Realm"][0] == 1)
                    nextCohortID = StartingCohortsID[cellIndices.Count - 1] + TotalTerrestrialCellCohorts;
                else
                    nextCohortID = StartingCohortsID[cellIndices.Count - 1] + TotalMarineCellCohorts;
        }
        /// <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>
 /// Initialises herbivory implementation each time step
 /// </summary>
 /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
 /// <param name="gridCellStocks">The stocks in the current grid cell</param>
 /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param>
 /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param>
 /// <remarks>This only works if: a) herbivory is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips
 /// It is critical to run this every time step</remarks>
 public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions)
 {
     // Get the functional group indices of all autotroph stocks
     _FunctionalGroupIndicesToEat = madingleyStockDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "Autotroph", false);
 }
        /// <summary>
        /// Sets up the outputs associated with the high level of output detail
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The indices of active cells in the model grid</param>
        /// <param name="cellNumber">The index of the current cell in the list of active cells</param>
        /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void SetUpHighLevelOutputs(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellNumber,
            FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, Boolean marineCell)
        {
            // Create an SDS object for outputs by mass bin
            // MassBinsOutput = SDSCreator.CreateSDS("netCDF", "MassBins" + _OutputSuffix, _OutputPath);
            MassBinsOutputMemory = SDSCreator.CreateSDSInMemory(true);

            // Add relevant output variables to the mass bin output file
            string[] MassBinDimensions = { "Time step", "Mass bin" };
            string[] DoubleMassBinDimensions = new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" };

            if (OutputMetrics)
            {
                DataConverter.AddVariable(MassBinsOutputMemory, "Trophic Index Distribution", 2, new string[] {"Time step","Trophic Index Bins"}, ecosystemModelGrid.GlobalMissingValue, TimeSteps, Metrics.TrophicIndexBinValues);
            }

            if (marineCell)
            {
                foreach (string TraitValue in CohortTraitIndicesMarine.Keys)
                {
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                }
            }
            else
            {
                foreach (string TraitValue in CohortTraitIndices.Keys)
                {
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                }
            }

            // Create an SDS object in memory for tracked cohorts outputs
            // TrackedCohortsOutput = SDSCreator.CreateSDS("netCDF", "TrackedCohorts" + _OutputSuffix, _OutputPath);
            TrackedCohortsOutputMemory = SDSCreator.CreateSDSInMemory(true);

            // Initialise list to hold tracked cohorts
            TrackedCohorts = new List<uint>();

            // Identify cohorts to track
            GridCellCohortHandler TempCohorts = null;
            bool FoundCohorts = false;

            // Get a local copy of the cohorts in the grid cell
            TempCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellNumber][0], cellIndices[cellNumber][1]);

            // Loop over functional groups and check whether any cohorts exist in this grid cell
            foreach (var CohortList in TempCohorts)
            {
                if (CohortList.Count > 0)
                {
                    FoundCohorts = true;
                    break;
                }
            }

            // If there are some cohorts in the grid cell, then setup the tracked cohorts
            if (FoundCohorts)
            {
                // Initialise stream writer to hold details of tracked cohorts
                StreamWriter sw = new StreamWriter(_OutputPath + "TrackedCohortProperties" + _OutputSuffix + ".txt");
                sw.WriteLine("Output ID\tCohort ID\tFunctional group index\tNutrition source\tDiet\tRealm\tMobility\tJuvenile mass\tAdult mass");

                // Counter for tracked cohorts
                int TrackedCohortCounter = 0;

                for (int i = 0; i < TempCohorts.Count; i++)
                {
                    if (TempCohorts[i].Count > 0)
                    {
                        for (int j = 0; j < TempCohorts[i].Count; j++)
                        {
                            // Write out properties of the selected cohort
                            sw.WriteLine(Convert.ToString(TrackedCohortCounter) + '\t' + Convert.ToString(TempCohorts[i][j].CohortID[0]) + '\t' + i + '\t' +
                                cohortFunctionalGroupDefinitions.GetTraitNames("Nutrition source", i) + '\t' + cohortFunctionalGroupDefinitions.
                                GetTraitNames("Diet", i) + '\t' + cohortFunctionalGroupDefinitions.GetTraitNames("Realm", i) + '\t' +
                                cohortFunctionalGroupDefinitions.GetTraitNames("Mobility", i) + '\t' + TempCohorts[i][j].JuvenileMass + '\t' +
                                TempCohorts[i][j].AdultMass);

                            // Add the ID of the cohort to the list of tracked cohorts
                            TrackedCohorts.Add(TempCohorts[i][j].CohortID[0]);

                            // Increment the counter of tracked cohorts
                            TrackedCohortCounter++;
                        }
                    }
                }

                // Generate an array of floating points to index the tracked cohorts in the output file
                float[] OutTrackedCohortIDs = new float[TrackedCohortCounter];
                for (int i = 0; i < TrackedCohortCounter; i++)
                {
                    OutTrackedCohortIDs[i] = i;
                }

                // Set up outputs for tracked cohorts
                string[] TrackedCohortsDimensions = { "Time step", "Cohort ID" };

                // Add output variables for the tracked cohorts output
                DataConverter.AddVariable(TrackedCohortsOutputMemory, "Individual body mass", 2, TrackedCohortsDimensions,
                ecosystemModelGrid.GlobalMissingValue, TimeSteps, OutTrackedCohortIDs);
                DataConverter.AddVariable(TrackedCohortsOutputMemory, "Number of individuals", 2, TrackedCohortsDimensions,
                ecosystemModelGrid.GlobalMissingValue, TimeSteps, OutTrackedCohortIDs);

                // Dispose of the streamwriter
                sw.Dispose();
            }

                // Get a list of all possible combinations of trait values as a jagged array
            string[][] TraitValueSearch;

                if (marineCell)
                    TraitValueSearch = CalculateAllCombinations(CohortTraitValuesMarine[CohortTraits[0]], CohortTraitValuesMarine[CohortTraits[1]]);
                else
                    TraitValueSearch = CalculateAllCombinations(CohortTraitValues[CohortTraits[0]], CohortTraitValues[CohortTraits[1]]);

                // Add the functional group indices of these trait combinations to the list of indices of the trait values to consider,
                // keyed with a concatenated version of the trait values
                string TraitValueJoin = "";
                string[] TimeDimension = { "Time step" };
                for (int i = 0; i < TraitValueSearch.Count(); i++)
                {
                    TraitValueJoin = "";
                    foreach (string TraitValue in TraitValueSearch[i])
                    {
                        TraitValueJoin += TraitValue + " ";
                    }

                    if (marineCell)
                    {
                        // Only add indices of marine functional groups
                        int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(CohortTraits, TraitValueSearch[i], true);
                        Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)];
                        for (int ii = 0; ii < TempIndices.GetLength(0); ii++)
                        {
                            if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Marine", StringComparison.OrdinalIgnoreCase))
                            {
                                TempIndices2[ii] = true;
                            }
                        }

                        // Extract only the indices which are marine
                        int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(zz => TempIndices2[zz]).ToArray();

                        if (TempIndices3.Length > 0)
                        {
                            // Extract the values at these indices
                            for (int ii = 0; ii < TempIndices3.Length; ii++)
                            {
                                TempIndices3[ii] = TempIndices[TempIndices3[ii]];
                            }

                            // Add in the indices for this functional group and this realm
                            CohortTraitIndices.Add(TraitValueJoin, TempIndices3);

                            DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                            DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);

                            TotalBiomassDensitiesOut.Add(TraitValueJoin, 0.0);
                            TotalDensitiesOut.Add(TraitValueJoin, 0.0);
                        }
                    }
                    else
                    {
                        // Only add indices of terrestrial functional groups
                        int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(CohortTraits, TraitValueSearch[i], true);
                        Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)];
                        for (int ii = 0; ii < TempIndices.GetLength(0); ii++)
                        {
                            if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Terrestrial", StringComparison.OrdinalIgnoreCase))
                            {
                                TempIndices2[ii] = true;
                            }
                        }

                        // Extract only the indices which are terrestrial
                        int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(zz => TempIndices2[zz]).ToArray();

                        if (TempIndices3.Length > 0)
                        {
                            // Extract the values at these indices
                            for (int ii = 0; ii < TempIndices3.Length; ii++)
                            {
                                TempIndices3[ii] = TempIndices[TempIndices3[ii]];
                            }

                            // Add in the indices for this functional group and this realm
                            CohortTraitIndices.Add(TraitValueJoin, TempIndices3);

                            DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                            DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);

                            TotalBiomassDensitiesOut.Add(TraitValueJoin, 0.0);
                            TotalDensitiesOut.Add(TraitValueJoin, 0.0);
                        }
                    }
                }
        }
        /// <summary>
        /// Set up the necessary architecture for generating outputs arranged by trait value
        /// </summary>
        /// <param name="cohortFunctionalGroupDefinitions">Functional group definitions for cohorts in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">Functional group definitions for stocks in the model</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void InitialiseTraitBasedOutputs(FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, FunctionalGroupDefinitions 
            stockFunctionalGroupDefinitions, Boolean marineCell)
        {
            // Define the cohort traits that will be used to separate outputs
            CohortTraits = new string[2] { "Nutrition source", "Endo/Ectotherm"};

            // Declare a sorted dictionary to hold all unique trait values
            CohortTraitValues = new SortedDictionary<string, string[]>();

            // Declare a sorted dictionary to hold all unique trait values for marine systems
            CohortTraitValuesMarine = new SortedDictionary<string, string[]>();

            // Get the list of functional group indices corresponding to each unique trait value
            if (marineCell)
            {
                // Add all unique trait values to the sorted dictionary
                foreach (string Trait in CohortTraits)
                {
                    CohortTraitValuesMarine.Add(Trait, cohortFunctionalGroupDefinitions.GetUniqueTraitValues(Trait));
                }

                foreach (string Trait in CohortTraits)
                {
                    foreach (string TraitValue in CohortTraitValuesMarine[Trait])
                    {
                        // Only add indices of marine functional groups
                        int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false);
                        Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)];
                        for (int ii = 0; ii < TempIndices.GetLength(0); ii++)
                        {
                            if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Marine", StringComparison.OrdinalIgnoreCase))
                            {
                                TempIndices2[ii] = true;
                            }
                        }

                        // Extract only the indices which are marine
                        int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(i => TempIndices2[i]).ToArray();

                        if (TempIndices3.Length > 0)
                        {
                            // Extract the values at these indices
                            for (int ii = 0; ii < TempIndices3.Length; ii++)
                            {
                                TempIndices3[ii] = TempIndices[TempIndices3[ii]];
                            }

                            // Add in the indices for this functional group and this realm
                            CohortTraitIndicesMarine.Add(TraitValue, TempIndices3);
                        }
                    }
                }

                if (TrackMarineSpecifics)
                {
                    // Add in the specific classes of zooplankton and baleen whales

                    // There are functional groups representing obligate zooplankton
                    string[] TempString = new string[1] { "Obligate zooplankton" };
                    CohortTraitValuesMarine.Add("Obligate zooplankton", TempString);
                    CohortTraitIndicesMarine.Add("Obligate zooplankton", cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Mobility", "planktonic", false));

                    // Whales have a special dietary index
                    TempString = new string[1] { "Baleen whales" };
                    CohortTraitValuesMarine.Add("Baleen whales", TempString);
                    CohortTraitIndicesMarine.Add("Baleen whales", cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Diet", "allspecial", false));

                    // But we also want all zooplankton, including larval/juvenile stages of other cohorts
                    int[] ZooplanktonIndices1 = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Mobility", "planktonic", false);

                    // Then there are all of the other groups which may have planktonic juveniles. In the ModelGrid.cs class, these cohorts are checked to see
                    // if they have a weight of less than the planktonic dispersal threshold.
                    TempString = new string[3] { "Realm", "Endo/Ectotherm", "Mobility" };
                    string[] TempString2 = new string[3] { "marine", "ectotherm", "mobile" };
                    int[] ZooplanktonIndices2 = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(TempString, TempString2, true);
                    CohortTraitIndicesMarine.Add("Zooplankton (all)", ZooplanktonIndices2.Concat(ZooplanktonIndices1).ToArray());
                }

                // Add unique trait values to each of the lists that will contain output data arranged by trait value
                foreach (string TraitValue in CohortTraitIndicesMarine.Keys)
                {
                    TotalBiomassDensitiesMarineOut.Add(TraitValue, 0.0);
                    TotalDensitiesMarineOut.Add(TraitValue, 0.0);
                }
            }
            else
            {
                // Add all unique trait values to the sorted dictionary
                foreach (string Trait in CohortTraits)
                {
                    CohortTraitValues.Add(Trait, cohortFunctionalGroupDefinitions.GetUniqueTraitValues(Trait));
                }

                foreach (string Trait in CohortTraits)
                {
                    foreach (string TraitValue in CohortTraitValues[Trait])
                    {
                        // Only add indices of terrestrial functional groups
                        int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false);
                        Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)];
                        for (int ii = 0; ii < TempIndices.GetLength(0); ii++)
                        {
                            if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Terrestrial", StringComparison.OrdinalIgnoreCase))
                            {
                                TempIndices2[ii] = true;
                            }
                        }

                        // Extract only the indices which are terrestrial
                        int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(i => TempIndices2[i]).ToArray();

                        if (TempIndices3.Length > 0)
                        {
                            // Extract the values at these indices
                            for (int ii = 0; ii < TempIndices3.Length; ii++)
                            {
                                TempIndices3[ii] = TempIndices[TempIndices3[ii]];
                            }

                            // Add in the indices for this functional group and this realm
                            CohortTraitIndices.Add(TraitValue, TempIndices3);
                        }
                    }
                }

                // Add unique trait values to each of the lists that will contain output data arranged by trait value
                foreach (string TraitValue in CohortTraitIndices.Keys)
                {
                    TotalBiomassDensitiesOut.Add(TraitValue, 0.0);
                    TotalDensitiesOut.Add(TraitValue, 0.0);
                }
            }

            if (marineCell)
            {
                // Define the stock traits that will be used to separate outputs
                StockTraitsMarine = new string[1] { "Heterotroph/Autotroph"};

                // Re-initialise the sorted dictionary to hold all unique trait values
                StockTraitValuesMarine = new SortedDictionary<string, string[]>();

                // Add all unique stock trait values to the sorted dictionary
                foreach (string Trait in StockTraitsMarine)
                {
                    StockTraitValuesMarine.Add(Trait, stockFunctionalGroupDefinitions.GetUniqueTraitValues(Trait));
                }

                // Get the list of functional group indices corresponding to each unique marine trait value
                foreach (string Trait in StockTraitsMarine)
                {
                    foreach (string TraitValue in StockTraitValuesMarine[Trait])
                    {
                        StockTraitIndicesMarine.Add(TraitValue, stockFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false));
                    }
                }

                // Add unique trait values to each of the lists that will contain output data arranged by trait value
                foreach (string TraitValue in StockTraitIndicesMarine.Keys)
                {
                    TotalBiomassDensitiesOut.Add(TraitValue, 0.0);
                }
            }
            else
            {
                // Define the stock traits that will be used to separate outputs
                StockTraits = new string[2] { "Heterotroph/Autotroph", "Leaf strategy" };

                // Re-initialise the sorted dictionary to hold all unique trait values
                StockTraitValues = new SortedDictionary<string, string[]>();

                // Add all unique marine stock trait values to the sorted dictionary
                foreach (string Trait in StockTraits)
                {
                    StockTraitValues.Add(Trait, stockFunctionalGroupDefinitions.GetUniqueTraitValues(Trait));
                }

                // Get the list of functional group indices corresponding to each unique  trait value
                foreach (string Trait in StockTraits)
                {
                    foreach (string TraitValue in StockTraitValues[Trait])
                    {
                        StockTraitIndices.Add(TraitValue, stockFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false));
                    }
                }

                // Add unique trait values to each of the lists that will contain output data arranged by trait value
                foreach (string TraitValue in StockTraitIndices.Keys)
                {
                    TotalBiomassDensitiesOut.Add(TraitValue, 0.0);
                }
            }
        }
        /// <summary>
        /// Seed grid cell with stocks, as specified in the model input files
        /// </summary>
        /// <param name="functionalGroups">A reference to the stock functional group handler</param>
        /// <param name="cellEnvironment">The environment in the grid cell</param>
        /// <param name="globalDiagnostics">A list of global diagnostic variables for the model grid</param>
        private void SeedGridCellStocks(ref FunctionalGroupDefinitions functionalGroups, ref SortedList<string, double[]> 
            cellEnvironment, SortedList<string, double> globalDiagnostics)
        {
            // Set the seed for the random number generator from the system time
            RandomNumberGenerator.SetSeedFromSystemTime();

            Stock NewStock;

            // Define local variables
            int[] FunctionalGroupsToUse;

            // Get the individual body masses for organisms in each stock functional group
            double[] IndividualMass = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("individual mass");

            // Check which realm the cell is in
            if (cellEnvironment["Realm"][0] == 1.0 && _CellEnvironment["Precipitation"][0] != _CellEnvironment["Missing Value"][0] && _CellEnvironment["Temperature"][0] != _CellEnvironment["Missing Value"][0])
            {
                // Get the indices of all terrestrial functional groups
                FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "terrestrial", true);
            }
            else if (cellEnvironment["Realm"][0] == 2.0 && _CellEnvironment["NPP"][0] != _CellEnvironment["Missing Value"][0])
            {
                // Get the indices of all marine functional groups
                FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "marine", true);
            }
            else
            {
                // For cells without a realm designation, no functional groups will be used
                FunctionalGroupsToUse = new int[0];
            }

            // Loop over all functional groups in the model
            for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++)
            {
                // Create a new list to hold the stocks in the grid cell
                _GridCellStocks[FunctionalGroup] = new List<Stock>();

                // If it is a functional group that corresponds to the current realm, then seed the stock
                if (FunctionalGroupsToUse.Contains(FunctionalGroup))
                {
                    if (_CellEnvironment["Realm"][0] == 1.0)
                    {
                        // An instance of the terrestrial carbon model class
                        RevisedTerrestrialPlantModel PlantModel = new RevisedTerrestrialPlantModel();

                        // Calculate predicted leaf mass at equilibrium for this stock
                        double LeafMass = PlantModel.CalculateEquilibriumLeafMass(_CellEnvironment, functionalGroups.GetTraitNames("leaf strategy", FunctionalGroup) == "deciduous");

                        // Initialise the new stock with the relevant properties
                        NewStock = new Stock((byte)FunctionalGroup, IndividualMass[FunctionalGroup], LeafMass);

                        // Add the new stock to the list of grid cell stocks
                        _GridCellStocks[FunctionalGroup].Add(NewStock);

                        // Increment the variable tracking the total number of stocks in the model
                        globalDiagnostics["NumberOfStocksInModel"]++;

                    }
                    else if (FunctionalGroupsToUse.Contains(FunctionalGroup))
                    {
                        // Initialise the new stock with the relevant properties
                        NewStock = new Stock((byte)FunctionalGroup, IndividualMass[FunctionalGroup], 1e12);

                        // Add the new stock to the list of grid cell stocks
                        _GridCellStocks[FunctionalGroup].Add(NewStock);

                        // Increment the variable tracking the total number of stocks in the model
                        globalDiagnostics["NumberOfStocksInModel"]++;

                    }
                    else
                    {
                    }

                }

            }
        }
        /// <summary>
        /// Seed grid cell with cohorts, as specified in the model input files
        /// </summary>
        /// <param name="functionalGroups">The functional group definitions for cohorts in the grid cell</param>
        /// <param name="cellEnvironment">The environment in the grid cell</param>
        /// <param name="globalDiagnostics">A list of global diagnostic variables</param>
        /// <param name="nextCohortID">YThe unique ID to assign to the next cohort produced</param>
        /// <param name="tracking">boolean to indicate if cohorts are to be tracked in this model</param>
        /// <param name="totalCellTerrestrialCohorts">The total number of cohorts to be seeded in each terrestrial grid cell</param>
        /// <param name="totalCellMarineCohorts">The total number of cohorts to be seeded in each marine grid cell</param>
        /// <param name="DrawRandomly">Whether the model is set to use random draws</param>
        /// <param name="ZeroAbundance">Set this parameter to 'true' if you want to seed the cohorts with zero abundance</param>
        private void SeedGridCellCohorts(ref FunctionalGroupDefinitions functionalGroups, ref SortedList<string, double[]>
            cellEnvironment, SortedList<string, double> globalDiagnostics, Int64 nextCohortID, Boolean tracking, double totalCellTerrestrialCohorts, 
            double totalCellMarineCohorts, Boolean DrawRandomly, Boolean ZeroAbundance)
        {
            // Set the seed for the random number generator from the system time
            RandomNumberGenerator.SetSeedFromSystemTime();

            // StreamWriter tempsw = new StreamWriter("C://Temp//adult_juvenile_masses.txt");
            // tempsw.WriteLine("adult mass\tjuvenilemass");

            // Define local variables
            double CohortJuvenileMass;
            double CohortAdultMassRatio;
            double CohortAdultMass;
            double ExpectedLnAdultMassRatio;
            int[] FunctionalGroupsToUse;
            double NumCohortsThisCell;
            double TotalNewBiomass =0.0;

            // Get the minimum and maximum possible body masses for organisms in each functional group
            double[] MassMinima = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("minimum mass");
            double[] MassMaxima = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("maximum mass");
            string[] NutritionSource = functionalGroups.GetTraitValuesAllFunctionalGroups("nutrition source");

            double[] ProportionTimeActive = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("proportion suitable time active");

            //Variable for altering the juvenile to adult mass ratio for marine cells when handling certain functional groups eg baleen whales
            double Scaling = 0.0;

            Int64 CohortIDIncrementer = nextCohortID;

            // Check which realm the cell is in
            if (cellEnvironment["Realm"][0] == 1.0)
            {
                // Get the indices of all terrestrial functional groups
                FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "terrestrial", true);
                NumCohortsThisCell = totalCellTerrestrialCohorts;
            }
            else
            {
                // Get the indices of all marine functional groups
                FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "marine", true);
                NumCohortsThisCell = totalCellMarineCohorts;
            }
            Debug.Assert(cellEnvironment["Realm"][0] > 0.0, "Missing realm for grid cell");

            if (NumCohortsThisCell > 0)
            {
                // Loop over all functional groups in the model
                for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++)
                {

                    // Create a new list to hold the cohorts in the grid cell
                    _GridCellCohorts[FunctionalGroup] = new List<Cohort>();

                    // If it is a functional group that corresponds to the current realm, then seed cohorts
                    if (FunctionalGroupsToUse.Contains(FunctionalGroup))
                    {
                        // Loop over the initial number of cohorts
                        double NumberOfCohortsInThisFunctionalGroup = 1.0;
                        if (!ZeroAbundance)
                        {
                            NumberOfCohortsInThisFunctionalGroup = functionalGroups.GetBiologicalPropertyOneFunctionalGroup("initial number of gridcellcohorts", FunctionalGroup);
                        }
                        for (int jj = 0; jj < NumberOfCohortsInThisFunctionalGroup; jj++)
                        {
                            // Check whether the model is set to randomly draw the body masses of new cohorts
                            if (DrawRandomly)
                            {
                                // Draw adult mass from a log-normal distribution with mean -6.9 and standard deviation 10.0,
                                // within the bounds of the minimum and maximum body masses for the functional group
                                CohortAdultMass = Math.Pow(10, (RandomNumberGenerator.GetUniform() * (Math.Log10(MassMaxima[FunctionalGroup]) - Math.Log10(50 * MassMinima[FunctionalGroup])) + Math.Log10(50 * MassMinima[FunctionalGroup])));

                                // Terrestrial and marine organisms have different optimal prey/predator body mass ratios
                                if (cellEnvironment["Realm"][0] == 1.0)
                                    // Optimal prey body size 10%
                                    OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02));
                                else
                                {
                                    if (functionalGroups.GetTraitNames("Diet", FunctionalGroup) == "allspecial")
                                    {
                                        // Note that for this group
                                        // it is actually (despite the name) not an optimal prey body size ratio, but an actual body size.
                                        // This is because it is invariant as the predator (filter-feeding baleen whale) grows.
                                        // See also the predation classes.
                                        OptimalPreyBodySizeRatio = Math.Max(0.00001, RandomNumberGenerator.GetNormal(0.0001, 0.1));
                                    }
                                    else
                                    {
                                        // Optimal prey body size or marine organisms is 10%
                                        OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02));
                                    }

                                }

                                // Draw from a log-normal distribution with mean 10.0 and standard deviation 5.0, then add one to obtain
                                // the ratio of adult to juvenile body mass, and then calculate juvenile mass based on this ratio and within the
                                // bounds of the minimum and maximum body masses for this functional group
                                if (cellEnvironment["Realm"][0] == 1.0)
                                {
                                    do
                                    {
                                        ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass);
                                        CohortAdultMassRatio = 1.0 + RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5);
                                        CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio;
                                    } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]);
                                }
                                // In the marine realm, have a greater difference between the adult and juvenile body masses, on average
                                else
                                {
                                    uint Counter = 0;
                                    Scaling = 0.2;
                                    // Use the scaling to deal with baleen whales not having such a great difference
                                    do
                                    {

                                        ExpectedLnAdultMassRatio = 2.5 + Scaling * Math.Log(CohortAdultMass);
                                        CohortAdultMassRatio = 1.0 + 10 * RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5);
                                        CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio;
                                        Counter++;
                                        if (Counter > 10)
                                        {
                                            Scaling -= 0.01;
                                            Counter = 0;
                                        }
                                    } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]);
                                }
                            }
                            else
                            {
                                // Use the same seed for the random number generator every time
                                RandomNumberGenerator.SetSeed((uint)(jj + 1), (uint)((jj + 1) * 3));

                                // Draw adult mass from a log-normal distribution with mean -6.9 and standard deviation 10.0,
                                // within the bounds of the minimum and maximum body masses for the functional group
                                CohortAdultMass = Math.Pow(10, (RandomNumberGenerator.GetUniform() * (Math.Log10(MassMaxima[FunctionalGroup]) - Math.Log10(50 * MassMinima[FunctionalGroup])) + Math.Log10(50 * MassMinima[FunctionalGroup])));

                                OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02));

                                // Draw from a log-normal distribution with mean 10.0 and standard deviation 5.0, then add one to obtain
                                // the ratio of adult to juvenile body mass, and then calculate juvenile mass based on this ratio and within the
                                // bounds of the minimum and maximum body masses for this functional group
                                if (cellEnvironment["Realm"][0] == 1.0)
                                {
                                    do
                                    {
                                        ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass);
                                        CohortAdultMassRatio = 1.0 + RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5);
                                        CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio;
                                    } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]);

                                }
                                // In the marine realm, have a greater difference between the adult and juvenile body masses, on average
                                else
                                {
                                    do
                                    {
                                        ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass);
                                        CohortAdultMassRatio = 1.0 + 10 * RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5);
                                        CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio;
                                    } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]);
                                }
                            }

                            // An instance of Cohort to hold the new cohort
                            Cohort NewCohort;
                            //double NewBiomass = Math.Pow(0.2, (Math.Log10(CohortAdultMass))) * (1.0E9 * _CellEnvironment["Cell Area"][0]) / NumCohortsThisCell;
                            // 3000*(0.6^log(mass)) gives individual cohort biomass density in g ha-1
                            // * 100 to give g km-2
                            // * cell area to give g grid cell
                            //*3300/NumCohortsThisCell scales total initial biomass in the cell to some approximately reasonable mass
                            double NewBiomass = (3300 / NumCohortsThisCell) * 100 * 3000 *
                                Math.Pow(0.6, (Math.Log10(CohortJuvenileMass))) * (_CellEnvironment["Cell Area"][0]);
                            TotalNewBiomass += NewBiomass;
                            double NewAbund = 0.0;
                            if (!ZeroAbundance)
                            {
                                NewAbund = NewBiomass / CohortJuvenileMass;
                            }

                            /*
                            // TEMPORARILY MARINE ONLY
                            if (cellEnvironment["Realm"][0] == 1)
                            {
                                NewAbund = 0.0;
                            }
                            */
                            double TrophicIndex;
                            switch (NutritionSource[FunctionalGroup])
                            {
                                case "herbivore":
                                    TrophicIndex = 2;
                                    break;
                                case "omnivore":
                                    TrophicIndex = 2.5;
                                    break;
                                case "carnivore":
                                    TrophicIndex = 3;
                                    break;
                                default:
                                    Debug.Fail("Unexpected nutrition source trait value when assigning trophic index");
                                    TrophicIndex = 0.0;
                                    break;
                            }

                            // Initialise the new cohort with the relevant properties
                            NewCohort = new Cohort((byte)FunctionalGroup, CohortJuvenileMass, CohortAdultMass, CohortJuvenileMass, NewAbund,
                            OptimalPreyBodySizeRatio, (ushort)0, ProportionTimeActive[FunctionalGroup], ref CohortIDIncrementer,TrophicIndex, tracking);

                            // Add the new cohort to the list of grid cell cohorts
                            _GridCellCohorts[FunctionalGroup].Add(NewCohort);

                            // TEMPORARY
                            /*
                            // Check whether the model is set to randomly draw the body masses of new cohorts
                            if ((Longitude % 4 == 0) && (Latitude % 4 == 0))
                            {
                                if (DrawRandomly)
                                {
                                    CohortAdultMass = 100000;
                                    CohortJuvenileMass = 100000;
                                }
                                else
                                {
                                    CohortAdultMass = 100000;
                                    CohortJuvenileMass = 100000;
                                }

                                // An instance of Cohort to hold the new cohort
                                Cohort NewCohort;
                                double NewBiomass = (1.0E7 * _CellEnvironment["Cell Area"][0]) / NumCohortsThisCell;
                                double NewAbund = 0.0;
                                NewAbund = 3000;

                                // Initialise the new cohort with the relevant properties
                                NewCohort = new Cohort((byte)FunctionalGroup, CohortJuvenileMass, CohortAdultMass, CohortJuvenileMass, NewAbund,
                                    (ushort)0, ref nextCohortID, tracking);

                                // Add the new cohort to the list of grid cell cohorts
                                _GridCellCohorts[FunctionalGroup].Add(NewCohort);
                            }
                            */

                            // Incrememt the variable tracking the total number of cohorts in the model
                            globalDiagnostics["NumberOfCohortsInModel"]++;

                        }

                    }
                }

            }
            else
            {
                                // Loop over all functional groups in the model
                for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++)
                {

                    // Create a new list to hold the cohorts in the grid cell
                    _GridCellCohorts[FunctionalGroup] = new List<Cohort>();
                }
            }

            // tempsw.Dispose();
        }
Esempio n. 14
0
        /// <summary>
        /// Set up the file, screen and live outputs prior to the model run
        /// </summary>
        /// <param name="EcosystemModelGrid">The model grid that output data will be derived from</param>
        /// <param name="CohortFunctionalGroupDefinitions">The definitions for cohort functional groups</param>
        /// <param name="StockFunctionalGroupDefinitions">The definitions for stock functional groups</param>
        /// <param name="NumTimeSteps">The number of time steps in the model run</param>
        public void SetUpOutputs(ModelGrid EcosystemModelGrid, FunctionalGroupDefinitions CohortFunctionalGroupDefinitions, 
            FunctionalGroupDefinitions StockFunctionalGroupDefinitions, uint NumTimeSteps, string FileOutputs)
        {
            // Get the functional group indices of herbivore, carnivore and omnivore cohorts, and autotroph stocks
            string[] Trait = { "Nutrition source" };
            string[] Trait2 = { "Heterotroph/Autotroph" };
            string[] TraitValue1 = { "Herbivory" };
            string[] TraitValue2 = { "Carnivory" };
            string[] TraitValue3 = { "Omnivory" };
            string[] TraitValue4 = { "Autotroph" };

            HerbivoreIndices = CohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue1, false);
            CarnivoreIndices = CohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue2, false);
            OmnivoreIndices = CohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue3, false);
            AutotrophIndices = StockFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait2, TraitValue4, false);

            // Set up vectors to hold dimension data for the output variables
            float[] outLats = new float[EcosystemModelGrid.NumLatCells];
            float[] outLons = new float[EcosystemModelGrid.NumLonCells];
            float[] IdentityMassBins;

            // Populate the dimension variable vectors with cell centre latitude and longitudes
            for (int ii = 0; ii < EcosystemModelGrid.NumLatCells; ii++)
            {
                outLats[ii] = EcosystemModelGrid.Lats[ii] + (EcosystemModelGrid.LatCellSize / 2);
            }

            for (int jj = 0; jj < EcosystemModelGrid.NumLonCells; jj++)
            {
                outLons[jj] = EcosystemModelGrid.Lons[jj] + (EcosystemModelGrid.LonCellSize / 2);
            }

            // Create vector to hold the values of the time dimension
            OutTimes = new float[NumTimeSteps + 1];
            // Set the first value to be -1 (this will hold initial outputs)
            OutTimes[0] = -1;
            // Fill other values from 0 (this will hold outputs during the model run)
            for (int ii = 1; ii < NumTimeSteps + 1; ii++)
            {
                OutTimes[ii] = ii + 1;
            }

            // Set up a vector to hold (log) individual body mass bins
            OutMassBins = new float[MassBinNumber];
            IdentityMassBins = new float[MassBinNumber];

            // Get the (log) minimum and maximum possible (log) masses across all functional groups combined, start with default values of
            // Infinity and -Infinity
            float MaximumMass = -1 / 0F;
            float MinimumMass = 1 / 0F;
            foreach (int FunctionalGroupIndex in CohortFunctionalGroupDefinitions.AllFunctionalGroupsIndex)
            {
                MinimumMass = (float)Math.Min(MinimumMass, Math.Log(CohortFunctionalGroupDefinitions.GetBiologicalPropertyOneFunctionalGroup("minimum mass", FunctionalGroupIndex)));
                MaximumMass = (float)Math.Max(MaximumMass, Math.Log(CohortFunctionalGroupDefinitions.GetBiologicalPropertyOneFunctionalGroup("maximum mass", FunctionalGroupIndex)));
            }

            // Get the interval required to span the range between the minimum and maximum values in 100 steps
            float MassInterval = (MaximumMass - MinimumMass) / MassBinNumber;

            // Fill the vector of output mass bins with (log) body masses spread evenly between the minimum and maximum values
            for (int ii = 0; ii < MassBinNumber; ii++)
            {
                OutMassBins[ii] = MinimumMass + ii * MassInterval;
                IdentityMassBins[ii] = Convert.ToSingle(Math.Exp(Convert.ToDouble(OutMassBins[ii])));
            }

            // Create file for model outputs
            DataSetForFileOutput = CreateSDSObject.CreateSDS("netCDF", FileOutputs);

            // Add three-dimensional variables to output file, dimensioned by latitude, longtiude and time
            string[] dimensions3D = { "Latitude", "Longitude", "Time step" };
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Biomass density", 3, dimensions3D, 0, outLats, outLons, OutTimes);


            dimensions3D = new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" };
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Carnivore abundance in juvenile vs adult bins", 3, dimensions3D,Math.Log(0), OutMassBins, OutMassBins, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Herbivore abundance in juvenile vs adult bins", 3, dimensions3D, Math.Log(0), OutMassBins, OutMassBins, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Carnivore biomass in juvenile vs adult bins", 3, dimensions3D, Math.Log(0), OutMassBins, OutMassBins, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Herbivore biomass in juvenile vs adult bins", 3, dimensions3D, Math.Log(0), OutMassBins, OutMassBins, OutTimes);

            // Add two-dimensional variables to output file, dimensioned by mass bins and time
            string[] dimensions2D = { "Time step", "Mass bin" };
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Carnivore abundance in mass bins", 2, dimensions2D, Math.Log(0), OutTimes, OutMassBins);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Herbivore abundance in mass bins", 2, dimensions2D, Math.Log(0), OutTimes, OutMassBins);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Carnivore biomass in mass bins", 2, dimensions2D, Math.Log(0), OutTimes, OutMassBins);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Herbivore biomass in mass bins", 2, dimensions2D, Math.Log(0), OutTimes, OutMassBins);


            // Add one-dimensional variables to the output file, dimensioned by time
            string[] dimensions1D = { "Time step" };
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Herbivore density", "Individuals / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Herbivore abundance", "Individuals", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Herbivore biomass", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Carnivore density", "Individuals / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Carnivore abundance", "Individuals", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes); 
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Carnivore biomass", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Omnivore density", "Individuals / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Omnivore abundance", "Individuals", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes); 
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Omnivore biomass", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Autotroph biomass", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Organic matter pool", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Respiratory CO2 pool", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of cohorts extinct", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of cohorts produced", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of cohorts combined", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of cohorts in model", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of stocks in model", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);

            // Add one-dimensional variables to the output file, dimensioned by mass bin index
            // To enable outputs to be visualised against mass instead of index



            // Initialise the arrays that will be used for the grid-based outputs
            LogBiomassDensityGridCohorts = new double[EcosystemModelGrid.NumLatCells, EcosystemModelGrid.NumLonCells];
            LogBiomassDensityGridStocks = new double[EcosystemModelGrid.NumLatCells, EcosystemModelGrid.NumLonCells];
            LogBiomassDensityGrid = new double[EcosystemModelGrid.NumLatCells, EcosystemModelGrid.NumLonCells];

        }
        /// <summary>
        /// Initialises herbivory implementation each time step
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <remarks>This only works if: a) herbivory is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips
        /// It is critical to run this every time step</remarks>
        public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, SortedList<string, double[]>
            cellEnvironment)
        {
            // Get the functional group indices of all autotroph stocks
               _FunctionalGroupIndicesToEat = madingleyStockDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "Autotroph", false);

            string[] realm = { "Terrestrial", "Marine" };

            int realm_index = (int)cellEnvironment["Realm"][0] - 1;
            string Current_realm = realm[realm_index];
        }
        /// <summary>
        /// Seed the stocks and cohorts from output from a previous simulation
        /// </summary>
        /// <param name="cellIndices">A list of the active cells in the model grid</param>
        /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">The functional group definitions for stocks in the model</param>
        /// <param name="globalDiagnostics">A list of global diagnostic variables</param>
        /// <param name="nextCohortID">The ID number to be assigned to the next produced cohort</param>
        /// <param name="tracking">Whether process-tracking is enabled</param>
        /// <param name="DrawRandomly">Whether the model is set to use a random draw</param>
        /// <param name="dispersalOnly">Whether to run dispersal only (i.e. to turn off all other ecological processes</param>
        /// <param name="processTrackers">An instance of the ecological process tracker</param>
        public void SeedGridCellStocksAndCohorts(List<uint[]> cellIndices,
            InputModelState inputModelState,
            FunctionalGroupDefinitions cohortFunctionalGroupDefinitions,
            FunctionalGroupDefinitions stockFunctionalGroupDefinitions)
        {
            int ii = 1;
            Console.WriteLine("Seeding grid cell stocks and cohorts:");

            //Check to see if the correct number of functional groups exist in the definitions file and in the input state

            if (cohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups() != inputModelState.GridCellCohorts[
                cellIndices[0][0],cellIndices[0][1]].Count)
            {
                Console.WriteLine("Mismatch in the number of functional groups defined in CohortFunctionalGroupDefinitions.csv set-up file and the Model State being read in");
                Environment.Exit(0);
            }

            int[] TerrestrialStockFunctionalIndices = stockFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Terrestrial", false);
            int[] MarineStockFunctionalIndices = stockFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Marine", false);

            int[] TerrestrialCohortFunctionalIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Terrestrial", false);
            int[] MarineCohortFunctionalIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Marine", false);

            foreach (uint[] cellIndexPair in cellIndices)
            {

                for (int i = 0; i < inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]].Count; i++)
                {
                    InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellCohorts[i] = new List<Cohort>();
                }
                //Check which cohorts should be initialised for each cell
                if (InternalGrid[cellIndexPair[0], cellIndexPair[1]].CellEnvironment["Realm"][0] == 1)
                {
                    //This is a terrestrial cell so only add the terrestrial stocks
                    foreach (int fg in TerrestrialCohortFunctionalIndices)
                    {
                        //Cohort[] tempGridCellCohorts = (Cohort[])inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Clone();
                        //Cohort[] tempGridCellCohorts = (Cohort[])Array.ConvertAll(inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray(),
                        //    element => (Cohort)element.Clone());
                        Cohort[] tempGridCellCohorts = inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Select(cohort => new Cohort(cohort)).ToArray();
                        InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellCohorts[fg] = tempGridCellCohorts.ToList();
                    }
                }
                else
                {
                    // this is a marine cell so only add the marine stocks
                    foreach (int fg in MarineCohortFunctionalIndices)
                    {
                        //Cohort[] tempGridCellCohorts = (Cohort[])inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Clone();
                        //Cohort[] tempGridCellCohorts = (Cohort[])Array.ConvertAll(inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray(),
                        //     element => (Cohort)element.Clone());
                        Cohort[] tempGridCellCohorts = inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Select(cohort => new Cohort(cohort)).ToArray();
                        InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellCohorts[fg] = tempGridCellCohorts.ToList();
                    }
                }

                for (int i = 0; i < inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]].Count; i++)
                {
                    InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellStocks[i] = new List<Stock>();
                }

                //Check which stocks should be initialised for each cell
                if (InternalGrid[cellIndexPair[0], cellIndexPair[1]].CellEnvironment["Realm"][0] == 1)
                {
                    //This is a terrestrial cell so only add the terrestrial stocks
                    foreach (int fg in TerrestrialStockFunctionalIndices)
                    {
                        //Stock[] tempGridCellStocks = (Stock[])inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Clone();
                        //Stock[] tempGridCellStocks = (Stock[])Array.ConvertAll(inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray(),
                        //     element => (Stock)element.Clone());
                        Stock[] tempGridCellStocks = inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Select(stock => new Stock(stock)).ToArray();
                        InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellStocks[fg] = tempGridCellStocks.ToList();
                    }
                }
                else
                {
                    // this is a marine cell so only add the marine stocks
                    foreach (int fg in MarineStockFunctionalIndices)
                    {
                        //Stock[] tempGridCellStocks = (Stock[])inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Clone();
                        //Stock[] tempGridCellStocks = (Stock[])Array.ConvertAll(inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray(),
                        //     element => (Stock)element.Clone());
                        Stock[] tempGridCellStocks = inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Select(stock => new Stock(stock)).ToArray();
                        InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellStocks[fg] = tempGridCellStocks.ToList();
                    }
                }

                Console.Write("\rGrid Cell: {0} of {1}", ii++, cellIndices.Count);

            }

            Console.WriteLine("");
            Console.WriteLine("");
        }
        /// <summary>
        /// Initialises predation implementation each time step
        /// </summary>
        /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
        /// <param name="gridCellStocks">The stocks in the current grid cell</param>
        /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param>
        /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param>
        /// <param name="cellEnvironment">The environment in the current grid cell</param>
        /// <remarks>This only works if: a) predation is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips
        /// It is critical to run this every time step</remarks>
        public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, SortedList<string, double[]>
            cellEnvironment)
        {
            // Get the functional group indices of all heterotroph cohorts (i.e. potential prey)

            // _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "heterotroph", false);

            string [] realm = { "Terrestrial", "Marine" };

            int realm_index = (int)cellEnvironment["Realm"][0] - 1;
            string Current_realm = realm[realm_index];

               _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("realm", Current_realm, false);

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

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

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

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

            // Loop over functional groups that are potential prey and determine which are carnivores THIS IS COMMENTED OUT AS IT'S NOT USED
            //foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
            //    _CarnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "carnivore";

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

            //foreach (int FunctionalGroup in FunctionalGroupIndicesToEat)
            //    _PlanktonFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Mobility", FunctionalGroup) == "planktonic";
        }
        /// <summary>
        /// Set up the necessary architecture for generating outputs arranged by trait value
        /// </summary>
        /// <param name="cohortFunctionalGroupDefinitions">Functional group definitions for cohorts in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">Functional group definitions for stocks in the model</param>
        private void InitialiseTraitBasedOutputs(FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, FunctionalGroupDefinitions
            stockFunctionalGroupDefinitions)
        {
            // Define the cohort traits that will be used to separate outputs
            CohortTraits = new string[3] { "Nutrition source", "Endo/Ectotherm", "Reproductive strategy" };

            // Declare a sorted dictionary to hold all unique trait values
            CohortTraitValues = new SortedDictionary<string, string[]>();

            // Add all unique trait values to the sorted dictionary
            foreach (string Trait in CohortTraits)
            {
                CohortTraitValues.Add(Trait, cohortFunctionalGroupDefinitions.GetUniqueTraitValues(Trait));
            }

            // Get the list of functional group indices corresponding to each unique trait value
            foreach (string Trait in CohortTraits)
            {
                foreach (string TraitValue in CohortTraitValues[Trait])
                {
                    CohortTraitIndices.Add(TraitValue, cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false));
                }
            }

            // Define the stock traits that will be used to separate outputs
            StockTraits = new string[2] { "Heterotroph/Autotroph" ,"Leaf strategy"};

            // Re-initialise the sorted dictionary to hold all unique trait values
            StockTraitValues = new SortedDictionary<string, string[]>();

            // Add all unique stock trait values to the sorted dictionary
            foreach (string Trait in StockTraits)
            {
                StockTraitValues.Add(Trait, stockFunctionalGroupDefinitions.GetUniqueTraitValues(Trait));
            }

            // Get the list of functional group indices corresponding to each unique trait value
            foreach (string Trait in StockTraits)
            {
                foreach (string TraitValue in StockTraitValues[Trait])
                {
                    StockTraitIndices.Add(TraitValue, stockFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue, false));
                }
            }
        }
 /// <summary>
 /// Initialises herbivory implementation each time step
 /// </summary>
 /// <param name="gridCellCohorts">The cohorts in the current grid cell</param>
 /// <param name="gridCellStocks">The stocks in the current grid cell</param>
 /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param>
 /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param>
 /// <remarks>This only works if: a) herbivory is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips
 /// It is critical to run this every time step</remarks>
 public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions)
 {
     // Get the functional group indices of all autotroph stocks
     _FunctionalGroupIndicesToEat = madingleyStockDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "Autotroph", false);
 }