/// <summary>
        /// Constructor for the autotroph processor: initialises necessary classes
        /// </summary>
        public AutotrophProcessor()
        {
            _PhytoplanktonConversionRatio = EcologicalParameters.Parameters["AutotrophProcessor.ConvertNPPtoAutotroph.PhytoplanktonConversionRatio"];

            // Initialise the utility functions
            Utilities = new UtilityFunctions();
        }
        /// <summary>
        /// Constructor for dispersal: assigns all parameter values
        /// </summary>
        public AdvectiveDispersal(string globalModelTimeStepUnit, Boolean DrawRandomly)
        {
            InitialiseParatemersAdvectiveDispersal();

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

            // Calculate the scalar to convert from the time step units used by this implementation of dispersal to the global model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);

            // Initialise the advective dispersal temporal scaling to adjust between time steps appropriately
            _AdvectionTimeStepsPerModelTimeStep = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, "day") * 24 / _AdvectiveModelTimeStepLengthHours;

            // Convert velocity from m/s to km/month. Note that if the _TimeUnitImplementation changes, this will also have to change.
            VelocityUnitConversion = 60 * 60 * 24 * Utilities.ConvertTimeUnits(globalModelTimeStepUnit, "day") * _DeltaT / 1000;

            // Set the seed for the random number generator
            RandomNumberGenerator = new NonStaticSimpleRNG();
            if (DrawRandomly)
            {
                RandomNumberGenerator.SetSeedFromSystemTime();
            }
            else
            {
                RandomNumberGenerator.SetSeed(14141);
            }
        }
        /// <summary>
        /// Constructor for starvation mortality: assigns all parameter values
        /// 
        /// </summary>
        public StarvationMortality(string globalModelTimeStepUnit)
        {
            InitialiseParametersStarvationMortality();

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

            // Calculate the scalar to convert from the time step units used by this implementation of mortality to the global  model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);
        }
        /// <summary>
        /// Constructor for metabolism: assigns all parameter values
        /// </summary>
        public MetabolismEndotherm(string globalModelTimeStepUnit)
        {
            // Initialise ecological parameters for metabolism
            InitialiseMetabolismParameters();

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

            // Calculate the scalar to convert from the time step units used by this implementation of metabolism to the global  model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);
        }
        /// <summary>
        /// Constructor for metabolism: assigns all parameter values
        /// </summary>
        public MetabolismHeterotroph(string globalModelTimeStepUnit)
        {
            // Initialise ecological parameters for metabolism
            InitialiseMetabolismParameters();

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

            // Calculate the scalar to convert from the time step units used by this implementation of metabolism to the global  model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);
            
            // Set the constant to convert temperature in degrees Celsius to Kelvin
            _TemperatureUnitsConvert = 273.0;
        }
Beispiel #6
0
        /// <summary>
        /// Constructor for dispersal: assigns all parameter values
        /// </summary>
        public DiffusiveDispersal(string globalModelTimeStepUnit, Boolean DrawRandomly)
        {
            InitialiseParametersDiffusiveDispersal();

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

            // Calculate the scalar to convert from the time step units used by this implementation of dispersal to the global model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);

            // Set the seed for the random number generator
            RandomNumberGenerator = new NonStaticSimpleRNG();
            if (DrawRandomly)
            {
                RandomNumberGenerator.SetSeedFromSystemTime();
            }
            else
            {
                RandomNumberGenerator.SetSeed(14141);
            }
        }
        /// <summary>
        /// Assigns all parameter values for repsonsive dispersal
        /// </summary>
        public ResponsiveDispersal(string globalModelTimeStepUnit, Boolean DrawRandomly)
        {
            InitialiseParametersResponsiveDispersal();

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

            // Calculate the scalar to convert from the time step units used by this implementation of dispersal to the global model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);

            // Set the seed for the random number generator
            RandomNumberGenerator = new NonStaticSimpleRNG();
            if (DrawRandomly)
            {
                RandomNumberGenerator.SetSeedFromSystemTime();
            }
            else
            {
                RandomNumberGenerator.SetSeed(14141);
            }
        }
        /// <summary>
        /// Constructor for reproduction: assigns all parameter values
        /// <param name="globalModelTimeStepUnit">The time step of the global model</param>
        /// <param name="drawRandomly">Indicates whether to draw values randomly</param>
        /// </summary>
        public ReproductionBasic(string globalModelTimeStepUnit, Boolean drawRandomly)
        {
            // Initialise ecological parameters for reproduction
            InitialiseReproductionParameters();

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

            // Calculate the scalar to convert from the time step units used by this implementation of herbivory to the global model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);

            // Instantiate the random number generator
            RandomNumberGenerator = new NonStaticSimpleRNG();

            // Set the seed for the random number generator
            if (drawRandomly)
            {
                RandomNumberGenerator.SetSeedFromSystemTime();
            }
            else
            {
                RandomNumberGenerator.SetSeed(4000);
            }
        }
