/// <summary>
        /// Reads in three-dimensional environmental data from a NetCDF and stores them in the array of values within this instance of EnviroData
        /// </summary>
        /// <param name="internalData">The SDS object to get data from</param>
        /// <param name="dataName">The name of the variable within the NetCDF file</param>
        /// <param name="latInverted">Whether the latitude values are inverted in the NetCDF file (i.e. large to small values)</param>
        /// <param name="longInverted">Whether the longitude values are inverted in the NetCDF file (i.e. large to small values)</param>
        private void EnvironmentListFromNetCDF3D(DataSet internalData, string dataName, bool latInverted, bool longInverted)
        {
            // Vector to hold the position in the dimensions of the NetCDF file of the latitude, longitude and third dimensions
            int[] positions = new int[3];

            // Array to store environmental data with latitude as the first dimension and longitude as the second dimension
            double[,] LatLongArrayUnsorted = new double[_NumLats, _NumLons];

            // Check that the requested variable exists in the NetCDF file
            Debug.Assert(internalData.Variables.Contains(dataName), "Requested variable does not exist in the specified file");

            // Check that the environmental variable in the NetCDF file has three dimensions
            Debug.Assert(internalData.Variables[dataName].Dimensions.Count == 3, "The specified variable in the NetCDF file does not have three dimensions, which is the required number for this method");

            // Possible names for the missing value metadata in the NetCDF file
            string[] SearchStrings = { "missing_value", "MissingValue" };

            // Loop over possible names for the missing value metadata until a match is found in the NetCDF file
            int kk = 0;
            while ((kk < SearchStrings.Length) & (!internalData.Variables[dataName].Metadata.ContainsKey(SearchStrings[kk]))) kk++;

            // If a match is found, then set the missing data field equal to the value in the file, otherwise throw an error
            if (kk < SearchStrings.Length)
            {
                _MissingValue = Convert.ToDouble(internalData.Variables[dataName].Metadata[SearchStrings[kk]]);
            }
            else
            {
                Debug.Fail("No missing data value found for environmental data file: " + internalData.Name.ToString());
            }

            // Possible names for the latitude dimension in the NetCDF file
            SearchStrings = new string[] { "lat", "Lat", "latitude", "Latitude", "lats", "Lats", "latitudes", "Latitudes", "y" };
            // Check which position the latitude dimension is in in the NetCDF file and add this to the vector of positions. If the latitude dimension cannot be
            // found then throw an error
            if (SearchStrings.Contains(internalData.Dimensions[0].Name.ToString()))
            {
                positions[0] = 1;
            }
            else if (SearchStrings.Contains(internalData.Dimensions[1].Name.ToString()))
            {
                positions[1] = 1;
            }
            else if (SearchStrings.Contains(internalData.Dimensions[2].Name.ToString()))
            {
                positions[2] = 1;
            }
            else
            {
                Debug.Fail("Cannot find a latitude dimension");
            }

            // Possible names for the longitude dimension in the netCDF file
            SearchStrings = new string[] { "lon", "Lon", "longitude", "Longitude", "lons", "Lons", "long", "Long", "longs", "Longs", "longitudes", "Longitudes", "x" };
            // Check which position the latitude dimension is in in the NetCDF file and add this to the vector of positions. If the latitude dimension cannot be
            // found then throw an error
            if (SearchStrings.Contains(internalData.Dimensions[0].Name.ToString()))
            {
                positions[0] = 2;
            }
            else if (SearchStrings.Contains(internalData.Dimensions[1].Name.ToString()))
            {
                positions[1] = 2;
            }
            else if (SearchStrings.Contains(internalData.Dimensions[2].Name.ToString()))
            {
                positions[2] = 2;
            }
            else
            {
                Debug.Fail("Cannot find a longitude dimension");
            }

            // Possible names for the monthly temporal dimension in the netCDF file
            SearchStrings = new string[] { "month", "Month", "months", "Months" };
            // Check which position the temporal dimension is in in the NetCDF file and add this to the vector of positions. If the temporal dimension cannot be
            // found then throw an error
            if (SearchStrings.Contains(internalData.Dimensions[0].Name.ToString()))
            {
                positions[0] = 3;
            }
            else if (SearchStrings.Contains(internalData.Dimensions[1].Name.ToString()))
            {
                positions[1] = 3;
            }
            else if (SearchStrings.Contains(internalData.Dimensions[2].Name.ToString()))
            {
                positions[2] = 3;
            }

            // Check the format of the specified environmental variable
            if (internalData.Variables[dataName].TypeOfData.Name.ToString().ToLower() == "single")
            {
                // Read the environmental data into a temporary array
                Single[, ,] TempArray;
                TempArray = internalData.GetData<Single[, ,]>(dataName);

                // Revised for speed
                switch (positions[0])
                {
                    case 1:
                        switch (positions[1])
                        {
                            case 2:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[ii, jj, hh];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 3:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[ii, hh, jj];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    case 2:
                        switch (positions[1])
                        {
                            case 1:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[jj, ii, hh];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 3:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[jj, hh, ii];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    case 3:
                        switch (positions[1])
                        {
                            case 1:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[hh, ii, jj];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 2:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[hh, jj, ii];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    default:
                        Debug.Fail("Failure detecting latitude dimension");
                        break;
                }

            }
            else if (internalData.Variables[dataName].TypeOfData.Name.ToString().ToLower() == "double")
            {
                // Read the environmental data into a temporary array
                double[, ,] TempArray;
                TempArray = internalData.GetData<double[, ,]>(dataName);
                // Revised for speed
                switch (positions[0])
                {
                    case 1:
                        switch (positions[1])
                        {
                            case 2:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = TempArray[ii, jj, hh];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 3:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = TempArray[ii, hh, jj];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    case 2:
                        switch (positions[1])
                        {
                            case 1:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = TempArray[jj, ii, hh];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 3:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = TempArray[jj, hh, ii];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    case 3:
                        switch (positions[1])
                        {
                            case 1:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = TempArray[hh, ii, jj];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 2:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = TempArray[hh, jj, ii];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    default:
                        Debug.Fail("Failure detecting latitude dimension");
                        break;
                }
            }
            else if (internalData.Variables[dataName].TypeOfData.Name.ToString().ToLower() == "int32")
            {
                // Read the environmental data into a temporary array
                Int32[, ,] TempArray;
                TempArray = internalData.GetData<Int32[, ,]>(dataName);
                // Revised for speed
                switch (positions[0])
                {
                    case 1:
                        switch (positions[1])
                        {
                            case 2:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[ii, jj, hh];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 3:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[ii, hh, jj];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    case 2:
                        switch (positions[1])
                        {
                            case 1:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[jj, ii, hh];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 3:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[jj, hh, ii];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    case 3:
                        switch (positions[1])
                        {
                            case 1:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[hh, ii, jj];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 2:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[hh, jj, ii];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    default:
                        Debug.Fail("Failure detecting latitude dimension");
                        break;
                }

            }
            else if (internalData.Variables[dataName].TypeOfData.Name.ToString().ToLower() == "int16")
            {
                // Read the environmental data into a temporary array
                Int16[, ,] TempArray;
                TempArray = internalData.GetData<Int16[, ,]>(dataName);

                // Revised for speed
                switch (positions[0])
                {
                    case 1:
                        switch (positions[1])
                        {
                            case 2:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[ii, jj, hh];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 3:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[ii, hh, jj];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    case 2:
                        switch (positions[1])
                        {
                            case 1:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[jj, ii, hh];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 3:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[jj, hh, ii];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    case 3:
                        switch (positions[1])
                        {
                            case 1:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[hh, ii, jj];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            case 2:
                                // Loop over time steps
                                for (int hh = 0; hh < _NumTimes; hh++)
                                {
                                    // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
                                    double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

                                    // Add to the unsorted array of values, transposing the data to be dimensioned by latitude first and 
                                    // longitude second
                                    for (int ii = 0; ii < _NumLats; ii++)
                                    {
                                        for (int jj = 0; jj < _NumLons; jj++)
                                        {
                                            LatLongArrayUnsorted[ii, jj] = (double)TempArray[hh, jj, ii];
                                        }
                                    }

                                    // Transpose the environmental data so that they are in ascending order of both latitude and longitude
                                    if (latInverted)
                                    {
                                        if (longInverted)
                                        {
                                            // Both dimensions inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }

                                        else
                                        {
                                            // Latitude only inverted
                                            int LatLengthMinusOne = LatLongArrayUnsorted.GetLength(0) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[LatLengthMinusOne - ii, jj];
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (longInverted)
                                        {
                                            // Longitude only inverted
                                            int LongLengthMinusOne = LatLongArrayUnsorted.GetLength(1) - 1;
                                            for (int ii = 0; ii < _NumLats; ii++)
                                            {
                                                for (int jj = 0; jj < _NumLons; jj++)
                                                {
                                                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[ii, LongLengthMinusOne - jj];
                                                }
                                            }
                                        }
                                    }
                                    // Add the final array to the class field for environmental data values
                                    _DataArray.Add(LatLongArraySorted);

                                }
                                break;
                            default:
                                Debug.Fail("Failure detecting latitude dimension");
                                break;
                        }
                        break;
                    default:
                        Debug.Fail("Failure detecting latitude dimension");
                        break;
                }

            }
            else
            {
                // Format of environmental data not recognized so throw an error
                Debug.Fail("Environmental data are in an unrecognized format");
            }

            // If either latitude or longitude were inverted, then reverse their values in the class fields
            if (latInverted)
            {
                // Temporary vector to store inverted latitude values
                double[] tempLats = new double[_NumLats];
                // Loop over latitude values
                for (int ii = 0; ii < _NumLats; ii++)
                {
                    // Invert the values in the temporary vector
                    tempLats[ii] = _Lats[_Lats.Length - 1 - ii];
                }
                // Overwrite the old vector of latitude values with the inverted values
                _Lats = tempLats;
                // Reverse the sign on the difference in latitude values between adjacent cells
                _LatStep = -_LatStep;
            }
            if (longInverted)
            {
                // Temporary vector to store inverted longitude values
                double[] tempLongs = new double[_NumLons];
                // Loop over longitude values
                for (int jj = 0; jj < _NumLons; jj++)
                {
                    // Invert the values in the temporary vector
                    tempLongs[jj] = _Lons[_Lons.Length - 1 - jj];
                }
                // Overwrite the old vector of longitude values with the inverted values
                _Lons = tempLongs;
                // Reverse the sign on the difference in longitude values between adjacent cells
                _LonStep = -_LonStep;
            }

            // Check that the increment in both latitudes and longitudes between consecutive grid cells is now positive
            Debug.Assert(_LatStep > 0.0, "Latitudes are still inverted in an environmental variable stored in EnviroData");
            Debug.Assert(_LonStep > 0.0, "Longitudes are still inverted in an environmental variable stored in EnviroData");

        }
        /// <summary>
        /// Asynchronously views an SDS
        /// </summary>
        /// <param name="DataSetToView">The name of the SDS to view</param>
        /// <param name="viewingParameters">A string of viewing parameters ('hints') to pass to SDS viewer</param>
        /// <todoD>Need to update to be able to select which variable to view</todoD>
        /// <todoD>Pass sleep length</todoD>
        /// <todoD>UPdate title on each timestep</todoD>
        public void AsynchronousView(ref DataSet DataSetToView, string viewingParameters)
        {
            DataSetToView.SpawnViewer(viewingParameters);
            // Slow down computation
            //System.Threading.Thread.Sleep(10);

        }
        /// <summary>
        /// Provides a snapshot view of an SDS
        /// </summary>
        /// <param name="DataSetToView">The name of the SDS to view</param>
        /// <param name="handle">An object handle for the viewer instance; send the same handle to prevent multiple instances of SDS viewer opening</param>
        /// <todoD>Need to update to be able to select which variable to view</todoD>
        /// <todoD>Pass sleep length</todoD>
        public void SnapshotView(ref DataSet DataSetToView, ref object handle)
        {
            // Open the snapshot viewer
            handle = DataSetToView.ViewSnapshot("", handle);
            
            // Slow down computation
            System.Threading.Thread.Sleep(250);

        }
        /// <summary>
        /// Adds a one-dimensional variable to the specified SDS object with floating point dimension data
        /// </summary>
        /// <param name="SDSObject">A reference to an SDS object</param>
        /// <param name="variableName">The name of the variable to create</param>
        /// <param name="units">Units of the data</param>
        /// <param name="numDimensions">The number of dimensions for the new variable</param>
        /// <param name="namesDimensions">A vector of names of the dimensions for the variable</param>
        /// <param name="missingValue">The missing value to apply to the new variable</param>
        /// <param name="dimension1Data">A vector of values of the first dimension</param>
        public void AddVariable(DataSet SDSObject, string variableName, string units, int numDimensions, string[] namesDimensions, double missingValue, float[] dimension1Data)
        {
            //If the wrong number of dimension names have been provided, then return error
            if (namesDimensions.Length != numDimensions)
                Debug.Fail("Error: you have provided the wrong number of dimension names");
            
            //Since this overload method deals with one-dimensional variables, return an error if this is not the case
            if (numDimensions != 1)
                Debug.Fail("Error: you have provided data for the wrong number of dimensions");

            //If the variable already exists in the SDS, then take no action, otherwise create new variable
            if (SDSObject.Variables.Contains(variableName))
            {
                Console.WriteLine("SDS object already contains a variable with that name. Skipping...");
            }
            else
            {
                //For each dimension, if it already exists in the SDS then take no action, otherwise create a new variable and populate it with the provided data
                if (SDSObject.Variables.Contains(namesDimensions[0]))
                {
                }
                else
                {
                    SDSObject.AddVariable<float>(namesDimensions[0], dimension1Data, namesDimensions[0]);
                }
                
                //If the variable is the same as one of the entered dimensions, then take no action, otherwise create the new variable and populate it with missing values
                if (SDSObject.Variables.Contains(variableName))
                {
                }
                else
                {
                    //Create array of missing values of the correct dimensions
                    double[] tempOutData = new double[dimension1Data.Length];

                    for (int ii = 0; ii < dimension1Data.Length; ii++)
                    {
                        tempOutData[ii] = missingValue;
                    }


                    //Add variable to SDS
                    var testOut = SDSObject.Add<double[]>(variableName, units, missingValue, tempOutData, namesDimensions);

                    //Metadata required by SDS
                    testOut.Metadata["DisplayName"] = variableName;
                    testOut.Metadata["MissingValue"] = missingValue;
                    
                    //Commit changes
                    SDSObject.Commit();

                }
            }

        }
Beispiel #5
0
 /// <summary>
 /// Copies given dataset into dataset determined by <paramref name="dstUri"/>.
 /// </summary>
 /// <param name="src">Original dataset to clone.</param>
 /// <param name="dstUri">URI of the destination dataset.</param>
 /// <param name="updater">Delegate accepting update progressm notifications.</param>
 /// <returns>New instance of <see cref="DataSet"/> class.</returns>
 /// <remarks>
 /// This method splits the original dataser into parts and therefore is able
 /// to clone very large datasets not fitting to memory.
 /// </remarks>
 public static DataSet Clone(DataSet src, DataSetUri dstUri, ProgressUpdater updater)
 {
     DataSet dst = null;
     try
     {
         dst = DataSet.Open(dstUri);
         return Clone(src, dst, updater);
     }
     catch
     {
         if (dst != null) dst.Dispose();
         throw;
     }
 }
		internal DataSetChangeset(DataSet sds, DataSet.Changes changes, bool initializeAtOnce)
		{
			if (changes == null)
				throw new ArgumentNullException("changes");
			lock (sds)
			{
				this.changes = changes;
				this.dataSet = sds;
				this.changeSetId = sds.Version;// +1;
				this.source = changes.ChangesetSource;

				if (initializeAtOnce)
					Initialize();
			}
		}
		public void Init(string constructionString)
		{
			dataSet = DataSet.Create(constructionString);

			//Инициализируем DataSet
			Variable X = dataSet.AddVariable<double>("X", "x");
			Variable Y = dataSet.AddVariable<double>("Y", "y");
			Variable Z = dataSet.AddVariable<double>("Z", "z");
			time = dataSet.AddVariable<double>("Time", "t");

			u = dataSet.AddVariable<double>("U velocity", "x", "y", "z", "t");
			v = dataSet.AddVariable<double>("V velocity", "x", "y", "z", "t");
			w = dataSet.AddVariable<double>("W velocity", "x", "y", "z", "t");
			T = dataSet.AddVariable<double>("Temperature", "x", "y", "z", "t");
			div = dataSet.AddVariable<double>("Divergence", "x", "y", "z", "t");

			double[] wArr = new double[modellingParams.Nx];
			for (int i = 0; i < modellingParams.Nx; i++)
			{
				wArr[i] = i * modellingParams.Dx;
			}
			X.PutData(wArr);
			wArr = new double[modellingParams.Ny];
			for (int i = 0; i < modellingParams.Ny; i++)
			{
				wArr[i] = i * modellingParams.Dy;
			}
			Y.PutData(wArr);
			wArr = new double[modellingParams.Nz];
			for (int i = 0; i < modellingParams.Nz; i++)
			{
				wArr[i] = i * modellingParams.Dz;
			}
			Z.PutData(wArr);

			//Инициализируем рассчетный модуль для слоя начальными условиями
			solver = new LayerSolver(prevData, modellingParams);
			u.Append(prevData.U.ToArray(), "t");
			v.Append(prevData.V.ToArray(), "t");
			w.Append(prevData.W.ToArray(), "t");
			T.Append(prevData.T.ToArray(), "t");
			div.Append(prevData.Div.ToArray(), "t");

			time.PutData(new double[1] { 0 });
			dataSet.Commit();
		}
        protected override DataSet ServerProcessInternal(DataSet ds)
        {
            if (serviceUri == "(local)")
                return LocalProcess(ds);

            string hash = DataSetDiskCache.ComputeHash(ds);

            DataSet proxyDataSet = null;

            // Creating new DataSet at the service.
            // TODO: fix following:       
            try
            {
                try
                {
                    proxyDataSet = ProxyDataSet.CreateProxySync(taskQueue, ServicePort, "msds:memory", false, 10 * 60 * 1000);
                }
                catch (CommunicationObjectFaultedException)
                {
                    //Connection to server closed.
                    //Recreate service port and try again.
                    if (proxyDataSet != null && !proxyDataSet.IsDisposed) proxyDataSet.Dispose();
                    this._servicePort = null;
                    proxyDataSet = ProxyDataSet.CreateProxySync(taskQueue, ServicePort, "msds:memory", false, 10 * 60 * 1000);
                }
                AutoResetEvent completed = new AutoResetEvent(false);
                OnCommittedHandler onCommitted = new OnCommittedHandler(completed, OnDataSetCommitted);
                proxyDataSet.Committed += onCommitted.Handler;

                proxyDataSet.IsAutocommitEnabled = false;
                FetchClimateRequestBuilder.CopyRequestedDataSet(ds, proxyDataSet, false);
                proxyDataSet.Metadata[Namings.metadataNameHash] = hash;
                proxyDataSet.Commit();

                if (proxyDataSet.HasChanges) proxyDataSet.Commit();

                completed.WaitOne();
                proxyDataSet.IsAutocommitEnabled = true;
                return proxyDataSet;
            }
            catch
            {
                if (proxyDataSet != null && !proxyDataSet.IsDisposed) proxyDataSet.Dispose();
                throw;
            }
        }
        /// <summary>
        /// Returns bytes of specified <see cref="DataSet"/> instance after converting it into <see cref="CsvDataSet"/>.
        /// </summary>
        /// <param name="ds">Input <see cref="DataSet"/> instance.</param>
        /// <returns>Resulting bytes array.</returns>
        public static byte[] GetCsvBytes(DataSet ds)
        {
            if (ds == null)
            {
                throw new ArgumentNullException("ds");
            }
            string resultTempFileName = GetTempCsvFileName();
            byte[] bytes = null;

            try
            {
                ds.Clone(String.Format(CultureInfo.InvariantCulture, "msds:csv?file={0}&openMode=create&appendMetadata=true", resultTempFileName));
                bytes = File.ReadAllBytes(resultTempFileName);
            }
            finally
            {
                if (resultTempFileName != null)
                    File.Delete(resultTempFileName);
            }

            return bytes;
        }
        protected override DataSet ServerProcessInternal(DataSet ds)
        {
            DateTime start = DateTime.Now;

            bool resultGot = false;

            DataSet inputDs = ds;

            DataSet resultDs = null;
            while (!resultGot)
            {
                resultDs = RestApiWrapper.Instance.Process(ds);
                if (FetchClimateRequestBuilder.IsResultDataSet(resultDs))
                {
                    resultGot = true;
                }
                else
                {
                    if (FetchClimateRequestBuilder.ResendRequest(resultDs))
                        ds = inputDs;
                    else
                        ds = resultDs;

                    int expectedCalculationTime = 0;
                    string hash = string.Empty;
                    FetchClimateRequestBuilder.GetStatusCheckParams(resultDs, out expectedCalculationTime, out hash);

                    Thread.Sleep(expectedCalculationTime);
                }

                if ((!resultGot) && (DateTime.Now - start) > timeout)
                {
                    throw new TimeoutException("Request to fetch climate has timed out. Increase timeout value or try again later.");
                }
            }

            return resultDs;
        }
Beispiel #11
0
 public DataSetChangingEventArgs(DataSet sds, DataSetChangeAction action, object target)
 {
     this.sds = sds;
     this.target = target;
     this.cancel = false;
     this.action = action;
 }
Beispiel #12
0
        public DataSetChangedEventArgs(DataSet sds, DataSetChangeAction action, object target, DataSetChangeset changes)
        {
            this.sds = sds;
            this.action = action;
            this.target = target;

            this.changes = changes;
        }
Beispiel #13
0
 /// <summary>
 /// Initializes the instance of the class.
 /// </summary>
 /// <param name="sds">The committed data set.</param>
 /// <param name="changes">Committed changes.</param>
 /// <param name="committedSchema">DataSet schema after commit</param>
 internal DataSetCommittedEventArgs(DataSet sds, DataSetChangeset changes, DataSetSchema committedSchema)
 {
     this.sds = sds;
     this.changes = changes;
     this.committedSchema = committedSchema;
 }
        /// <summary>
        /// Set up the file, screen and live outputs prior to the model run
        /// </summary>
        /// <param name="EcosystemModelGrid">The model grid that output data will be derived from</param>
        /// <param name="CohortFunctionalGroupDefinitions">The definitions for cohort functional groups</param>
        /// <param name="StockFunctionalGroupDefinitions">The definitions for stock functional groups</param>
        /// <param name="NumTimeSteps">The number of time steps in the model run</param>
        public void SetUpOutputs(ModelGrid EcosystemModelGrid, FunctionalGroupDefinitions CohortFunctionalGroupDefinitions, 
            FunctionalGroupDefinitions StockFunctionalGroupDefinitions, uint NumTimeSteps, string FileOutputs)
        {
            // Get the functional group indices of herbivore, carnivore and omnivore cohorts, and autotroph stocks
            string[] Trait = { "Nutrition source" };
            string[] Trait2 = { "Heterotroph/Autotroph" };
            string[] TraitValue1 = { "Herbivory" };
            string[] TraitValue2 = { "Carnivory" };
            string[] TraitValue3 = { "Omnivory" };
            string[] TraitValue4 = { "Autotroph" };

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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



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

        }
        /// <summary>
        /// Spawn dataset viewer for the live outputs
        /// </summary>
        /// <param name="NumTimeSteps">The number of time steps in the model run</param>
        /// <param name="gridView">Whether to launch dataset viewer in full model grid view (otherwise in graph view)</param>
        public void SpawnDatasetViewer(uint NumTimeSteps)
        {
            Console.WriteLine("Spawning Dataset Viewer");

            // Intialise the SDS object for the live view
            DataSetToViewLive = CreateSDSObject.CreateSDSInMemory(true);
            
            // Set up the grid viewer asynchronously
            // If the grid view option is selected, then live output will be the full model grid, otherwise the output will be a graph with total
            // carnivore and herbivore abundance
            if (GridView)
            {
                DataSetToViewLive.Metadata["VisualHints"] = 
                    "\"Biomass density\"(Longitude,Latitude) Style:Colormap; Palette:#000040=0,Blue=0.1,Green=0.2661,#FA8000=0.76395,Red; Transparency:0.38";
            }
            else
            {
                DataSetToViewLive.Metadata["VisualHints"] = "\"Carnivore density\"[Time step]; Style:Polyline; Visible: 0,1," + 
                    NumTimeSteps.ToString() + "," + MaximumYValue.ToString() +
                    "; LogScale:Y;  Stroke:#D95F02; Thickness:3;; \"Herbivore density\"[Time step]; Style:Polyline; Visible: 0,1," +
                    NumTimeSteps.ToString() + "," + MaximumYValue.ToString() +
                    "; LogScale:Y;  Stroke:#1B9E77; Thickness:3;; \"Omnivore density\"[Time step]; Style:Polyline; Visible: 0,1," +
                    NumTimeSteps.ToString() + "," + MaximumYValue.ToString() +
                    "; LogScale:Y;  Stroke:#7570B3;Thickness:3; Title:\"Heterotroph Densities"
                    + "\"";
            }

            
            // Start viewing
            ViewGrid.AsynchronousView(ref DataSetToViewLive, "");
        
        }
        /// <summary>
        /// Set up all outputs (live, console and file) prior to the model run
        /// </summary>
        /// <param name="numTimeSteps">The number of time steps in the model run</param>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="outputFilesSuffix">The suffix to be applied to all output files</param>
        public void SetupOutputs(uint numTimeSteps, ModelGrid ecosystemModelGrid, string outputFilesSuffix)
        {
            Console.WriteLine("Setting up global outputs...\n");

            // Set the suffix for all output files
            _OutputSuffix = outputFilesSuffix + "_Global";

            // Create an SDS object to hold total abundance and biomass data
            BasicOutput = SDSCreator.CreateSDS("netCDF", "BasicOutputs" + _OutputSuffix, _OutputPath);

            // Create vector to hold the values of the time dimension
            TimeSteps = new float[numTimeSteps + 1];

            // Set the first value to be -1 (this will hold initial outputs)
            TimeSteps[0] = 0;

            // Fill other values from 0 (this will hold outputs during the model run)
            for (int i = 1; i < numTimeSteps + 1; i++)
            {
                TimeSteps[i] = i;
            }

            // Add basic variables to the output file, dimensioned by time
            string[] TimeDimension = { "Time step" };
            DataConverter.AddVariable(BasicOutput, "Total living biomass", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
            DataConverter.AddVariable(BasicOutput, "Organic matter pool", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
            DataConverter.AddVariable(BasicOutput, "Respiratory CO2 pool", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
            DataConverter.AddVariable(BasicOutput, "Number of cohorts extinct", "", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
            DataConverter.AddVariable(BasicOutput, "Number of cohorts produced", "", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
            DataConverter.AddVariable(BasicOutput, "Number of cohorts combined", "", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
            DataConverter.AddVariable(BasicOutput, "Number of cohorts in model", "", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
            DataConverter.AddVariable(BasicOutput, "Number of stocks in model", "", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);

            this.FileName = _OutputPath + "BasicOutputs" + _OutputSuffix + ".nc";
        }
        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>
        /// Set up the predation tracker
        /// </summary>_
        /// <param name="numTimeSteps">The total number of timesteps for this simulation</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="massFlowsFilename">Filename for outputs of the flows of mass between predators and prey</param>
        /// <param name="cohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="missingValue">The missing value to be used in the output file</param>
        /// <param name="outputFileSuffix">The suffix to be applied to the output file</param>
        /// <param name="outputPath">The path to write the output file to</param>
        /// <param name="trackerMassBins">The mass bin handler containing the mass bins to be used for predation tracking</param>
        /// <param name="cellIndex">The index of the current cell in the list of all cells to run the model for</param>
        public PredationTracker(uint numTimeSteps,
            List<uint[]> cellIndices,
            string massFlowsFilename,
            FunctionalGroupDefinitions cohortDefinitions,
            double missingValue,
            string outputFileSuffix,
            string outputPath, MassBinsHandler trackerMassBins, int cellIndex)
        {
            // Assign the missing value
            _MissingValue = missingValue;

            // Get the mass bins to use for the predation tracker and the number of mass bins that this correpsonds to
            _MassBins = trackerMassBins.GetSpecifiedMassBins();
            _NumMassBins = trackerMassBins.NumMassBins;

            // Initialise the array to hold data on mass flows between mass bins
            _MassFlows = new double[_NumMassBins, _NumMassBins];

            // Define the model time steps to be used in the output file
            float[] TimeSteps = new float[numTimeSteps];
            for (int i = 1; i <= numTimeSteps; i++)
            {
                TimeSteps[i - 1] = i;
            }

            // Initialise the data converter
            DataConverter = new ArraySDSConvert();

            // Initialise the SDS object creator
            SDSCreator = new CreateSDSObject();

            // Create an SDS object to hold the predation tracker data
            MassFlowsDataSet = SDSCreator.CreateSDS("netCDF", massFlowsFilename + outputFileSuffix + "_Cell" + cellIndex, outputPath);

            // Define the dimensions to be used in the predation tracker output file
            string[] dimensions = { "Predator mass bin", "Prey mass bin", "Time steps" };

            // Add the mass flow variable to the predation tracker
            DataConverter.AddVariable(MassFlowsDataSet, "Log mass (g)", 3, dimensions, _MissingValue, _MassBins, _MassBins, TimeSteps);
        }
        /// <summary>
        /// Spawn dataset viewer for the live outputs
        /// </summary>
        /// <param name="NumTimeSteps">The number of time steps in the model run</param>
        public void SpawnDatasetViewer(uint NumTimeSteps)
        {
            Console.WriteLine("Spawning Dataset Viewer\n");

            // Intialise the SDS object for the live view
            DataSetToViewLive = SDSCreator.CreateSDSInMemory(true);

            // Check the output detail level
            if (ModelOutputDetail == OutputDetailLevel.Low)
            {
                // For low detail level, just show total living biomass
                DataSetToViewLive.Metadata["VisualHints"] = "\"Total living biomass\"[Time step]; Style:Polyline; Visible: 0,1," +
                    NumTimeSteps.ToString() + "," + MaximumYValue.ToString() +
                    "; LogScale:Y; Stroke:#D95F02; Thickness:3; Title:\"Total Biomass" + "\"";
            }
            else
            {
                // For medium and high detail levels, show biomass by trophic level
                DataSetToViewLive.Metadata["VisualHints"] = "\"autotroph biomass\"[Time step]; Style:Polyline; Visible: 0,1,"
                    + NumTimeSteps.ToString() + ","
                    + MaximumYValue.ToString() + "; LogScale:Y;  Stroke:#FF008040;Thickness:3;;\"carnivore biomass\"[Time step] ; Style:Polyline; Visible: 0,1,"
                    + NumTimeSteps.ToString() + ","
                    + MaximumYValue.ToString() + "; LogScale:Y;  Stroke:#FFFF0000;Thickness:3;;\"herbivore biomass\"[Time step] ; Style:Polyline; Visible: 0,1,"
                    + NumTimeSteps.ToString() + ","
                    + MaximumYValue.ToString() + "; LogScale:Y;  Stroke:#FF00FF00;Thickness:3;;\"omnivore biomass\"[Time step] ; Style:Polyline; Visible: 0,1,"
                    + NumTimeSteps.ToString() + ","
                    + MaximumYValue.ToString() + "; LogScale:Y;  Stroke:#FF0000FF;Thickness:3; Title:\"Biomass Densities";
            }

            // Start viewing
            GridViewer.AsynchronousView(ref DataSetToViewLive, "");
        }
        /// <summary>
        /// Sets up the outputs associated with the high level of output detail
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The indices of active cells in the model grid</param>
        /// <param name="cellNumber">The index of the current cell in the list of active cells</param>
        /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void SetUpHighLevelOutputs(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellNumber,
            FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, Boolean marineCell)
        {
            // Create an SDS object for outputs by mass bin
            // MassBinsOutput = SDSCreator.CreateSDS("netCDF", "MassBins" + _OutputSuffix, _OutputPath);
            MassBinsOutputMemory = SDSCreator.CreateSDSInMemory(true);

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

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

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

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

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

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

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

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

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

                // Counter for tracked cohorts
                int TrackedCohortCounter = 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                            TotalBiomassDensitiesOut.Add(TraitValueJoin, 0.0);
                            TotalDensitiesOut.Add(TraitValueJoin, 0.0);
                        }
                    }
                }
        }
        /// <summary>
        /// Reads in two-dimensional environmental data from a NetCDF and stores them in the array of values within this instance of EnviroData
        /// </summary>
        /// <param name="internalData">The SDS object to get data from</param>
        /// <param name="dataName">The name of the variable within the NetCDF file</param>
        /// <param name="latInverted">Whether the latitude values are inverted in the NetCDF file (i.e. large to small values)</param>
        /// <param name="longInverted">Whether the longitude values are inverted in the NetCDF file (i.e. large to small values)</param>
        private void EnvironmentListFromNetCDF(DataSet internalData, string dataName, bool latInverted, bool longInverted)
        {
            // Vector two hold the position in the dimensions of the NetCDF file of the latitude and longitude dimensions
            int[] positions = new int[2];

            // Array to store environmental data with latitude as the first dimension and longitude as the second dimension
            double[,] LatLongArrayUnsorted = new double[_NumLats, _NumLons];
            // Array to store environmental data as above, but with data in ascending order of both latitude and longitude
            double[,] LatLongArraySorted = new double[_NumLats, _NumLons];

            Console.WriteLine(dataName);
            // Check that the requested variable exists in the NetCDF file
            Debug.Assert(internalData.Variables.Contains(dataName), "Requested variable does not exist in the specified file");

            // Check that the environmental variable in the NetCDF file has two dimensions
            Debug.Assert(internalData.Variables[dataName].Dimensions.Count == 2, "The specified variable in the NetCDF file does not have two dimensions, which is the required number for this method");

            // Possible names for the missing value metadata in the NetCDF file
            string[] SearchStrings = { "missing_value", "MissingValue" };

            // Loop over possible names for the missing value metadata until a match is found in the NetCDF file
            int kk = 0;
            while ((kk < SearchStrings.Length) && (!internalData.Variables[dataName].Metadata.ContainsKey(SearchStrings[kk]))) kk++;

            // If a match is found, then set the missing data field equal to the value in the file, otherwise throw an error
            if (kk < SearchStrings.Length)
            {
                _MissingValue = Convert.ToDouble(internalData.Variables[dataName].Metadata[SearchStrings[kk]]);
            }
            else
            {
                Console.WriteLine("No missing data value found for this variable: assigning a value of -9999");
                _MissingValue = -9999;
            }

            // Possible names for the latitude dimension in the NetCDF file
            SearchStrings = new string[] { "lat", "Lat", "latitude", "Latitude", "lats", "Lats", "latitudes", "Latitudes", "y", "Y" };
            // Check which position the latitude dimension is in in the NetCDF file and add this to the vector of positions. If the latitude dimension cannot be
            // found then throw an error
            if (SearchStrings.Contains(internalData.Dimensions[0].Name.ToString()))
            {
                positions[0] = 1;
            }
            else if (SearchStrings.Contains(internalData.Dimensions[1].Name.ToString()))
            {
                positions[1] = 1;
            }
            else
            {
                Debug.Fail("Cannot find a latitude dimension");
            }

            // Possible names for the longitude dimension in the netCDF file
            SearchStrings = new string[] { "lon", "Lon", "longitude", "Longitude", "lons", "Lons", "long", "Long", "longs", "Longs", "longitudes", "Longitudes", "x", "X" };
            // Check which position the latitude dimension is in in the NetCDF file and add this to the vector of positions. If the latitude dimension cannot be
            // found then throw an error
            if (SearchStrings.Contains(internalData.Dimensions[0].Name.ToString()))
            {
                positions[0] = 2;
            }
            else if (SearchStrings.Contains(internalData.Dimensions[1].Name.ToString()))
            {
                positions[1] = 2;
            }
            else
            {
                Debug.Fail("Cannot find a longitude dimension");
            }

            // Check the format of the specified environmental variable
            if (internalData.Variables[dataName].TypeOfData.Name.ToString().ToLower() == "single")
            {
                // Read the environmental data into a temporary array
                Single[,] TempArray;
                TempArray = internalData.GetData<Single[,]>(dataName);
                // Convert the data to double format and add to the unsorted array of values, transposing the data to be dimensioned by latitude first and longitude second
                for (int ii = 0; ii < _NumLats; ii++)
                {
                    for (int jj = 0; jj < _NumLons; jj++)
                    {
                        LatLongArrayUnsorted[ii, jj] = TempArray[(int)(ii * Convert.ToDouble(positions[0] == 1) + jj * Convert.ToDouble(positions[0] == 2)),
                            (int)(ii * Convert.ToDouble(positions[1] == 1) + jj * Convert.ToDouble(positions[1] == 2))];
                    }
                }
            }
            else if (internalData.Variables[dataName].TypeOfData.Name.ToString().ToLower() == "double")
            {
                // Read the environmental data into a temporary array
                double[,] TempArray;
                TempArray = internalData.GetData<double[,]>(dataName);
                // Add the data to the unsorted array of values, transposing the data to be dimensioned by latitude first and longitude second
                for (int ii = 0; ii < _NumLats; ii++)
                {
                    for (int jj = 0; jj < _NumLons; jj++)
                    {
                        LatLongArrayUnsorted[ii, jj] = TempArray[(int)(ii * Convert.ToDouble(positions[0] == 1) + jj * Convert.ToDouble(positions[0] == 2)), (int)(ii * Convert.ToDouble(positions[1] == 1) + jj * Convert.ToDouble(positions[1] == 2))];
                    }
                }
            }
            else if (internalData.Variables[dataName].TypeOfData.Name.ToString().ToLower() == "int32")
            {
                // Read the environmental data into a temporary array
                Int32[,] TempArray;
                TempArray = internalData.GetData<Int32[,]>(dataName);
                // Convert the data to double format and add to the unsorted array of values, transposing the data to be dimensioned by latitude first and longitude second
                for (int ii = 0; ii < _NumLats; ii++)
                {
                    for (int jj = 0; jj < _NumLons; jj++)
                    {
                        LatLongArrayUnsorted[ii, jj] = TempArray[(int)(ii * Convert.ToDouble(positions[0] == 1) + jj * Convert.ToDouble(positions[0] == 2)), (int)(ii * Convert.ToDouble(positions[1] == 1) + jj * Convert.ToDouble(positions[1] == 2))];
                    }
                }
            }
            else
            {
                // Format of environmental data not recognized so throw an error
                Debug.Fail("Environmental data are in an unrecognized format");
            }

            // Transpose the environmental data so that they are in ascending order of both latitude and longitude
            for (int ii = 0; ii < _NumLats; ii++)
            {
                for (int jj = 0; jj < _NumLons; jj++)
                {
                    LatLongArraySorted[ii, jj] = LatLongArrayUnsorted[(int)((1 - Convert.ToDouble(latInverted)) * ii + (Convert.ToDouble(latInverted) * (LatLongArrayUnsorted.GetLength(0) - 1 - ii))), (int)((1 - Convert.ToDouble(longInverted)) * jj + (Convert.ToDouble(longInverted) * (LatLongArrayUnsorted.GetLength(1) - 1 - jj)))];
                }
            }
            // Add the final array to the class field for environmental data values
            _DataArray.Add(LatLongArraySorted);

            // If either latitude or longitude were inverted, then reverse their values in the class fields
            if (latInverted)
            {
                // Temporary vector to store inverted latitude values
                double[] tempLats = new double[_NumLats];
                // Loop over latitude values
                for (int ii = 0; ii < _NumLats; ii++)
                {
                    // Invert the values in the temporary vector
                    tempLats[ii] = _Lats[_Lats.Length - 1 - ii];
                }
                // Overwrite the old vector of latitude values with the inverted values
                _Lats = tempLats;
                // Reverse the sign on the difference in latitude values between adjacent cells
                _LatStep = -_LatStep;
            }
            if (longInverted)
            {
                // Temporary vector to store inverted longitude values
                double[] tempLongs = new double[_NumLons];
                // Loop over longitude values
                for (int jj = 0; jj < _NumLons; jj++)
                {
                    // Invert the values in the temporary vector
                    tempLongs[jj] = _Lons[_Lons.Length - 1 - jj];
                }
                // Overwrite the old vector of longitude values with the inverted values
                _Lons = tempLongs;
                // Reverse the sign on the difference in longitude values between adjacent cells
                _LonStep = -_LonStep;
            }

            // Check that the increment in both latitudes and longitudes between consecutive grid cells is now positive
            Debug.Assert(_LatStep > 0.0, "Latitudes are still inverted in an environmental variable stored in EnviroData");
            Debug.Assert(_LonStep > 0.0, "Longitudes are still inverted in an environmental variable stored in EnviroData");

        }
		/// <summary>
		/// 
		/// </summary>
		/// <param name="failedDataSet"></param>
		/// <param name="inner"></param>
		public DistributedCommitFailedException(DataSet failedDataSet, Exception inner)
			: base("DataSet " + failedDataSet.URI + " commit failed", inner) 
		{
			failed = failedDataSet;
		}
        public void Copy(OutputGlobal existing)
        {
            this.BasicOutput.Dispose();
            this.BasicOutput = null;

            System.IO.File.Copy(existing.FileName, this.FileName, true);

            var fileString = "msds:nc?file=" + this.FileName;
            this.BasicOutput = DataSet.Open(fileString);
        }
Beispiel #24
0
 public DataSetCommittingEventArgs(DataSet sds, DataSet.Changes changes)
 {
     this.sds = sds;
     this.cancel = false;
     this.changes = changes;
 }
Beispiel #25
0
 public DataSetRolledBackEventArgs(DataSet sds)
 {
     this.sds = sds;
 }
Beispiel #26
0
        /// <summary>
        /// Processes request <see cref="DataSet"/> via Fetch Climate.
        /// </summary>
        /// <param name="ds">Request <see cref="DataSet"/> with fetching parameters.</param>
        /// <returns>DataSet with response.</returns>
        ///  <remarks>
        /// <para>
        /// There are two types of responses from server:
        /// If request time is lower than <see cref="maxExecutionTimeWithoutReturns"/> time, 
        /// <see cref="DataSet"/> with request result will be returned.
        /// If request time exceeds <see cref="maxExecutionTimeWithoutReturns"/> time, <see cref="DataSet"/> 
        /// instance with requets hash and next time to request ("status response") will be returned instead of <see cref="DataSet"/> with result.
        /// Pass it to the <see cref="Process"/> method after specified time interval again to get the result.
        /// If client got status response, it must send it back to server to get next status response, or result. But there is exception in this rule (see next).
        /// For status response, there are three matdata attributes in it:
        /// -"ExpectedCalculationTime" is expected time, calculation will take.
        /// Client should request next time after this time will pass.
        /// -"Hash" is hash of original request dataset.
        /// -"ReplyWithRequestDs" indicates, whether client needs to resend request
        /// next time or not. If so, client needs to send entire request next time
        /// instead of status response, it got from server.
        /// </para>
        /// </remarks>
        public DataSet Process(DataSet ds)
        {
            int retryAttempt = 0;

            int retryTimeDelta = RestApiWrapper.retryStartTimeDelta;

            while (true)
            {
                if (retryAttempt > 1)
                {
                    //Increase retry time delta, if not first attempt and not first retry.
                    retryTimeDelta = (int)(Math.Round(retryTimeDelta * RestApiWrapper.retryTimeIncreaseCoeff));
                }

                if (retryAttempt > retriesCount)
                    break;
                else if (retryAttempt > 0)
                    Thread.Sleep(retryTimeDelta);

                try
                {
                    string login = null;
                    string password = null;

                    CredentialsManager.GetCredentials(out login, out password);

                    HttpWebRequest request = WebRequest.Create(serviceUrl) as HttpWebRequest;
                    var credentialCache = new CredentialCache();
                    credentialCache.Add(
                        new Uri(serviceUrl),
                        "Digest",
                        new NetworkCredential(login, password)
                    );
                    request.Credentials = credentialCache;
                    request.PreAuthenticate = true;
                    request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
                    request.Method = RestApiNamings.postRequestMethodName;
                    //request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
                    request.ContentType = RestApiNamings.textRequestTypeName;
                    request.Timeout = RestApiWrapper.requestTimeout;

                    byte[] csvBytes = RestApiUtilities.GetCsvBytes(ds);

                    request.ContentLength = csvBytes.Length;

                    using (Stream dataStream = request.GetRequestStream())
                    {
                        dataStream.Write(csvBytes, 0, csvBytes.Length);
                    }

                    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                    {
                        if (response.ContentType == RestApiNamings.textRequestTypeName)
                        {
                            //DataSet is in response
                            using (Stream dataStream = response.GetResponseStream())
                            {
                                byte[] responseBytes = RestApiUtilities.ReadBytes(dataStream, (int)response.ContentLength);
                                DataSet resultDs = RestApiUtilities.GetDataSet(responseBytes);
                                return resultDs;
                            }
                        }
                        else
                        {
                            throw new InvalidOperationException("Unexpected exception. You should never see this");
                        }
                    }
                }
                catch (WebException)
                {
                    Trace.WriteIf(RestApiWrapper.Tracer.TraceWarning, string.Format("Web exception occured while processing input dataSet. {0} retry attempts left", RestApiWrapper.retriesCount - retryAttempt));
                    retryAttempt++;
                }
            }

            throw new WebException("Failed to connect to service. Make sure, it's available.");
        }
 /// <summary>
 /// Sets up the outputs associated with all levels of output detail
 /// </summary>
 /// <param name="numTimeSteps">The number of time steps in the model run</param>
 /// <param name="ecosystemModelGrid">The model grid</param>
 private void SetUpLowLevelOutputs(uint numTimeSteps, ModelGrid ecosystemModelGrid)
 {
     // Create an SDS object to hold total abundance and biomass data
     // BasicOutput = SDSCreator.CreateSDS("netCDF", "BasicOutputs" + _OutputSuffix, _OutputPath);
     BasicOutputMemory = SDSCreator.CreateSDSInMemory(true);
     string[] TimeDimension = { "Time step" };
     DataConverter.AddVariable(BasicOutputMemory, "Total Biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
     DataConverter.AddVariable(BasicOutputMemory, "Heterotroph Abundance density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
     DataConverter.AddVariable(BasicOutputMemory, "Heterotroph Biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
 }
        public void Copy(OutputCell existing)
        {
            this.BasicOutputMemory.Dispose();
            this.BasicOutputMemory = null;

            // Somehow this reverses the variables, so do it again to reverse them back again
            var filename = "msds:nc?file=" + existing.FileName + "&openMode=readOnly";

            if (System.IO.File.Exists(existing.FileName))
            {
                var dataSet = DataSet.Open(filename);
                this.BasicOutputMemory = dataSet.Clone("msds:memory");

                dataSet.Dispose();
                dataSet = null;
            }
            else if (existing.BasicOutputMemory.IsDisposed == false)
            {
                var dataSet = existing.BasicOutputMemory.Clone("msds:memory");
                this.BasicOutputMemory = dataSet.Clone("msds:memory");

                dataSet.Dispose();
                dataSet = null;
            }
        }
		public void SolveAll(string ctorString)
		{
			//dataSet = ProxyDataSet.Open("msds:nc?file=../../../temp.nc");
			dataSet = ProxyDataSet.Open("msds:memory");

			//Инициализируем DataSet
			Variable X = dataSet.AddVariable<double>("X", "x");
			Variable Y = dataSet.AddVariable<double>("Y", "y");
			Variable Z = dataSet.AddVariable<double>("Z", "z");
			Variable time = dataSet.AddVariable<double>("Time", "t");

			Variable u = dataSet.AddVariable<double>("U velocity", "x", "y", "z", "t");
			Variable v = dataSet.AddVariable<double>("V velocity", "x", "y", "z", "t");
			Variable w = dataSet.AddVariable<double>("W velocity", "x", "y", "z", "t");
			Variable T = dataSet.AddVariable<double>("Temperature", "x", "y", "z", "t");
			Variable div = dataSet.AddVariable<double>("Divergence", "x", "y", "z", "t");
			dataSet.Commit();

			double[] wArr = new double[modellingParams.Nx];
			for (int i = 0; i < modellingParams.Nx; i++)
			{
				wArr[i] = i * modellingParams.Dx;
			}
			X.PutData(wArr);
			wArr = new double[modellingParams.Ny];
			for (int i = 0; i < modellingParams.Ny; i++)
			{
				wArr[i] = i * modellingParams.Dy;
			}
			Y.PutData(wArr);
			wArr = new double[modellingParams.Nz];
			for (int i = 0; i < modellingParams.Nz; i++)
			{
				wArr[i] = i * modellingParams.Dz;
			}
			Z.PutData(wArr);

			//Инициализируем рассчетный модуль для слоя начальными условиями
			LayerSolver solver = new LayerSolver(prevData, modellingParams);
			u.Append(prevData.U.ToArray(), "t");
			v.Append(prevData.V.ToArray(), "t");
			w.Append(prevData.W.ToArray(), "t");
			T.Append(prevData.T.ToArray(), "t");
			div.Append(prevData.Div.ToArray(), "t");

			time.PutData(new double[1] { 0 });
			dataSet.Commit();

			//Основной рассчет
			for (int i = 1; i < modellingParams.Nt; i++)
			{
				LayerData result = solver.Solve(true);
				//Кладем данные в DataSet
				u.Append(result.U.ToArray(), "t");
				v.Append(result.V.ToArray(), "t");
				w.Append(result.W.ToArray(), "t");
				T.Append(result.T.ToArray(), "t");
				div.Append(result.Div.ToArray(), "t");
				time.Append(new double[1] { (double)i / modellingParams.Nt });
				dataSet.Commit();
				//Переходим на следующий слой
				solver = new LayerSolver(prevData, result, modellingParams);
				prevData = result;

				double temp = 0;
				int count = 0;
				for (int ii = 1; ii < result.Width; ii++)
				{
					for (int jj = 1; jj < result.Height; jj++)
					{
						for (int kk = 1; kk < result.Thickness; kk++)
						{
							temp += result.Div[ii, jj, kk];
							count++;
						}
					}
				}
				temp = temp / count * modellingParams.Dx * modellingParams.Dy * modellingParams.Dz;

				Console.WriteLine((double)i / modellingParams.Nt * 100 + "% Error = " + temp);
			}
			dataSet.Commit();
		}
        /// <summary>
        /// Constructor for the functional group definitions: reads in the specified functional group definition file, 
        /// constructs lookup tables, mass ranges and initial cohort numbers in each functional group
        /// </summary>
        /// <param name="fileName">The name of the functional group definition file to be read in</param>
        /// <param name="outputPath">The path to the output folder, in which to copy the functional group definitions file</param>
        public FunctionalGroupDefinitions(string fileName, string outputPath)
        {
            // Construct the URI for the functional group definition file
            string FileString = "msds:csv?file=input/Model setup/ecological definition files/" + fileName + "&openMode=readOnly";

            // Copy the Function group definitions file to the output directory
            System.IO.File.Copy("input/Model setup/ecological definition files/" + fileName, outputPath + fileName, true);

            // Read in the data
            InternalData = DataSet.Open(FileString);

            // Initialise the lists
            _AllFunctionalGroupsIndex = new int[InternalData.Dimensions[0].Length];
            _FunctionalGroupProperties = new SortedList<string, double[]>();

            // Loop over columns in the functional group definitions file
            foreach (Variable v in InternalData.Variables)
            {
                // Get the column header
                string TraitName = v.Name.Split('_')[1].ToLower();
                // Get the values in this column
                var TempValues = v.GetData();

                // For functional group definitions
                if (System.Text.RegularExpressions.Regex.IsMatch(v.Name, "DEFINITION_"))
                {
                    // Declare a sorted dictionary to hold the index values for each unique trait value
                    SortedDictionary<string, int[]> TraitIndexValuesList = new SortedDictionary<string, int[]>();
                    // Create a string array with the values of this trait
                    string[] TempString = new string[TempValues.Length];
                    for (int nn = 0; nn < TempValues.Length; nn++)
                    {
                        TempString[nn] = TempValues.GetValue(nn).ToString().ToLower();

                        // Add the functional group index to the list of all indices
                        _AllFunctionalGroupsIndex[nn] = nn;
                    }
                    // Add the trait values to the trait-value lookup list
                    TraitLookupFromIndex.Add(TraitName, TempString);

                    // Get the unique values for this trait
                    var DistinctValues = TempString.Distinct().ToArray();
                    //Loop over the unique values for this trait and list all the functional group indices with the value
                    foreach (string DistinctTraitValue in DistinctValues.ToArray())
                    {
                        List<int> FunctionalGroupIndex = new List<int>();
                        //Loop over the string array associated with this trait and add the index values of matching string to a list
                        for (int kk = 0; kk < TempString.Length; kk++)
                        {
                            if (TempString[kk].Equals(DistinctTraitValue))
                            {
                                FunctionalGroupIndex.Add(kk);
                            }
                        }
                        //Add the unique trait value and the functional group indices to the temporary list
                        TraitIndexValuesList.Add(DistinctTraitValue, FunctionalGroupIndex.ToArray());
                    }
                    // Add the unique trait values and corresponding functional group indices to the functional group index lookup
                    IndexLookupFromTrait.Add(TraitName, TraitIndexValuesList);
                }
                // For functional group properties
                else if (System.Text.RegularExpressions.Regex.IsMatch(v.Name, "PROPERTY_"))
                {
                    // Get the values for this property
                    double[] TempDouble = new double[TempValues.Length];
                    for (int nn = 0; nn < TempValues.Length; nn++)
                    {
                        TempDouble[nn] = Convert.ToDouble(TempValues.GetValue(nn));
                    }
                    // Add the values to the list of functional group properties
                    _FunctionalGroupProperties.Add(TraitName, TempDouble);
                }
                else if (System.Text.RegularExpressions.Regex.IsMatch(v.Name, "NOTES_"))
                {
                    // Ignore
                }
                // Otherwise, throw an error
                else
                {
                    Debug.Fail("All functional group data must be prefixed by DEFINITTION OR PROPERTY");
                }

            }
        }