/// <summary>
        /// Outputs a two-dimensional array to a three-dimensional variable in an SDS object, with specified offset for the third dimension
        /// </summary>
        /// <param name="dataToConvert">An array of values to output</param>
        /// <param name="newVariableName">The name of the variable to be created or written to</param>
        /// <param name="dimensionNames">A vector containing the names of the dimensions of the output variable</param>
        /// <param name="thirdDimensionOffset">The offset to be applied in the third dimension</param>
        /// <param name="missingValue">The missing value to be used</param>
        /// <param name="SDSObject">A reference to an SDS object</param>
        public void Array2DToSDS3D(double[,] dataToConvert, string newVariableName, string[] dimensionNames, int thirdDimensionOffset, double missingValue, DataSet SDSObject)
        {
            // Check that the length of the vector of dimension names equals the number of dimensions
            Debug.Assert(dimensionNames.Length == 3, "There should be three dimension names passed to this method");

            // Check that the dimension information has been defined already
            foreach (string dimensionName in dimensionNames)
            {
                Debug.Assert(SDSObject.Variables.Contains(dimensionName), "Error: where an offset is included, dimension information must be defined before adding data");
            }

            Debug.Assert(SDSObject.Variables.Contains(newVariableName), "Error: where an offset is included, target variable must be created before adding data");

            SDSObject.PutData<double[,]>(newVariableName, dataToConvert, DataSet.FromToEnd(0), DataSet.FromToEnd(0), DataSet.ReduceDim(thirdDimensionOffset));

            SDSObject.Commit();
        }
        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();
        }
        /// <summary>
        /// Extract an array of values from a state variable in a model grid and add to a two-dimensional variable in an SDS object
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid to extract data from</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="gridVariableName">The name of the state variable in the model grid</param>
        /// <param name="traitValue">The trait value of the functional groups to get data for</param>
        /// <param name="variableType">The type of the state variable: 'stock' or 'cohort'</param>
        /// <param name="outputVariableName">The name of the variable to write to</param>
        /// <param name="SDSObject">The SDS object to write to</param>
        /// <param name="functionalGroupHandler">The functional group handler corresponding to cohorts or stocks</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void Array2DToSDS2D(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, string gridVariableName, string traitValue,
            string variableType, string outputVariableName, DataSet SDSObject, FunctionalGroupDefinitions functionalGroupHandler,
            MadingleyModelInitialisation initialisation)
        {
            // Get the missing value from the model grid
            double MissingValue = ecosystemModelGrid.GlobalMissingValue;

            // create an array to hold the data to output
            double[,] dataToConvert = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];

            // generate arrays to hold latitudes and longitudes
            float[] lats = new float[ecosystemModelGrid.NumLatCells];
            float[] lons = new float[ecosystemModelGrid.NumLonCells];

            // Populate arrays of latitudes and longitudes, converting from bottom left cell references as used in the model grid
            // to cell centre references as required by SDS
            for (uint ii = 0; ii < ecosystemModelGrid.NumLatCells; ii++)
            {
                lats[ii] = ecosystemModelGrid.Lats[ii] + (ecosystemModelGrid.LatCellSize / 2);

            }

            for (uint jj = 0; jj < ecosystemModelGrid.NumLonCells; jj++)
            {
                lons[jj] = ecosystemModelGrid.Lons[jj] + (ecosystemModelGrid.LonCellSize / 2);
            }

            // Get the required data from the model grid
            dataToConvert = ecosystemModelGrid.GetStateVariableGrid(gridVariableName, traitValue, functionalGroupHandler.AllFunctionalGroupsIndex, cellIndices,
                variableType, initialisation);

            // If not already contained in the SDS, add the dimension information (geographic coordinates)
            if (SDSObject.Variables.Contains("Latitude"))
            {
            }
            else
            {
                SDSObject.AddVariable<float>("Latitude", lats, "Lat");
            }
            if (SDSObject.Variables.Contains("Longitude"))
            {
            }
            else
            {
                SDSObject.AddVariable<float>("Longitude", lons, "Lon");
            }

            // If the SDS object already contains the output variable, then add the data. Otherwise, define the variable and then add the data
            if (SDSObject.Variables.Contains(outputVariableName))
            {
                SDSObject.PutData<double[,]>(outputVariableName, dataToConvert);
                // Commit the changes
                SDSObject.Commit();
            }
            else
            {
                // Set up the dimensions and add the gridded data
                string[] dimensions = { "Lat", "Lon" };
                var DataGrid = SDSObject.AddVariable<double>(outputVariableName, dataToConvert, dimensions);

                // Add appropriate metadata (including missing values)
                DataGrid.Metadata["DisplayName"] = outputVariableName;
                DataGrid.Metadata["MissingValue"] = (double)MissingValue;

                // Commit changes to update data set
                SDSObject.Commit();
            }
        }
        /// <summary>
        /// Adds a geographical array of values to a two-dimensional variable in an SDS object
        /// </summary>
        /// <param name="dataToConvert">The array of values to add</param>
        /// <param name="ouputVariableName">The name of the variable to write to</param>
        /// <param name="lats">The values of the latitude dimension variable</param>
        /// <param name="lons">The values of the longitude dimension variable</param>
        /// <param name="missingValue">The value used for missing data</param>
        /// <param name="SDSObject">The SDS object to write to</param>
        public void Array2DToSDS2D(double[,] dataToConvert, string ouputVariableName, float[] lats, float[] lons, double missingValue, DataSet SDSObject)
        {
            // If not already contained in the SDS, add the dimension information (geographic coordinates)
            if (SDSObject.Variables.Contains("Latitude"))
            {
            }
            else
            {
                SDSObject.AddVariable<float>("Latitude", lats, "Lat");
            }
            if (SDSObject.Variables.Contains("Longitude"))
            {
            }
            else
            {
                SDSObject.AddVariable<float>("Longitude", lons, "Lon");
            }

            // If the SDS object contains the variable to write to, then simply add the data, otherwise add a new variable and then add the data
            if (SDSObject.Variables.Contains(ouputVariableName))
            {
                // Add the data
                SDSObject.PutData<double[,]>(ouputVariableName, dataToConvert);

                // Commit the changes
                SDSObject.Commit();

            }
            else
            {
                // Set up the dimensions and add the gridded data
                string[] dimensions = { "Lat", "Lon" };
                var DataGrid = SDSObject.AddVariable<double>(ouputVariableName, dataToConvert, dimensions);

                // Add appropriate metadata (including missing values)
                DataGrid.Metadata["DisplayName"] = ouputVariableName;
                DataGrid.Metadata["MissingValue"] = (double)missingValue;

                // Commit changes to update data set
                SDSObject.Commit();
            }
        }
        /// <summary>
        /// Adds a vector of values to a two-dimensional variable in an SDS object at the specified offset in the first dimension
        /// </summary>
        /// <param name="dataToConvert">The vector of values to add</param>
        /// <param name="outputVariableName">The name of the variable to write to</param>
        /// <param name="dimensionNames">A vector containing the names of the dimension variables</param>
        /// <param name="dimension1Data">The values of the first dimension variable</param>
        /// <param name="dimension2Data">The values of the second dimension</param>
        /// <param name="missingValue">The value used for missing data</param>
        /// <param name="SDSObject">The SDS object to write to</param>
        /// <param name="dimension1Offset">The required offset in the first dimension</param>
        public void VectorToSDS2D(double[] dataToConvert, string outputVariableName, string[] dimensionNames, float[] dimension1Data, float[] dimension2Data, double missingValue, DataSet SDSObject, int dimension1Offset)
        {
            // Check that the length of the vector of dimension names equals the number of dimensions
            Debug.Assert(dimensionNames.Length == 2, "There should be two dimension names passed to this method");

            // Check that the dimension information has been defined already
            foreach (string dimensionName in dimensionNames)
            {
                Debug.Assert(SDSObject.Variables.Contains(dimensionName), "Error: where an offset is included, dimension information must be defined before adding data");
            }

            // Check that the output variable has been defined already
            Debug.Assert(SDSObject.Variables.Contains(outputVariableName), "Error: where an offset is included, the variable must be created before adding data");

            // Add the data to the SDS object
            SDSObject.PutData<double[]>(outputVariableName, dataToConvert, DataSet.ReduceDim(dimension1Offset), DataSet.FromToEnd(0));

            // Commit the SDS object
            SDSObject.Commit();

        }
        /// <summary>
        /// Adds a vector of double values to a one-dimensional variable in an SDS object
        /// </summary>
        /// <param name="dataToConvert">The vector of values to add</param>
        /// <param name="outputVariableName">The name of the variable to write to</param>
        /// <param name="dimensionName">The name of the dimension variable of the output variable</param>
        /// <param name="dimensionValues">The values of the dimension variable</param>
        /// <param name="missingValue">The value used for missing data</param>
        /// <param name="SDSObject">The SDS object to write to</param>
        public void VectorToSDS1D(double[] dataToConvert, string outputVariableName, string dimensionName, float[] dimensionValues, double missingValue, DataSet SDSObject)
        {
            // If not already contained in the SDS object, add the dimension variable
            if (SDSObject.Variables.Contains(dimensionName))
            {
            }
            else
            {
                SDSObject.AddVariable<float>(dimensionName, dimensionValues, dimensionName);
            }

            // If not already contained in the SDS object, add the output variable
            if (SDSObject.Variables.Contains(outputVariableName))
            {
                SDSObject.PutData<double[]>(outputVariableName, dataToConvert);
                SDSObject.Commit();
            }
            else
            {
                // Set up the dimensions and add the gridded data
                string[] dimensions = { dimensionName };
                var DataGrid = SDSObject.AddVariable<double>(outputVariableName, dataToConvert, dimensions);

                // Add appropriate metadata (including missing values)
                DataGrid.Metadata["DisplayName"] = outputVariableName;
                DataGrid.Metadata["MissingValue"] = (double)missingValue;

                // Commit changes
                SDSObject.Commit();
            }

        }
        /// <summary>
        /// Adds a double value to an one-dimensional variable in an SDS object at the specified offset in the dimension
        /// </summary>
        /// <param name="dataToConvert">The value to add to the SDS object</param>
        /// <param name="outputVariableName">The name of the variable to add the data to</param>
        /// <param name="dimensionName">The name of the dimension variable of the output variable</param>
        /// <param name="missingValue">The value used for missing data</param>
        /// <param name="SDSObject">The SDS object to write to</param>
        /// <param name="dimensionOffset">The required offset in the dimension variable</param>
        public void ValueToSDS1D(double dataToConvert, string outputVariableName, string dimensionName, double missingValue,
            DataSet SDSObject, int dimensionOffset)
        {
            // Check that the dimension variables and the output variable have been defined already
            Debug.Assert(SDSObject.Variables.Contains(dimensionName),
                "Error: where an offset is included, dimension information must be defined before adding data");
            Debug.Assert(SDSObject.Variables.Contains(outputVariableName),
                "Error: where an offset is included, the variable must be created before adding data");

            // Add the data to the SDS object
            SDSObject.PutData<double>(outputVariableName, dataToConvert, DataSet.ReduceDim(dimensionOffset));

            // Commit the SDS object
            SDSObject.Commit();

        }