Beispiel #9
0
        /// <summary>
        /// Constructor for reproduction: assigns all parameter values
        /// <param name="globalModelTimeStepUnit">The time step of the global model</param>
        /// <param name="drawRandomly">Indicates whether to draw values randomly</param>
        /// </summary>
        public ReproductionBasic(string globalModelTimeStepUnit, Boolean drawRandomly)
        {
            // Initialise ecological parameters for reproduction
            InitialiseReproductionParameters();

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

            // Calculate the scalar to convert from the time step units used by this implementation of herbivory to the global model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);

            // Instantiate the random number generator
            RandomNumberGenerator = new NonStaticSimpleRNG();

            // Set the seed for the random number generator
            if (drawRandomly)
            {
                RandomNumberGenerator.SetSeedFromSystemTime();
            }
            else
            {
                RandomNumberGenerator.SetSeed(4000);
            }
        }
        /// <summary>
        /// Overloaded constructor for model grid to construct the grid for specific locations
        /// </summary>
        /// <param name="minLat">Minimum grid latitude (degrees)</param>
        /// <param name="minLon">Minimum grid longitude (degrees, currently -180 to 180)</param>
        /// <param name="maxLat">Maximum grid latitude (degrees)</param>
        /// <param name="maxLon">Maximum grid longitude (degrees, currently -180 to 180)</param>
        /// <param name="latCellSize">Latitudinal size of grid cells</param>
        /// <param name="lonCellSize">Longitudinal size of grid cells</param>
        /// <param name="cellList">List of indices of active cells in the model grid</param>
        /// <param name="enviroStack">List of environmental data layers</param>
        /// <param name="cohortFunctionalGroups">The functional group definitions for cohorts in the model</param>
        /// <param name="stockFunctionalGroups">The functional group definitions for stocks in the model</param>
        /// <param name="globalDiagnostics">Global diagnostic variables</param>
        /// <param name="tracking">Whether process tracking is enabled</param>
        /// <param name="specificLocations">Whether the model is to be run for specific locations</param>
        /// <param name="runInParallel">Whether model grid cells will be run in parallel</param>
        public ModelGrid(float minLat, float minLon, float maxLat, float maxLon, float latCellSize, float lonCellSize, List<uint[]> cellList, 
            SortedList<string, EnviroData> enviroStack, FunctionalGroupDefinitions cohortFunctionalGroups,
            FunctionalGroupDefinitions stockFunctionalGroups, SortedList<string, double> globalDiagnostics, Boolean tracking, 
            Boolean specificLocations, Boolean runInParallel)
        {
            // Add one to the counter of the number of grids. If there is more than one model grid, exit the program with a debug crash.
            NumGrids = NumGrids + 1;
            //Debug.Assert(NumGrids < 2, "You have initialised more than one grid on which to apply models. At present, this is not supported");

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

            // CURRENTLY DEFINING MODEL CELLS BY BOTTOM LEFT CORNER
            _MinLatitude = minLat;
            _MinLongitude = minLon;
            _MaxLatitude = maxLat;
            _MaxLongitude = maxLon;
            _LatCellSize = latCellSize;
            _LonCellSize = lonCellSize;
            _GridCellRarefaction = 1;

            // Check to see if the number of grid cells is an integer
            Debug.Assert((((_MaxLatitude - _MinLatitude) % _LatCellSize) == 0), "Error: number of grid cells is non-integer: check cell size");

            _NumLatCells = (UInt32)((_MaxLatitude - _MinLatitude) / _LatCellSize);
            _NumLonCells = (UInt32)((_MaxLongitude - _MinLongitude) / _LonCellSize);
            _Lats = new float[_NumLatCells];
            _Lons = new float[_NumLonCells];

            // Set up latitude and longitude vectors - lower left
            for (int ii = 0; ii < _NumLatCells; ii++)
            {
                _Lats[ii] = _MinLatitude + ii * _LatCellSize;
            }
            for (int jj = 0; jj < _NumLonCells; jj++)
            {
                _Lons[jj] = _MinLongitude + jj * _LonCellSize;
            }

            // Set up a grid of grid cells
            InternalGrid = new GridCell[_NumLatCells, _NumLonCells];

            // Instantiate the arrays of lists of cohorts to disperse
            DeltaFunctionalGroupDispersalArray = new List<uint>[_NumLatCells, _NumLonCells];
            DeltaCohortNumberDispersalArray = new List<uint>[_NumLatCells, _NumLonCells];

            // Instantiate the array of lists of grid cells to disperse those cohorts to
            DeltaCellToDisperseToArray = new List<uint[]>[_NumLatCells, _NumLonCells];

            // Instantiate the arrays of cell entry and exit directions
            DeltaCellExitDirection = new List<uint>[_NumLatCells, _NumLonCells];
            DeltaCellEntryDirection = new List<uint>[_NumLatCells, _NumLonCells];

            // An array of lists of cells to which organisms in each cell can disperse to; includes all cells which contribute to the
            // perimeter list, plus diagonal cells if they are in the same realm
            CellsForDispersal = new List<uint[]>[_NumLatCells, _NumLonCells];

            // An array of lists of directions corresponding to cells which organisms can disperse to
            CellsForDispersalDirection = new List<uint>[_NumLatCells, _NumLonCells];

            Console.WriteLine("Initialising grid cell environment:");

            int Count = 0;

            int NCells = cellList.Count;

            if (!runInParallel)
            {
                // Loop over cells to set up the model grid
                for (int ii = 0; ii < cellList.Count; ii++)
                {
                    // Create the grid cell at the specified position
                    InternalGrid[cellList[ii][0], cellList[ii][1]] = new GridCell(_Lats[cellList[ii][0]], cellList[ii][0],
                        _Lons[cellList[ii][1]], cellList[ii][1], latCellSize, lonCellSize, enviroStack, _GlobalMissingValue,
                        cohortFunctionalGroups, stockFunctionalGroups, globalDiagnostics, tracking, specificLocations);
                    if (!specificLocations)
                    {
                        CellsForDispersal[cellList[ii][0], cellList[ii][1]] = new List<uint[]>();
                        CellsForDispersalDirection[cellList[ii][0], cellList[ii][1]] = new List<uint>();
                        Count++;
                        Console.Write("\rInitialised {0} of {1}", Count, NCells);
                    }
                    else
                    {
                        Console.Write("\rRow {0} of {1}", ii + 1, NumLatCells / GridCellRarefaction);
                        Console.WriteLine("");
                        Console.WriteLine("");
                    }
                }
            }
            else
            {

                // Run a parallel loop over rows

                Parallel.For(0, NCells, ii =>
                {
                    // Create the grid cell at the specified position
                    InternalGrid[cellList[ii][0], cellList[ii][1]] = new GridCell(_Lats[cellList[ii][0]], cellList[ii][0],
                        _Lons[cellList[ii][1]], cellList[ii][1], latCellSize, lonCellSize, enviroStack, _GlobalMissingValue,
                        cohortFunctionalGroups, stockFunctionalGroups, globalDiagnostics, tracking, specificLocations);
                    if (!specificLocations)
                    {
                        CellsForDispersal[cellList[ii][0], cellList[ii][1]] = new List<uint[]>();
                        CellsForDispersalDirection[cellList[ii][0], cellList[ii][1]] = new List<uint>();
                    }

                    Count++;
                    Console.Write("\rInitialised {0} of {1}", Count, NCells);
                }
                 );

            }

            if (!specificLocations)
            {
                InterpolateMissingValues();

                // Fill in the array of dispersable perimeter lengths for each grid cell
                CalculatePerimeterLengthsAndCellsDispersableTo();

                CellHeightsKm = new double[_Lats.Length];
                CellWidthsKm = new double[_Lats.Length];

                // Calculate the lengths of widths of grid cells in each latitudinal strip
                // Assume that we are at the midpoint of each cell when calculating lengths
                for (int ii = 0; ii < _Lats.Length; ii++)
                {
                    CellHeightsKm[ii] = Utilities.CalculateLengthOfDegreeLatitude(_Lats[ii] + _LatCellSize / 2) * _LatCellSize;
                    CellWidthsKm[ii] = Utilities.CalculateLengthOfDegreeLongitude(_Lats[ii] + _LatCellSize / 2) * _LonCellSize;
                }
            }

            Console.WriteLine("\n");
        }
        /// <summary>
        /// Initializes the ecosystem model
        /// </summary>
        /// <param name="initialisation">An instance of the model initialisation class</param> 
        /// <param name="scenarioParameters">The parameters for the scenarios to run</param>
        /// <param name="scenarioIndex">The index of the scenario being run</param>
        /// <param name="outputFilesSuffix">The suffix to be applied to all outputs from this model run</param>
        /// <param name="globalModelTimeStepUnit">The time step unit used in the model</param>
        /// <param name="simulation">The index of the simulation being run</param>
        public MadingleyModel(MadingleyModelInitialisation initialisation, ScenarioParameterInitialisation scenarioParameters, int scenarioIndex,
            string outputFilesSuffix, string globalModelTimeStepUnit, int simulation)
        {
            // Assign the properties for this model run
            AssignModelRunProperties(initialisation, scenarioParameters, scenarioIndex, outputFilesSuffix);

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

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

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

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

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

            // Temporary variables
            Boolean varExists;

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

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

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

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

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

            if (SpecificLocations) initialisation.RunRealm = "";

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

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

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

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

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

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

            // Initialise the harvesting impacts class
            HarvestingSimulator = new Harvesting(EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, (float)EcosystemModelGrid.LatCellSize);
        }
        /// <summary>
        /// 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>
 /// Constructor for human appropriation of autotroph matter
 /// </summary>
 public HumanAutotrophMatterAppropriation()
 {
     _Utilities = new UtilityFunctions();
 }
        /// <summary>
        /// Constructor for herbivory: assigns all parameter values
        /// </summary>
        /// <param name="cellArea">The area (in square km) of the grid cell</param>
        /// <param name="globalModelTimeStepUnit">The time step unit used in the model</param>
        public RevisedHerbivory(double cellArea, string globalModelTimeStepUnit)
        {
            InitialiseParametersHerbivory();

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

            // Calculate the scalar to convert from the time step units used by this implementation of herbivory to the global model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);

            // Store the specified cell area in this instance of this herbivory implementation
            _CellArea = cellArea;
            _CellAreaHectares = cellArea * 100;
        }
        /// <summary>
        /// Constructor for model grid: assigns grid properties and initialises the grid cells
        /// </summary>
        /// <param name="minLat">Minimum grid latitude (degrees)</param>
        /// <param name="minLon">Minimum grid longitude (degrees, currently -180 to 180)</param>
        /// <param name="maxLat">Maximum grid latitude (degrees)</param>
        /// <param name="maxLon">Maximum grid longitude (degrees, currently -180 to 180)</param>
        /// <param name="latCellSize">Latitudinal resolution of grid cell</param>
        /// <param name="lonCellSize">Longitudinal resolution of grid cell</param>
        /// <param name="cellRarefaction">The rarefaction to be applied to active grid cells in the model</param>
        /// <param name="enviroStack">Environmental data layers</param>
        /// <param name="cohortFunctionalGroups">The functional group definitions for cohorts in the model</param>
        /// <param name="stockFunctionalGroups">The functional group definitions for stocks in the model</param>
        /// <param name="globalDiagnostics">Global daignostic variables</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="specificLocations">Whether the model is to be run for specific locations</param>
        public ModelGrid(float minLat, float minLon,float maxLat,float maxLon,float latCellSize,float lonCellSize, 
            SortedList<string,EnviroData> enviroStack, FunctionalGroupDefinitions cohortFunctionalGroups, FunctionalGroupDefinitions
            stockFunctionalGroups, SortedList<string, double> globalDiagnostics, Boolean tracking, Boolean DrawRandomly, 
            Boolean specificLocations, string globalModelTimeStepUnit)
        {
            // Add one to the counter of the number of grids. If there is more than one model grid, exit the program with a debug crash.
            NumGrids = NumGrids + 1;
            //Debug.Assert(NumGrids < 2, "You have initialised more than one grid on which to apply models. At present, this is not supported");

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

            // Seed the random number generator
            // Set the seed for the random number generator
            RandomNumberGenerator = new NonStaticSimpleRNG();
            if (DrawRandomly)
            {
                RandomNumberGenerator.SetSeedFromSystemTime();
            }
            else
            {
                RandomNumberGenerator.SetSeed(4315);
            }

            // CURRENTLY DEFINING MODEL CELLS BY BOTTOM LEFT CORNER
            _MinLatitude = minLat;
            _MinLongitude = minLon;
            _MaxLatitude = maxLat;
            _MaxLongitude = maxLon;
            _LatCellSize = latCellSize;
            _LonCellSize = lonCellSize;

            // Check to see if the number of grid cells is an integer
            Debug.Assert((((_MaxLatitude - _MinLatitude) % _LatCellSize) == 0), "Error: number of grid cells is non-integer: check cell size");

            _NumLatCells = (UInt32)((_MaxLatitude - _MinLatitude) / _LatCellSize);
            _NumLonCells = (UInt32)((_MaxLongitude - _MinLongitude) / _LonCellSize);
            _Lats = new float[_NumLatCells];
            _Lons = new float[_NumLonCells];

            // Set up latitude and longitude vectors - lower left
            for (int ii = 0; ii < _NumLatCells; ii++)
            {
                _Lats[ii] = _MinLatitude + ii * _LatCellSize;
            }
            for (int jj = 0; jj < _NumLonCells; jj++)
            {
                _Lons[jj] = _MinLongitude + jj * _LonCellSize;
            }

            // Instantiate a grid of grid cells
            InternalGrid = new GridCell[_NumLatCells, _NumLonCells];

            // Instantiate the arrays of lists of cohorts to disperse
            DeltaFunctionalGroupDispersalArray = new List<uint>[_NumLatCells, _NumLonCells];
            DeltaCohortNumberDispersalArray = new List<uint>[_NumLatCells, _NumLonCells];

            // Instantiate the array of lists of grid cells to disperse those cohorts to
            DeltaCellToDisperseToArray = new List<uint[]>[_NumLatCells, _NumLonCells];

            // Instantiate the arrays of cell entry and exit directions
            DeltaCellExitDirection = new List<uint>[_NumLatCells, _NumLonCells];
            DeltaCellEntryDirection = new List<uint>[_NumLatCells, _NumLonCells];

            // An array of lists of cells to which organisms in each cell can disperse to; includes all cells which contribute to the
            // perimeter list, plus diagonal cells if they are in the same realm
            CellsForDispersal = new List<uint[]>[_NumLatCells, _NumLonCells];

            // An array of lists of directions corresponding to cells which organisms can disperse to
            CellsForDispersalDirection = new List<uint>[_NumLatCells, _NumLonCells];

            Console.WriteLine("Initialising grid cell environment:");

            // Loop through to set up model grid
            for (int ii = 0; ii < _NumLatCells; ii+=GridCellRarefaction)
            {
                for (int jj = 0; jj < _NumLonCells; jj+=GridCellRarefaction)
                {
                    InternalGrid[ii, jj] = new GridCell(_Lats[ii],(uint)ii, _Lons[jj],(uint)jj, LatCellSize, LonCellSize, enviroStack,
                        GlobalMissingValue, cohortFunctionalGroups, stockFunctionalGroups, globalDiagnostics, tracking, specificLocations,globalModelTimeStepUnit);
                    CellsForDispersal[ii,jj] = new List<uint[]>();
                    CellsForDispersalDirection[ii, jj] = new List<uint>();
                    Console.Write("\rRow {0} of {1}", ii+1, NumLatCells/GridCellRarefaction);
                }
            }
            Console.WriteLine("");
            Console.WriteLine("");

            InterpolateMissingValues(globalModelTimeStepUnit);

            // Fill in the array of dispersable perimeter lengths for each grid cell
            CalculatePerimeterLengthsAndCellsDispersableTo();

            CellHeightsKm = new double[_Lats.Length];
            CellWidthsKm = new double[_Lats.Length];

            // Calculate the lengths of widths of grid cells in each latitudinal strip
            // Assume that we are at the midpoint of each cell when calculating lengths
            for (int ii = 0; ii < _Lats.Length; ii++)
            {
                 CellHeightsKm[ii] = Utilities.CalculateLengthOfDegreeLatitude(_Lats[ii] + _LatCellSize / 2) * _LatCellSize;
                 CellWidthsKm[ii] = Utilities.CalculateLengthOfDegreeLongitude(_Lats[ii] + _LatCellSize / 2) * _LonCellSize;
            }
        }
 /// <summary>
 /// Constructor for human appropriation of autotroph matter
 /// </summary>
 public HumanAutotrophMatterAppropriation()
 {
     _Utilities = new UtilityFunctions();
 }
        /// <summary>
        /// Constructor for EnviroData
        /// </summary>
        /// <param name="fileName">Filename (including extension)</param>
        /// <param name="dataName">The name of the variable that contains the data within the specified file</param>
        /// <param name="dataType">Type of data, nc = NetCDF, ascii = ESRI ASCII)</param>
        /// <param name="dataResolution">The temporal resolution of the environmental variable</param>
        /// <param name="units">The units of the data</param>
        /// <todo>Check whether lat/lon or 0/1 are fixed for all NetCDFs</todo>
        /// <todo>CHECK IF DIMENSIONS HAVE TO BE THE SAME FOR ALL VARIABLES IN A NETCDF AND HOW TO EXTRACT DIMENSIONS FOR A SINGLE VARIABLE IF NECESSARY</todo>
        /// <todo>Write code to check for equal cell sizes in NetCDFs</todo>
        public EnviroData(string fileName, string dataName, string dataType, string dataResolution, string units)
        {
            // Initialise the utility functions
            Utilities = new UtilityFunctions();

            // Temporary array to hold environmental data
            double[,] tempDoubleArray;

            // Temporary vectors to hold dimension data
            Single[] tempSingleVector;
            Int32[] tempInt32Vector;
            Int16[] tempInt16Vector;

            // Vectors of possible names of the dimension variables to search in the files for
            string[] LonSearchStrings = new string[] { "lon", "Lon", "longitude", "Longitude", "lons", "Lons", "long", "Long", "longs", "Longs", "longitudes", "Longitudes", "x", "X" };
            string[] LatSearchStrings = new string[] { "lat", "Lat", "latitude", "Latitude", "lats", "Lats", "latitudes", "Latitudes", "y", "Y" };
            string[] MonthSearchStrings = new string[] { "month", "Month", "months", "Months", "Time", "time" };

            //Integer counter for iterating through search strings
            int kk = 0;

            // Intialise the list of arrays to hold the values of the environmental data
            _DataArray = new List<double[,]>();

            // Construct the string required to access the file using Scientific Dataset
            _ReadFileString = "msds:" + dataType + "?file=" + fileName + "&openMode=readOnly";

            // Open the data file using Scientific Dataset
            DataSet internalData = DataSet.Open(_ReadFileString);

            // Store the specified units
            _Units = units;

            // Switch based on the tempeoral resolution and data type
            switch (dataResolution)
            {
                case "year":
                    switch (dataType)
                    {
                        case "esriasciigrid":
                            // Extract the number of latidudinal and longitudinal cells in the file
                            _NumLats = (uint)internalData.Dimensions["x"].Length;
                            _NumLons = (uint)internalData.Dimensions["y"].Length;
                            // Set number of time intervals equal to 1
                            _NumTimes = 1;
                            // Initialise the vector of time steps with length 1
                            _Times = new double[1];
                            // Assign the single value of the time step dimension to be equal to 1
                            _Times[0] = 1;
                            // Get the value used for missing data in this environmental variable
                            _MissingValue = internalData.GetAttr<double>(1, "NODATA_value");
                            // Get the latitudinal and longitudinal sizes of grid cells
                            _LatStep = internalData.GetAttr<double>(1, "cellsize");
                            _LonStep = _LatStep;
                            // Get longitudinal 'x' and latitudinal 'y' corners of the bottom left of the data grid
                            _LatMin = internalData.GetAttr<double>(1, "yllcorner");
                            _LonMin = internalData.GetAttr<double>(1, "xllcorner");
                            // Create vectors holding the latitudes and longitudes of the bottom-left corners of the grid cells
                            _Lats = new double[NumLats];
                            for (int ii = 0; ii < NumLats; ii++)
                            {
                                _Lats[NumLats - 1 - ii] = LatMin + ii * _LatStep;
                            }
                            _Lons = new double[NumLons];
                            for (int ii = 0; ii < NumLons; ii++)
                            {
                                _Lons[ii] = LonMin + ii * _LonStep;
                            }
                            //Fill in the two-dimensional environmental data array
                            // Note: currently assumes  Lats (x), Lons (y) in SDS - this is different to ESRI ASCII
                            tempDoubleArray = new double[NumLats, NumLons];
                            tempDoubleArray = internalData.GetData<double[,]>(dataName);
                            _DataArray.Add(tempDoubleArray);
                            break;
                        case "nc":
                            // Loop over possible names for the latitude dimension until a match in the data file is found
                            kk = 0;
                            while ((kk < LatSearchStrings.Length) && (!internalData.Variables.Contains(LatSearchStrings[kk]))) kk++;

                            // If a match for the latitude dimension has been found then read in the data, otherwise throw an error
                            if (kk < LatSearchStrings.Length)
                            {
                                // Get number of latitudinal cells in the file
                                _NumLats = (uint)internalData.Dimensions[LatSearchStrings[kk]].Length;
                                // Read in the values of the latitude dimension from the file
                                // Check which format the latitude dimension data are in; if unrecognized, then throw an error
                                if (internalData.Variables[LatSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "single")
                                {
                                    // Read the latitude dimension data to a temporary vector
                                    tempSingleVector = internalData.GetData<Single[]>(LatSearchStrings[kk]);
                                    // Convert the dimension data to double format and add to the vector of dimension values
                                    _Lats = new double[tempSingleVector.Length];
                                    for (int jj = 0; jj < tempSingleVector.Length; jj++)
                                    {
                                        _Lats[jj] = (double)tempSingleVector[jj];
                                    }
                                }
                                else if (internalData.Variables[LatSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "double")
                                {
                                    // Read the dimension data directly into the vector of dimension values
                                    _Lats = internalData.GetData<double[]>(LatSearchStrings[kk]);
                                }
                                else
                                {
                                    // Data format unrecognized, so throw an error
                                    Debug.Fail("Unrecognized data format for latitude dimension");
                                }
                            }
                            else
                            {
                                // Didn't find a plausible match for latitude dimension data, so throw an error
                                Debug.Fail("Cannot find any variables that look like latitude dimensions");
                            }

                            // Loop over possible names for the latitude dimension until a match in the data file is found
                            kk = 0;
                            while ((kk < LonSearchStrings.Length) && (!internalData.Variables.Contains(LonSearchStrings[kk]))) kk++;

                            // If a match for the longitude dimension has been found then read in the data, otherwise throw an error
                            if (kk < LonSearchStrings.Length)
                            {
                                // Get number of longitudinal cells in the file
                                _NumLons = (uint)internalData.Dimensions[LonSearchStrings[kk]].Length;
                                // Read in the values of the longitude dimension from the file
                                // Check which format the longitude dimension data are in; if unrecognized, then throw an error
                                if (internalData.Variables[LonSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "single")
                                {
                                    // Read the longitude dimension data to a temporary vector
                                    tempSingleVector = internalData.GetData<Single[]>(LonSearchStrings[kk]);
                                    // Convert the dimension data to double format and add to the vector of dimension values
                                    _Lons = new double[tempSingleVector.Length];
                                    for (int jj = 0; jj < tempSingleVector.Length; jj++)
                                    {
                                        _Lons[jj] = (double)tempSingleVector[jj];
                                    }
                                }
                                else if (internalData.Variables[LonSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "double")
                                {
                                    // Read the dimension data directly into the vector of dimension values
                                    _Lons = internalData.GetData<double[]>(LonSearchStrings[kk]);
                                }
                                else
                                {
                                    // Data format unrecognized, so throw an error
                                    Debug.Fail("Unrecognized data format for longitude dimension");
                                }
                            }
                            else
                            {
                                // Didn't find a plausible match for longitude dimension data, so throw an error
                                Debug.Fail("Cannot find any variables that look like longitude dimensions");
                            }
                            // Set number of time intervals equal to 1
                            _NumTimes = 1;
                            // Initialise the vector of time steps with length 1
                            _Times = new double[1];
                            // Assign the single value of the time step dimension to be equal to 1
                            _Times[0] = 1;
                            // Get the latitudinal and longitudinal sizes of grid cells
                            _LatStep = (_Lats[1] - _Lats[0]);
                            _LonStep = (_Lons[1] - _Lons[0]);
                            // Convert vectors of latitude and longutiude dimension data from cell-centre references to bottom-left references
                            //if LatStep is positive then subtract the step to convert to the bottom  left corner of the cell,
                            // else if LatStep is negative, then need to add the step to convert to the bottom left
                            for (int ii = 0; ii < _Lats.Length; ii++)
                            {
                                _Lats[ii] = (_LatStep.CompareTo(0.0) > 0) ? _Lats[ii] - (_LatStep / 2) : _Lats[ii] + (_LatStep / 2);
                            }
                            for (int jj = 0; jj < _Lons.Length; jj++)
                            {
                                _Lons[jj] = (_LonStep.CompareTo(0.0) > 0) ? _Lons[jj] - (_LonStep / 2) : _Lons[jj] + (_LonStep / 2);
                            }
                            // Check whether latitudes and longitudes are inverted in the NetCDF file
                            bool LatInverted = (_Lats[1] < _Lats[0]);
                            bool LongInverted = (_Lons[1] < _Lons[0]);
                            // Run method to read in the environmental data and the dimension data from the NetCDF
                            EnvironmentListFromNetCDF(internalData, dataName, LatInverted, LongInverted);
                            // Get longitudinal 'x' and latitudinal 'y' corners of the bottom left of the data grid
                            _LatMin = _Lats[0];
                            _LonMin = _Lons[0];
                            break;
                        default:
                            // Data type not recognized, so throw an error
                            Debug.Fail("Data type not supported");
                            break;
                    }
                    break;
                case "month":
                    switch (dataType)
                    {
                        case "esriasciigrid":
                            // This combination does not work in the model, so throw an error
                            Debug.Fail("Variables at monthly temporal resolution must be stored as three-dimensional NetCDFs");
                            break;
                        case "nc":
                            // Loop over possible names for the latitude dimension until a match in the data file is found
                            kk = 0;
                            while ((kk < LatSearchStrings.Length) && (!internalData.Variables.Contains(LatSearchStrings[kk]))) kk++;

                            // If a match for the latitude dimension has been found then read in the data, otherwise throw an error
                            if (kk < LatSearchStrings.Length)
                            {
                                // Get number of latitudinal cells in the file
                                _NumLats = (uint)internalData.Dimensions[LatSearchStrings[kk]].Length;
                                // Read in the values of the latitude dimension from the file
                                // Check which format the latitude dimension data are in; if unrecognized, then throw an error
                                if (internalData.Variables[LatSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "single")
                                {
                                    // Read the latitude dimension data to a temporary vector
                                    tempSingleVector = internalData.GetData<Single[]>(LatSearchStrings[kk]);
                                    // Convert the dimension data to double format and add to the vector of dimension values
                                    _Lats = new double[tempSingleVector.Length];
                                    for (int jj = 0; jj < tempSingleVector.Length; jj++)
                                    {
                                        _Lats[jj] = (double)tempSingleVector[jj];
                                    }
                                }
                                else if (internalData.Variables[LatSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "double")
                                {
                                    // Read the dimension data directly into the vector of dimension values
                                    _Lats = internalData.GetData<double[]>(LatSearchStrings[kk]);
                                }
                                else
                                {
                                    // Data format unrecognized, so throw an error
                                    Debug.Fail("Unrecognized data format for latitude dimension");
                                }
                            }
                            else
                            {
                                // Didn't find a plausible match for latitude dimension data, so throw an error
                                Debug.Fail("Cannot find any variables that look like latitude dimensions");
                            }

                            // Loop over possible names for the latitude dimension until a match in the data file is found
                            kk = 0;
                            while ((kk < LonSearchStrings.Length) && (!internalData.Variables.Contains(LonSearchStrings[kk]))) kk++;

                            // If a match for the longitude dimension has been found then read in the data, otherwise throw an error
                            if (kk < LonSearchStrings.Length)
                            {
                                // Get number of longitudinal cells in the file
                                _NumLons = (uint)internalData.Dimensions[LonSearchStrings[kk]].Length;
                                // Read in the values of the longitude dimension from the file
                                // Check which format the longitude dimension data are in; if unrecognized, then throw an error
                                if (internalData.Variables[LonSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "single")
                                {
                                    // Read the longitude dimension data to a temporary vector
                                    tempSingleVector = internalData.GetData<Single[]>(LonSearchStrings[kk]);
                                    // Convert the dimension data to double format and add to the vector of dimension values
                                    _Lons = new double[tempSingleVector.Length];
                                    for (int jj = 0; jj < tempSingleVector.Length; jj++)
                                    {
                                        _Lons[jj] = (double)tempSingleVector[jj];
                                    }
                                }
                                else if (internalData.Variables[LonSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "double")
                                {
                                    // Read the dimension data directly into the vector of dimension values
                                    _Lons = internalData.GetData<double[]>(LonSearchStrings[kk]);
                                }
                                else
                                {
                                    // Data format unrecognized, so throw an error
                                    Debug.Fail("Unrecognized data format for longitude dimension");
                                }
                            }
                            else
                            {
                                // Didn't find a plausible match for longitude dimension data, so throw an error
                                Debug.Fail("Cannot find any variables that look like longitude dimensions");
                            }

                            // Loop over possible names for the monthly temporal dimension until a match in the data file is found
                            kk = 0;
                            while ((kk < MonthSearchStrings.Length) && (!internalData.Variables.Contains(MonthSearchStrings[kk]))) kk++;

                            // Of a match for the monthly temporal dimension has been found then read in the data, otherwise thrown an error
                            if (internalData.Variables.Contains(MonthSearchStrings[kk]))
                            {
                                // Get the number of months in the temporal dimension
                                _NumTimes = (uint)internalData.Dimensions[MonthSearchStrings[kk]].Length;
                                // Check that the number of months is 12
                                Debug.Assert(_NumTimes == 12, "Number of time intervals in an environmental data file with specified monthly temporal resolution is not equal to 12");
                                // Read in the values of the temporal dimension from the file
                                // Check which format the temporal dimension data are in; if unrecognized, then throw an error
                                if (internalData.Variables[MonthSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "single")
                                {
                                    // Read the temporal dimension data to a temporary vector
                                    tempSingleVector = internalData.GetData<Single[]>(MonthSearchStrings[kk]);
                                    // Convert the dimension data to double format and add to the vector of dimension values
                                    _Times = new double[_NumTimes];
                                    for (int hh = 0; hh < tempSingleVector.Length; hh++)
                                    {
                                        _Times[hh] = (double)tempSingleVector[hh];
                                    }
                                }
                                else if (internalData.Variables[MonthSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "double")
                                {
                                    // Read the dimension data directly into the vector of dimension values
                                    _Times = internalData.GetData<double[]>(MonthSearchStrings[kk]);
                                }
                                else if (internalData.Variables[MonthSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "int32")
                                {
                                    // Read the temporal dimension data to a temporary vector
                                    tempInt32Vector = internalData.GetData<Int32[]>(MonthSearchStrings[kk]);
                                    // Convert the dimension data to double format and add to the vector of dimension values
                                    _Times = new double[_NumTimes];
                                    for (int hh = 0; hh < tempInt32Vector.Length; hh++)
                                    {
                                        _Times[hh] = (double)tempInt32Vector[hh];
                                    }
                                }
                                else if (internalData.Variables[MonthSearchStrings[kk]].TypeOfData.Name.ToString().ToLower() == "int16")
                                {
                                    // Read the temporal dimension data to a temporary vector
                                    tempInt16Vector = internalData.GetData<Int16[]>(MonthSearchStrings[kk]);
                                    // Convert the dimension data to double format and add to the vector of dimension values
                                    _Times = new double[_NumTimes];
                                    for (int hh = 0; hh < tempInt16Vector.Length; hh++)
                                    {
                                        _Times[hh] = (double)tempInt16Vector[hh];
                                    }
                                }
                                else
                                {
                                    // Data format unrecognized, so throw an error
                                    Debug.Fail("Unrecognized data format for time dimension");
                                }
                            }
                            else
                            {
                                // Didn't find a plausible match for temporal dimension data, so throw an error
                                Debug.Fail("Cannot find any variables that look like a monthly temporal dimension");
                            }

                            // Convert the values of the time dimension to equal integers between 1 and 12 (the format currently recognized by the model)
                            for (int hh = 0; hh < _NumTimes; hh++)
                            {
                                if (_Times[hh] != hh + 1) _Times[hh] = hh + 1;
                            }
                            // Get the latitudinal and longitudinal sizes of grid cells
                            _LatStep = (_Lats[1] - _Lats[0]);
                            _LonStep = (_Lons[1] - _Lons[0]);
                            // Convert vectors of latitude and longutiude dimension data from cell-centre references to bottom-left references
                            for (int ii = 0; ii < _Lats.Length; ii++)
                            {
                                _Lats[ii] = _Lats[ii] - (_LatStep / 2);
                            }
                            for (int jj = 0; jj < _Lons.Length; jj++)
                            {
                                _Lons[jj] = _Lons[jj] - (_LonStep / 2);
                            }
                            // Check whether latitudes and longitudes are inverted in the NetCDF file
                            bool LatInverted = (_Lats[1] < _Lats[0]);
                            bool LongInverted = (_Lons[1] < _Lons[0]);
                            // Run method to read in the environmental data and the dimension data from the NetCDF
                            EnvironmentListFromNetCDF3D(internalData, dataName, LatInverted, LongInverted);
                            // Get longitudinal 'x' and latitudinal 'y' corners of the bottom left of the data grid
                            _LatMin = _Lats[0];
                            _LonMin = _Lons[0];
                            break;
                        default:
                            // Data type not recognized, so throw an error
                            Debug.Fail("Data type not supported");
                            break;
                    }
                    break;
                default:
                    // The model currently only supports variables with temporal resolution 'year' or 'month', so throw an error
                    Debug.Fail("Temporal resolution not supported");
                    break;
            }

            // Check to see whether the environmental variable has longitude values from 0 to 360, instead of -180 to 180 (which the model currently recognizes)
            if (_LonMin + (_NumLons * _LonStep) > 180.0)
            {
                // Convert the longitude values to be -180 to 180, instead of 0 to 360
                Utilities.ConvertToM180To180(_Lons);
                // Update the minimum longitude value accordingly
                _LonMin = _Lons.Min();
            }

            // Update the variable keeping track of the number of environmental data layers
            _NumEnviroLayers = _NumEnviroLayers + 1;

            //Close the environmental data file
            internalData.Dispose();
        }
        /// <summary>
        /// Overloaded constructor to fetch climate information from the cloud using FetchClimate for specific locations
        /// </summary>
        /// <param name="dataName">Name of the the climate variable to be fetched</param>
        /// <param name="dataResolution">Time resolution requested</param>
        /// <param name="latMin">Bottom latitude</param>
        /// <param name="lonMin">Leftmost longitude</param>
        /// <param name="latMax">Maximum latitude</param>
        /// <param name="lonMax">Maximum longitude</param>
        /// <param name="cellSize">Size of each grid cell</param>
        /// <param name = "cellList">List of cells to be fetched</param>
        /// <param name="FetchClimateDataSource">Data source from which to fetch environmental data</param>
        public EnviroData(string dataName, string dataResolution, double latMin, double lonMin, double latMax, double lonMax, double cellSize,
            List<uint[]> cellList,
            EnvironmentalDataSource FetchClimateDataSource)
        {
            Console.WriteLine("Fetching environmental data for: " + dataName + " with resolution " + dataResolution);

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

            _NumLats = Convert.ToUInt32((latMax - latMin) / cellSize);
            _NumLons = Convert.ToUInt32((lonMax - lonMin) / cellSize);
            _LatMin = latMin;
            _LonMin = lonMin;

            _Lats = new double[_NumLats];
            _Lons = new double[_NumLons];

            for (int ii = 0; ii < _NumLats; ii++)
            {
                _Lats[ii] = Math.Round(_LatMin + (ii * cellSize), 2);
            }
            for (int jj = 0; jj < _NumLons; jj++)
            {
                _Lons[jj] = Math.Round(_LonMin + (jj * cellSize), 2);
            }

            _LatStep = Math.Round(Lats[1] - _Lats[0], 2);
            _LonStep = Math.Round(Lons[1] - Lons[0], 2);

            //Declare a dataset to perform the fetch
            var ds = DataSet.Open("msds:memory2");

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

            //Add the required time dimension to the dataset
            switch (dataResolution)
            {
                case "year":
                    ds.AddClimatologyAxisYearly(yearmin: 1961, yearmax: 1990, yearStep: 30);
                    break;
                case "month":
                    ds.AddClimatologyAxisMonthly();
                    break;
                default:
                    break;
            }

            //Add lat and lon information to the dataset
            for (int ii = 0; ii < cellList.Count; ii++)
            {
                ds.AddAxisCells("longitude", "degrees_east", _Lons[cellList[ii][1]], Lons[cellList[ii][1]] + cellSize, cellSize);
                ds.AddAxisCells("latitude", "degrees_north", _Lats[cellList[ii][0]], _Lats[cellList[ii][0]] + cellSize, cellSize);

                double[, ,] temp = null;


                //Fetch for the required data
                switch (dataName.ToLower())
                {
                    case "land_dtr":
                        ds.Fetch(ClimateParameter.FC_LAND_DIURNAL_TEMPERATURE_RANGE, "landdtr", dataSource: FetchClimateDataSource); //this call will create 2D variable on dimensions records and months and fill it with a FetchClimate
                        //int NumberOfRecords = ds.Dimensions["RecordNumber"].Length; // get number of records     
                        temp = (double[, ,])ds.Variables["landdtr"].GetData();
                        _MissingValue = (double)ds.Variables["landdtr"].GetMissingValue();
                        break;
                    case "temperature":
                        ds.Fetch(ClimateParameter.FC_TEMPERATURE, "airt", dataSource: FetchClimateDataSource); //this call will create 2D variable on dimensions records and months and fill it with a FetchClimate
                        //int NumberOfRecords = ds.Dimensions["RecordNumber"].Length; // get number of records     
                        temp = (double[, ,])ds.Variables["airt"].GetData();
                        _MissingValue = (double)ds.Variables["airt"].GetMissingValue();
                        break;
                    // Commenting out ocean air temperature because it is running too slow when using FetchClimate
                    case "temperature_ocean":
                        ds.Fetch(ClimateParameter.FC_OCEAN_AIR_TEMPERATURE, "oceanairt", dataSource: FetchClimateDataSource); //this call will create 2D variable on dimensions records and months and fill it with a FetchClimate
                        //int NumberOfRecords = ds.Dimensions["RecordNumber"].Length; // get number of records     
                        temp = (double[, ,])ds.Variables["oceanairt"].GetData();
                        _MissingValue = (double)ds.Variables["oceanairt"].GetMissingValue();
                        break;
                    case "precipitation":
                        ds.Fetch(ClimateParameter.FC_PRECIPITATION, "precip", dataSource: FetchClimateDataSource); //this call will create 2D variable on dimensions records and months and fill it with a FetchClimate
                        //int NumberOfRecords = ds.Dimensions["RecordNumber"].Length; // get number of records     
                        temp = (double[, ,])ds.Variables["precip"].GetData();
                        _MissingValue = (double)ds.Variables["precip"].GetMissingValue();
                        break;
                    case "frost":
                        ds.Fetch(ClimateParameter.FC_LAND_FROST_DAY_FREQUENCY, "frost", dataSource: FetchClimateDataSource);
                        temp = (double[, ,])ds.Variables["frost"].GetData();
                        _MissingValue = (double)ds.Variables["frost"].GetMissingValue();
                        break;
                    default:
                        Debug.Fail("No Enviro data read in for " + dataName);
                        break;
                }

                _NumTimes = (uint)ds.Dimensions["time"].Length;

                //Add the fetched data to the Envirodata array
                for (int tt = 0; tt < _NumTimes; tt++)
                {
                    double[,] TempArray;
                    if (_DataArray.Count > tt)
                    {
                        TempArray = _DataArray[tt];
                    }
                    else
                    {
                        TempArray = new double[NumLats, NumLons];
                    }

                    // Currently FetchClimate returns longitudes as the last array dimension
                    TempArray[cellList[ii][0], cellList[ii][1]] = temp[tt, 0, 0];

                    if (_DataArray.Count > tt)
                    {
                        _DataArray.RemoveAt(tt);
                        _DataArray.Insert(tt, TempArray);
                    }
                    else
                    {
                        _DataArray.Add(TempArray);
                    }

                }
            }

            //DataSet Out = ds.Clone("output/" + dataName + ".nc");
            //Out.Dispose();
            ds.Dispose();

        }
        /// <summary>
        /// Constructor for predation: assigns all parameter values
        /// </summary>
        /// <param name="cellArea">The area (in square km) of the grid cell</param>
        /// <param name="globalModelTimeStepUnit">The time step unit used in the model</param>
        public RevisedPredation(double cellArea, string globalModelTimeStepUnit)
        {
            InitialiseParametersPredation();

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

            // Calculate the scalar to convert from the time step units used by this implementation of predation to the global model time step units
            _DeltaT = Utilities.ConvertTimeUnits(globalModelTimeStepUnit, _TimeUnitImplementation);

            // Store the specified cell area in this instance of this predation implementation
            _CellArea = cellArea;
            _CellAreaHectares = cellArea * 100;
            HalfNumberOfBins = NumberOfBins / 2;
            FeedingPreferenceHalfStandardDeviation = _FeedingPreferenceStandardDeviation * 0.5;
        }
        /// <summary>
        /// Constructor for the plant model
        /// </summary>
        public RevisedTerrestrialPlantModel()
        {
            // Initialise parameters
            InitialisePlantModelParameters();

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