/// <summary>
        /// Constructor for process tracker: Initialises the trackers for individual processes
        /// </summary>
        /// <param name="numTimesteps">The number of time steps in the model</param>
        /// <param name="lats">The latitudes of active grid cells in the model</param>
        /// <param name="lons">The longitudes of active grid cells in the model</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="Filenames">The filenames of the output files to write the tracking results to</param>
        /// <param name="trackProcesses">Whether to track processes</param>
        /// <param name="cohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="missingValue">The missing value to use in process tracking output files</param>
        /// <param name="outputFileSuffix">The suffix to be applied to output files from process tracking</param>
        /// <param name="outputPath">The path to the folder to be used for process tracking outputs</param>
        /// <param name="trackerMassBins">The mass bins to use for categorising output data in the process trackers</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        /// <param name="latCellSize">The size of grid cells latitudinally</param>
        /// <param name="lonCellSize">The size of grid cells longitudinally</param>
        public GlobalProcessTracker(uint numTimesteps,
            float[] lats, float[] lons,
            List<uint[]> cellIndices,
            SortedList<string, string> Filenames,
            Boolean trackProcesses,
            FunctionalGroupDefinitions cohortDefinitions,
            FunctionalGroupDefinitions stockDefinitions,
            double missingValue,
            string outputFileSuffix,
            string outputPath, MassBinsHandler trackerMassBins,
            Boolean specificLocations,
            MadingleyModelInitialisation initialisation,
            float latCellSize,
            float lonCellSize)
        {
            // Initialise trackers for ecological processes
            _TrackProcesses = trackProcesses;

            if (_TrackProcesses)
            {
                _TrackNPP = new GlobalNPPTracker(outputPath, lats.Length, lons.Length, lats, lons, latCellSize, lonCellSize,
                    (int)numTimesteps,stockDefinitions.GetNumberOfFunctionalGroups(),outputFileSuffix);

            }
        }
        /// <summary>
        /// Constructor for process tracker: Initialises the trackers for individual processes
        /// </summary>
        /// <param name="numTimesteps">The number of time steps in the model</param>
        /// <param name="lats">The latitudes of active grid cells in the model</param>
        /// <param name="lons">The longitudes of active grid cells in the model</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="Filenames">The filenames of the output files to write the tracking results to</param>
        /// <param name="trackProcesses">Whether to track processes</param>
        /// <param name="cohortDefinitions">The definitions for cohort functional groups in the model</param>
        /// <param name="missingValue">The missing value to use in process tracking output files</param>
        /// <param name="outputFileSuffix">The suffix to be applied to output files from process tracking</param>
        /// <param name="outputPath">The path to the folder to be used for process tracking outputs</param>
        /// <param name="trackerMassBins">The mass bins to use for categorising output data in the process trackers</param>
        /// <param name="specificLocations">Whether the model is being run for specific locations</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        /// <param name="latCellSize">The size of grid cells latitudinally</param>
        /// <param name="lonCellSize">The size of grid cells longitudinally</param>
        public GlobalProcessTracker(uint numTimesteps,
                                    float[] lats, float[] lons,
                                    List <uint[]> cellIndices,
                                    SortedList <string, string> Filenames,
                                    Boolean trackProcesses,
                                    FunctionalGroupDefinitions cohortDefinitions,
                                    FunctionalGroupDefinitions stockDefinitions,
                                    double missingValue,
                                    string outputFileSuffix,
                                    string outputPath, MassBinsHandler trackerMassBins,
                                    Boolean specificLocations,
                                    MadingleyModelInitialisation initialisation,
                                    float latCellSize,
                                    float lonCellSize)
        {
            // Initialise trackers for ecological processes
            _TrackProcesses = trackProcesses;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            tempVector = _CellEnvironment["Temperature"];

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

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

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

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

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

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

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

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

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

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

            // Initialise the grid cell cohort and stock handlers
            _GridCellCohorts = new GridCellCohortHandler(cohortFunctionalGroups.GetNumberOfFunctionalGroups());
            _GridCellStocks = new GridCellStockHandler(stockFunctionalGroups.GetNumberOfFunctionalGroups());
        }
        /// <summary>
        /// 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();
        }
        public void OutputCurrentModelState(ModelGrid currentModelGrid, FunctionalGroupDefinitions functionalGroupHandler, List<uint[]> cellIndices, uint currentTimestep, int maximumNumberOfCohorts, string filename)
        {

            float[] Latitude = currentModelGrid.Lats;

            float[] Longitude = currentModelGrid.Lons;

            float[] CohortFunctionalGroup = new float[functionalGroupHandler.GetNumberOfFunctionalGroups()];
            for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
            {
                CohortFunctionalGroup[fg] = fg;
            }

            int CellCohortNumber = 0;
            GridCellCohortHandler CellCohorts;
            for (int cellIndex = 0; cellIndex < cellIndices.Count; cellIndex++)
            {
                CellCohorts = currentModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
                for (int i = 0; i < CellCohorts.Count; i++)
                {
                    if (CellCohorts[i].Count > CellCohortNumber) CellCohortNumber = CellCohorts[i].Count;
                }
            }

            int MaxNumberCohorts = Math.Max(CellCohortNumber, maximumNumberOfCohorts);

            float[] Cohort = new float[MaxNumberCohorts];
            for (int c = 0; c < Cohort.Length; c++)
            {
                Cohort[c] = c;
            }

            //Define an array for stock functional group - there are only three currently
            float[] StockFunctionalGroup = new float[] { 1, 2, 3 };

            //Define an array for index of stocks - there is only one currently
            float[] Stock = new float[] { 1 };

            string Filename = filename + "_" + currentTimestep.ToString() + Simulation.ToString();

            StateOutput = SDSCreator.CreateSDS("netCDF", Filename, _OutputPath);

            //Define the cohort properties for output
            string[] CohortProperties = new string[]
            {"JuvenileMass", "AdultMass", "IndividualBodyMass", "CohortAbundance",
             "BirthTimeStep", "MaturityTimeStep", "LogOptimalPreyBodySizeRatio",
             "MaximumAchievedBodyMass","Merged","TrophicIndex","ProportionTimeActive"};

            //define the dimensions for cohort outputs
            string[] dims = new string[] { "Latitude", "Longitude", "Cohort Functional Group", "Cohort" };

            // Add the variables for each cohort property
            // Then calculate the state for this property and put the data to this variable
            foreach (string v in CohortProperties)
            {
                DataConverter.AddVariable(StateOutput, "Cohort" + v, 4,
                dims, currentModelGrid.GlobalMissingValue, Latitude,
                Longitude, CohortFunctionalGroup, Cohort);

                StateOutput.PutData<double[, , ,]>("Cohort" + v,
                    CalculateCurrentCohortState(currentModelGrid, v, Latitude.Length, Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length, cellIndices));

                StateOutput.Commit();
            }

            //Define the stock properties for output
            string[] StockProperties = new string[] { "IndividualBodyMass", "TotalBiomass" };

            //define the dimensions for cohort outputs
            dims = new string[] { "Latitude", "Longitude", "Stock Functional Group", "Stock" };

            // Add the variables for each stock property
            // Then calculate the state for this property and put the data to this variable
            foreach (string v in StockProperties)
            {
                DataConverter.AddVariable(StateOutput, "Stock" + v, 4,
                dims, currentModelGrid.GlobalMissingValue, Latitude,
                Longitude, StockFunctionalGroup, Stock);

                StateOutput.PutData<double[, , ,]>("Stock" + v,
                    CalculateCurrentStockState(currentModelGrid, v, Latitude.Length, Longitude.Length, StockFunctionalGroup.Length, Stock.Length, cellIndices));

                StateOutput.Commit();
            }

            //Close this data set
            StateOutput.Dispose();
        }
        public void OutputCurrentModelState(ModelGrid currentModelGrid, FunctionalGroupDefinitions functionalGroupHandler, List <uint[]> cellIndices, uint currentTimestep, int maximumNumberOfCohorts, string filename)
        {
            float[] Latitude = currentModelGrid.Lats;

            float[] Longitude = currentModelGrid.Lons;

            float[] CohortFunctionalGroup = new float[functionalGroupHandler.GetNumberOfFunctionalGroups()];
            for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
            {
                CohortFunctionalGroup[fg] = fg;
            }

            int CellCohortNumber = 0;
            GridCellCohortHandler CellCohorts;

            for (int cellIndex = 0; cellIndex < cellIndices.Count; cellIndex++)
            {
                CellCohorts = currentModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
                for (int i = 0; i < CellCohorts.Count; i++)
                {
                    if (CellCohorts[i].Count > CellCohortNumber)
                    {
                        CellCohortNumber = CellCohorts[i].Count;
                    }
                }
            }

            int MaxNumberCohorts = Math.Max(CellCohortNumber, maximumNumberOfCohorts);

            float[] Cohort = new float[MaxNumberCohorts];
            for (int c = 0; c < Cohort.Length; c++)
            {
                Cohort[c] = c;
            }

            //Define an array for stock functional group - there are only three currently
            float[] StockFunctionalGroup = new float[] { 1, 2, 3 };

            //Define an array for index of stocks - there is only one currently
            float[] Stock = new float[] { 1 };

            string Filename = filename + "_" + currentTimestep.ToString() + Simulation.ToString();

            StateOutput = SDSCreator.CreateSDS("netCDF", Filename, _OutputPath);

            //Define the cohort properties for output
            string[] CohortProperties = new string[]
            { "JuvenileMass", "AdultMass", "IndividualBodyMass", "IndividualReproductivePotentialMass", "CohortAbundance",
              "BirthTimeStep", "MaturityTimeStep", "LogOptimalPreyBodySizeRatio",
              "MaximumAchievedBodyMass", "Merged", "TrophicIndex", "ProportionTimeActive" };

            //define the dimensions for cohort outputs
            string[] dims = new string[] { "Latitude", "Longitude", "Cohort Functional Group", "Cohort" };

            // Add the variables for each cohort property
            // Then calculate the state for this property and put the data to this variable
            foreach (string v in CohortProperties)
            {
                DataConverter.AddVariable(StateOutput, "Cohort" + v, 4,
                                          dims, currentModelGrid.GlobalMissingValue, Latitude,
                                          Longitude, CohortFunctionalGroup, Cohort);

                StateOutput.PutData <double[, , , ]>("Cohort" + v,
                                                     CalculateCurrentCohortState(currentModelGrid, v, Latitude.Length, Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length, cellIndices));

                StateOutput.Commit();
            }

            //Define the stock properties for output
            string[] StockProperties = new string[] { "IndividualBodyMass", "TotalBiomass" };


            //define the dimensions for cohort outputs
            dims = new string[] { "Latitude", "Longitude", "Stock Functional Group", "Stock" };

            // Add the variables for each stock property
            // Then calculate the state for this property and put the data to this variable
            foreach (string v in StockProperties)
            {
                DataConverter.AddVariable(StateOutput, "Stock" + v, 4,
                                          dims, currentModelGrid.GlobalMissingValue, Latitude,
                                          Longitude, StockFunctionalGroup, Stock);

                StateOutput.PutData <double[, , , ]>("Stock" + v,
                                                     CalculateCurrentStockState(currentModelGrid, v, Latitude.Length, Longitude.Length, StockFunctionalGroup.Length, Stock.Length, cellIndices));

                StateOutput.Commit();
            }

            //Close this data set
            StateOutput.Dispose();
        }
        /// <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("");
        }
