/// <summary>
        /// Calculate trophic evenness using the Rao Index
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of indices of cells to be run in the current simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to be run</param>
        /// <returns>Trophic evenness</returns>
        public double CalculateFunctionalEvennessRao(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions,
                                                     List <uint[]> cellIndices, int cellIndex, string trait)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            double[] EvennessValues = new double[2];

            double[,] Distances = new double[CellCohorts.GetNumberOfCohorts(), CellCohorts.GetNumberOfCohorts()];

            double[] FunctionalTrait    = new double[CellCohorts.GetNumberOfCohorts()];
            double   MaxModelTraitValue = 0;
            double   MinModelTraitValue = 0;

            // Construct a vector of cohort biomass (in case we want to weight by them)
            double[] CohortTotalBiomasses = new double[CellCohorts.GetNumberOfCohorts()];


            int CohortNumberCounter = 0;

            switch (trait.ToLower())
            {
            case "biomass":
                for (int fg = 0; fg < CellCohorts.Count; fg++)
                {
                    foreach (Cohort c in CellCohorts[fg])
                    {
                        FunctionalTrait[CohortNumberCounter]      = c.IndividualBodyMass;
                        CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                        CohortNumberCounter++;
                    }
                }

                //Define upper and lower limits for body mass
                MinModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
                MaxModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
                break;

            case "trophic index":
                for (int fg = 0; fg < CellCohorts.Count; fg++)
                {
                    foreach (Cohort c in CellCohorts[fg])
                    {
                        FunctionalTrait[CohortNumberCounter]      = c.IndividualBodyMass;
                        CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                        CohortNumberCounter++;
                    }
                }
                MinModelTraitValue = MinTI;
                MaxModelTraitValue = MaxTI;
                break;
            }


            Distances = CalculateDistanceMatrix(FunctionalTrait, MaxModelTraitValue, MinModelTraitValue);

            return(RaoEntropy(Distances, CohortTotalBiomasses));
        }
        public double[] CalculateFunctionalRichness(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions, 
            List<uint[]> cellIndices, int cellIndex, string trait)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double MinCurrentTraitValue = double.MaxValue;
            double MaxCurrentTraitValue = double.MinValue;
            double MinModelTraitValue = 0.0;
            double MaxModelTraitValue = 0.0;

            switch (trait.ToLower())
            {
                case "biomass":

                    foreach (var CohortList in CellCohorts)
                    {
                        foreach (var cohort in CohortList)
                        {

                            if (cohort.IndividualBodyMass < MinCurrentTraitValue) MinCurrentTraitValue = cohort.IndividualBodyMass;

                            if (cohort.IndividualBodyMass > MaxCurrentTraitValue) MaxCurrentTraitValue = cohort.IndividualBodyMass;

                        }
                    }

                    //Define upper and lower limits for body mass
                    MinModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
                    MaxModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
                    break;
                case "trophic index":
                    foreach (var CohortList in CellCohorts)
                    {
                        foreach (var cohort in CohortList)
                        {

                            if (cohort.TrophicIndex < MinCurrentTraitValue) MinCurrentTraitValue = cohort.TrophicIndex;

                            if (cohort.TrophicIndex > MaxCurrentTraitValue) MaxCurrentTraitValue = cohort.TrophicIndex;

                        }
                    }

                    //Define upper and lower limits for body mass
                    MinModelTraitValue = MinTI;
                    MaxModelTraitValue = MaxTI;

                    break;
                default:
                    Debug.Fail("Trait not recognised in calculation of ecosystem metrics: " + trait);
                    break;
            }

            Debug.Assert((MaxModelTraitValue - MinModelTraitValue) > 0.0, "Division by zero or negative model trait values in calculation of functional richness");

            double[] NewArray = {(MaxCurrentTraitValue-MinCurrentTraitValue)/(MaxModelTraitValue-MinModelTraitValue),MinCurrentTraitValue,MaxCurrentTraitValue};

            return NewArray;
        }
        /// <summary>
        /// Calculate trophic evenness using the Rao Index
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of indices of cells to be run in the current simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to be run</param>
        /// <returns>Trophic evenness</returns>
        public double CalculateFunctionalEvennessRao(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions,
            List<uint[]> cellIndices, int cellIndex, string trait)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            double[] EvennessValues = new double[2];

            double[,] Distances = new double[CellCohorts.GetNumberOfCohorts(), CellCohorts.GetNumberOfCohorts()];

            double[] FunctionalTrait = new double[CellCohorts.GetNumberOfCohorts()];
            double MaxModelTraitValue=0;
            double MinModelTraitValue=0;

            // Construct a vector of cohort biomass (in case we want to weight by them)
            double[] CohortTotalBiomasses = new double[CellCohorts.GetNumberOfCohorts()];

            int CohortNumberCounter = 0;
            switch (trait.ToLower())
            {
                case "biomass":
                    for (int fg = 0; fg < CellCohorts.Count; fg++)
                    {
                        foreach (Cohort c in CellCohorts[fg])
                        {

                            FunctionalTrait[CohortNumberCounter] = c.IndividualBodyMass;
                            CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                            CohortNumberCounter++;
                        }
                    }

                    //Define upper and lower limits for body mass
                    MinModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
                    MaxModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
                    break;
                case "trophic index":
                    for (int fg = 0; fg < CellCohorts.Count; fg++)
                    {
                        foreach (Cohort c in CellCohorts[fg])
                        {

                            FunctionalTrait[CohortNumberCounter] = c.IndividualBodyMass;
                            CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                            CohortNumberCounter++;
                        }
                    }
                    MinModelTraitValue = MinTI;
                    MaxModelTraitValue = MaxTI;
                    break;
            }

            Distances = CalculateDistanceMatrix(FunctionalTrait, MaxModelTraitValue, MinModelTraitValue);

            return RaoEntropy(Distances, CohortTotalBiomasses);
        }
        /// <summary>
        /// Calculates functional diversity of cohorts in a grid cell as functional richness and functional diveregence (using the Rao Index)
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="cellIndices">The list of cell indices in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to run</param>
        /// <returns>A pair of values representing the functional richness and functional divergence (functional richness currently disabled!)</returns>
        public double[] CalculateFunctionalDiversity(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions, 
            List<uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            //Variable to hold the functional richness value for the current cohorts
            double FunctionalRichness;
            //Variable to hold the functional divergence value for the current cohorts
            double RaoFunctionalDivergence = 0.0;
            double[,] Distances= new double[CellCohorts.GetNumberOfCohorts(), CellCohorts.GetNumberOfCohorts()];

            List<string> AllTraitNames = cohortDefinitions.GetAllTraitNames().ToList();

            AllTraitNames.Remove("realm");
            AllTraitNames.Remove("heterotroph/autotroph");
            AllTraitNames.Remove("diet");
            string[] TraitNames = AllTraitNames.ToArray();

            //Define upper and lower limits for body mass
            double MinMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
            double MaxMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
            //Define upp and lower limits for trophic index
            double MaxTI = 40.0;
            double MinTI = 1.0;

            // Construct an array of functional trait values for each cohort
            // Rows are specific cohorts
            // Columns are the functional traits (these include different types:
            //      quantative: current mass, trophic index
            //      nominal: diet, reproductive strategy, mobility, metabolism
            Tuple<double[], string[]>[] CohortFunctionalTraits = new Tuple<double[], string[]>[CellCohorts.GetNumberOfCohorts()];
            double[] IndividualBodyMasses = new double[CellCohorts.GetNumberOfCohorts()];
            double[] TrophicIndex = new double[CellCohorts.GetNumberOfCohorts()];
            string[][] CohortNominalTraitValues= new string[TraitNames.Length][];

            for (int i = 0; i < TraitNames.Length; i++)
            {
                CohortNominalTraitValues[i] = new string[CellCohorts.GetNumberOfCohorts()];
            }

            // Construct a vector of cohort biomass (in case we want to weight by them)
            double[] CohortTotalBiomasses = new double[CellCohorts.GetNumberOfCohorts()];

            string[] TraitValues = new string[TraitNames.Length];
            double[] QuantitativeTraitValues= new double[2];
            int CohortNumberCounter = 0;
            for (int fg = 0; fg < CellCohorts.Count; fg++)
            {
                foreach (Cohort c in CellCohorts[fg])
                {
                    TraitValues = cohortDefinitions.GetTraitValues(TraitNames, fg);
                    for (int ii = 0; ii < TraitValues.Length; ii++)
                    {
                        CohortNominalTraitValues[ii][CohortNumberCounter] = TraitValues[ii];
                    }

                    IndividualBodyMasses[CohortNumberCounter] = c.IndividualBodyMass;
                    TrophicIndex[CohortNumberCounter] = c.TrophicIndex;

                    QuantitativeTraitValues[0] = c.IndividualBodyMass;
                    QuantitativeTraitValues[1] = c.TrophicIndex;

                    CohortFunctionalTraits[CohortNumberCounter] = new Tuple<double[], string[]>(QuantitativeTraitValues, TraitValues);

                    CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                    CohortNumberCounter++;
                }
            }

            List<double[,]> DistanceList = new List<double[,]>();

            DistanceList.Add(CalculateDistanceMatrix(IndividualBodyMasses, MaxMass, MinMass));
            DistanceList.Add(CalculateDistanceMatrix(TrophicIndex, MaxTI, MinTI));
            foreach (string[] t in CohortNominalTraitValues)
            {
                DistanceList.Add(CalculateDistanceMatrix(t));
            }

            Distances = CalculateAggregateDistance(DistanceList);

            RaoFunctionalDivergence = RaoEntropy(Distances, CohortTotalBiomasses);

            return new double[] {0.0,RaoFunctionalDivergence};
        }
        /// <summary>
        /// Calculates functional diversity of cohorts in a grid cell as functional richness and functional diveregence (using the Rao Index)
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="cellIndices">The list of cell indices in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to run</param>
        /// <returns>A pair of values representing the functional richness and functional divergence (functional richness currently disabled!)</returns>
        public double[] CalculateFunctionalDiversity(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions,
                                                     List <uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            //Variable to hold the functional richness value for the current cohorts
            double FunctionalRichness;
            //Variable to hold the functional divergence value for the current cohorts
            double RaoFunctionalDivergence = 0.0;

            double[,] Distances = new double[CellCohorts.GetNumberOfCohorts(), CellCohorts.GetNumberOfCohorts()];

            List <string> AllTraitNames = cohortDefinitions.GetAllTraitNames().ToList();

            AllTraitNames.Remove("realm");
            AllTraitNames.Remove("heterotroph/autotroph");
            AllTraitNames.Remove("diet");
            string[] TraitNames = AllTraitNames.ToArray();


            //Define upper and lower limits for body mass
            double MinMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
            double MaxMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
            //Define upp and lower limits for trophic index
            double MaxTI = 40.0;
            double MinTI = 1.0;

            // Construct an array of functional trait values for each cohort
            // Rows are specific cohorts
            // Columns are the functional traits (these include different types:
            //      quantative: current mass, trophic index
            //      nominal: diet, reproductive strategy, mobility, metabolism
            Tuple <double[], string[]>[] CohortFunctionalTraits = new Tuple <double[], string[]> [CellCohorts.GetNumberOfCohorts()];
            double[]   IndividualBodyMasses     = new double[CellCohorts.GetNumberOfCohorts()];
            double[]   TrophicIndex             = new double[CellCohorts.GetNumberOfCohorts()];
            string[][] CohortNominalTraitValues = new string[TraitNames.Length][];

            for (int i = 0; i < TraitNames.Length; i++)
            {
                CohortNominalTraitValues[i] = new string[CellCohorts.GetNumberOfCohorts()];
            }

            // Construct a vector of cohort biomass (in case we want to weight by them)
            double[] CohortTotalBiomasses = new double[CellCohorts.GetNumberOfCohorts()];


            string[] TraitValues             = new string[TraitNames.Length];
            double[] QuantitativeTraitValues = new double[2];
            int      CohortNumberCounter     = 0;

            for (int fg = 0; fg < CellCohorts.Count; fg++)
            {
                foreach (Cohort c in CellCohorts[fg])
                {
                    TraitValues = cohortDefinitions.GetTraitValues(TraitNames, fg);
                    for (int ii = 0; ii < TraitValues.Length; ii++)
                    {
                        CohortNominalTraitValues[ii][CohortNumberCounter] = TraitValues[ii];
                    }


                    IndividualBodyMasses[CohortNumberCounter] = c.IndividualBodyMass;
                    TrophicIndex[CohortNumberCounter]         = c.TrophicIndex;

                    QuantitativeTraitValues[0] = c.IndividualBodyMass;
                    QuantitativeTraitValues[1] = c.TrophicIndex;

                    CohortFunctionalTraits[CohortNumberCounter] = new Tuple <double[], string[]>(QuantitativeTraitValues, TraitValues);

                    CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                    CohortNumberCounter++;
                }
            }

            List <double[, ]> DistanceList = new List <double[, ]>();

            DistanceList.Add(CalculateDistanceMatrix(IndividualBodyMasses, MaxMass, MinMass));
            DistanceList.Add(CalculateDistanceMatrix(TrophicIndex, MaxTI, MinTI));
            foreach (string[] t in CohortNominalTraitValues)
            {
                DistanceList.Add(CalculateDistanceMatrix(t));
            }

            Distances = CalculateAggregateDistance(DistanceList);

            RaoFunctionalDivergence = RaoEntropy(Distances, CohortTotalBiomasses);

            return(new double[] { 0.0, RaoFunctionalDivergence });
        }
        public double[] CalculateFunctionalRichness(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions,
                                                    List <uint[]> cellIndices, int cellIndex, string trait)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double MinCurrentTraitValue       = double.MaxValue;
            double MaxCurrentTraitValue       = double.MinValue;
            double MinModelTraitValue         = 0.0;
            double MaxModelTraitValue         = 0.0;

            switch (trait.ToLower())
            {
            case "biomass":

                foreach (var CohortList in CellCohorts)
                {
                    foreach (var cohort in CohortList)
                    {
                        if (cohort.IndividualBodyMass < MinCurrentTraitValue)
                        {
                            MinCurrentTraitValue = cohort.IndividualBodyMass;
                        }

                        if (cohort.IndividualBodyMass > MaxCurrentTraitValue)
                        {
                            MaxCurrentTraitValue = cohort.IndividualBodyMass;
                        }
                    }
                }


                //Define upper and lower limits for body mass
                MinModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
                MaxModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
                break;

            case "trophic index":
                foreach (var CohortList in CellCohorts)
                {
                    foreach (var cohort in CohortList)
                    {
                        if (cohort.TrophicIndex < MinCurrentTraitValue)
                        {
                            MinCurrentTraitValue = cohort.TrophicIndex;
                        }

                        if (cohort.TrophicIndex > MaxCurrentTraitValue)
                        {
                            MaxCurrentTraitValue = cohort.TrophicIndex;
                        }
                    }
                }


                //Define upper and lower limits for body mass
                MinModelTraitValue = MinTI;
                MaxModelTraitValue = MaxTI;

                break;

            default:
                Debug.Fail("Trait not recognised in calculation of ecosystem metrics: " + trait);
                break;
            }

            Debug.Assert((MaxModelTraitValue - MinModelTraitValue) > 0.0, "Division by zero or negative model trait values in calculation of functional richness");

            double[] NewArray = { (MaxCurrentTraitValue - MinCurrentTraitValue) / (MaxModelTraitValue - MinModelTraitValue), MinCurrentTraitValue, MaxCurrentTraitValue };

            return(NewArray);
        }
        /// <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();
        }