예제 #9
0
        public void SaveTimestep(int currentTimestep)
        {
            UInt32 hh = this.CurrentTimeStep;

            MadingleyModelInitialisation initialisation = this.ModelInitialisation;

            // Temporary variables
            Boolean varExists;

            // For runs with specific locations and where track processes has been specified, write out mass flows data and reset the mass flow tracker 
            // for the next time step
            if (SpecificLocations)
            {
                for (var cellIndex = 0; cellIndex < _CellList.Count; cellIndex++)
                {
                    if (ProcessTrackers[cellIndex].TrackProcesses)
                    {
                        ProcessTrackers[cellIndex].EndTimeStepPredationTracking(CurrentTimeStep);
                        ProcessTrackers[cellIndex].EndTimeStepHerbvioryTracking(CurrentTimeStep);
                    }
                }
            }

            if (TrackGlobalProcesses.TrackProcesses)
            {
                for (uint ii = 0; ii < StockFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); ii++)
                {
                    TrackGlobalProcesses.StoreNPPGrid(hh, ii);
                    TrackGlobalProcesses.StoreHANPPGrid(hh, ii);
                }
            }
#endif
            OutputTimer.Start();

            // Write the global outputs for this time step
            GlobalOutputs.TimeStepOutputs(EcosystemModelGrid, CurrentTimeStep, CurrentMonth, TimeStepTimer, CohortFunctionalGroupDefinitions,
                StockFunctionalGroupDefinitions, _CellList, GlobalDiagnosticVariables, initialisation);

            OutputTimer.Stop();
            Console.WriteLine("Global Outputs took: {0}", OutputTimer.GetElapsedTimeSecs());

            OutputTimer.Start();

            if (SpecificLocations)
            {
                // Loop over grid cells and write (a) time step outputs, and (b) trophic flow data (if process tracking is on)
                for (int i = 0; i < _CellList.Count; i++)
                {
                    // Write out the grid cell outputs for this time step
                    CellOutputs[i].TimeStepOutputs(EcosystemModelGrid, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions,
                        _CellList, i, GlobalDiagnosticVariables, TimeStepTimer, NumTimeSteps, CurrentTimeStep, initialisation, CurrentMonth, EcosystemModelGrid.GetEnviroLayer("Realm", 0, _CellList[i][0], _CellList[i][1], out varExists) == 2.0);

                    // Write out trophic flow data for this time step
                    if (ProcessTrackers[i].TrackProcesses) ProcessTrackers[i].WriteTimeStepTrophicFlows(CurrentTimeStep, EcosystemModelGrid.NumLatCells, EcosystemModelGrid.NumLonCells, initialisation,
                         EcosystemModelGrid.GetEnviroLayer("Realm", 0, _CellList[i][0], _CellList[i][1], out varExists) == 2.0);

                }
            }
            else
            {
                // Write out grid outputs for this time step
                GridOutputs.TimeStepOutputs(EcosystemModelGrid, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, _CellList,
                    CurrentTimeStep, initialisation);
            }

            OutputTimer.Stop();
            Console.WriteLine("Cell/Grid Outputs took: {0}", OutputTimer.GetElapsedTimeSecs());

            // Write the results of dispersal to the console
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Number of cohorts that dispersed this time step: {0}\n", Dispersals);
            Console.ForegroundColor = ConsoleColor.White;
            Dispersals = 0;

            if (OutputModelStateTimestep.Contains(hh))
            {
                OutputTimer.Start();
                Console.WriteLine("Outputting model state");

                //Writing to text based output
                WriteModelState.OutputCurrentModelState(EcosystemModelGrid, _CellList, hh);
                WriteModelState.OutputCurrentModelState(EcosystemModelGrid, CohortFunctionalGroupDefinitions, _CellList, CurrentTimeStep, initialisation.MaxNumberOfCohorts, "ModelState");

                OutputTimer.Stop();
                // Write the results of dispersal to the console
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("Writing model state took: {0}", OutputTimer.GetElapsedTimeSecs());
                Console.ForegroundColor = ConsoleColor.White;

                /*ReadModelState = new InputModelState(
                    initialisation.OutputPath,
                    "ModelState0");*/

            }
        }