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

                }
            }

        }
		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();
		}
        /// <summary>
        /// Creates and adds the variable as an axis to the coordinate system and related data set.
        /// </summary>
        /// <param name="name">Name of the new variable.</param>
        /// <param name="dims">Names of dimensions the variable depends on.</param>
        /// <typeparam name="DataType">Data type of the axis.</typeparam>
        /// <returns>The variable just created and added as an axis.</returns>
        internal Variable <DataType> AddAxis <DataType>(string name, params string[] dims)
        {
            if (committed)
            {
                throw new NotSupportedException("Coordinate system cannot be changed after committing.");
            }

            if (axes.Contains(name))
            {
                throw new Exception("The coordinate system already contains an axis with the same name.");
            }

            Variable <DataType> var = sds.AddVariable <DataType>(name, dims);

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

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

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

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

            }

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

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

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

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

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

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

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

                // Commit the changes
                SDSObject.Commit();

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

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

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

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

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

                // Commit changes
                SDSObject.Commit();
            }

        }
        public void WriteFeedingInteractions(uint timestep)
        {
            int nrows = FeedingInteractionsMatrixPredation.GetLength(0);
            int ncols = FeedingInteractionsMatrixPredation.GetLength(1);

            int MaxLengthPredation = 0;

            for (int i = 0; i < nrows; i++)
            {
                for (int j = 0; j < ncols; j++)
                {
                    if (FeedingInteractionsMatrixPredation[i, j].Count > MaxLengthPredation) MaxLengthPredation = FeedingInteractionsMatrixPredation[i, j].Count;
                }
            }

            double[, ,] FGIndicesPredation = new double[nrows, ncols, MaxLengthPredation];
            double[, ,] FGIndicesHerbivory = new double[nrows, ncols, 2];

            double[, ,] CIndicesPredation = new double[nrows, ncols, MaxLengthPredation];
            double[, ,] CIndicesHerbivory = new double[nrows, ncols, 2];

            double[, ,] BiomassAssimilatedPredation = new double[nrows, ncols, MaxLengthPredation];
            double[, ,] BiomassAssimilatedHerbivory = new double[nrows, ncols, 2];

            double[, ,] BiomassIngestedPredation = new double[nrows, ncols, MaxLengthPredation];
            double[, ,] BiomassIngestedHerbivory = new double[nrows, ncols, 2];

            float[] FGIndices = new float[nrows];
            float[] CIndices = new float[ncols];
            float[] PredationIndices = new float[MaxLengthPredation];
            float[] HerbivoryIndices = new float[2] {0,1};

            for (int i = 0; i < nrows; i++)
            {
                FGIndices[i] = i;
            }

            for (int i = 0; i < ncols; i++)
            {
                CIndices[i] = i;
            }

            for (int i = 0; i < MaxLengthPredation; i++)
            {
                PredationIndices[i] = i;
            }

            for (int f = 0; f < nrows; f++)
            {
                for (int c = 0; c < ncols; c++)
                {

                    //Populate output data matrices for predation feeding events
                    for (int i = 0; i < FeedingInteractionsMatrixPredation[f, c].Count; i++)
                    {
                        FGIndicesPredation[f, c, i] = Convert.ToDouble(FeedingInteractionsMatrixPredation[f, c].ElementAt(i).Item1);
                        CIndicesPredation[f, c, i] = Convert.ToDouble(FeedingInteractionsMatrixPredation[f, c].ElementAt(i).Item2);
                        BiomassAssimilatedPredation[f, c, i] = (FeedingInteractionsMatrixPredation[f, c].ElementAt(i).Item3);
                        BiomassIngestedPredation[f, c, i] = (FeedingInteractionsMatrixPredation[f, c].ElementAt(i).Item4);
                    }

                    //Populate output data matrices for herbivory feeding events
                    for (int i = 0; i < FeedingInteractionsMatrixHerbivory[f,c].Count; i++)
                    {
                        FGIndicesHerbivory[f, c, i] = Convert.ToDouble(FeedingInteractionsMatrixHerbivory[f, c].ElementAt(i).Item1);
                        CIndicesHerbivory[f, c, i] = Convert.ToDouble(FeedingInteractionsMatrixHerbivory[f, c].ElementAt(i).Item2);
                        BiomassAssimilatedHerbivory[f, c, i] = (FeedingInteractionsMatrixHerbivory[f, c].ElementAt(i).Item3);
                        BiomassIngestedHerbivory[f, c, i] = (FeedingInteractionsMatrixHerbivory[f, c].ElementAt(i).Item4);

                    }
                }
            }

            // 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
            _FeedingInteractionsDS = SDSCreator.CreateSDS("netCDF", _Filename + _OutputFileSuffix + timestep, _OutputPath);

            string[] PredationDimensions = new string[3] {"Functional Group Indices","Cohort Indices","Predation Interaction Dimension"};
            string[] HerbivoryDimensions = new string[3] { "Functional Group Indices", "Cohort Indices", "Herbivory Interaction Dimension" };

            string[] PredD1 = new string [1] { PredationDimensions[0] };
            string[] PredD2 = new string[1] { PredationDimensions[1] };
            string[] PredD3 = new string[1] { PredationDimensions[2] };
            string[] HerbD3 = new string[1] { HerbivoryDimensions[2] };

            DataConverter.AddVariable(_FeedingInteractionsDS, PredationDimensions[0], "index", 1, PredD1, _MV, FGIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, PredationDimensions[1], "index", 1, PredD2, _MV, CIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, PredationDimensions[2], "index", 1, PredD3, _MV, PredationIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, HerbivoryDimensions[2], "index", 1, HerbD3, _MV, HerbivoryIndices);

            /*DataConverter.AddVariable(_FeedingInteractionsDS, "Predation Interactions Functional Groups", 3, PredationDimensions, _MV, FGIndices, CIndices, PredationIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, "Predation Interactions Cohort Index", 3, PredationDimensions, _MV, FGIndices, CIndices, PredationIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, "Predation Interactions Biomass Assimilated", 3, PredationDimensions, _MV, FGIndices, CIndices, PredationIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, "Predation Interactions Biomass Ingested", 3, PredationDimensions, _MV, FGIndices, CIndices, PredationIndices);

            DataConverter.AddVariable(_FeedingInteractionsDS, "Herbivory Interactions Functional Groups", 3, HerbivoryDimensions, _MV, FGIndices, CIndices, HerbivoryIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, "Herbivory Interactions Stock Index", 3, HerbivoryDimensions, _MV, FGIndices, CIndices, HerbivoryIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, "Herbivory Interactions Biomass Assimilated", 3, HerbivoryDimensions, _MV, FGIndices, CIndices, HerbivoryIndices);
            DataConverter.AddVariable(_FeedingInteractionsDS, "Herbivory Interactions Biomass Ingested", 3, HerbivoryDimensions, _MV, FGIndices, CIndices, HerbivoryIndices);*/

            //Add variable to SDS
            var FGIPredOut = _FeedingInteractionsDS.AddVariable<double>("Predation Interactions Functional Groups", FGIndicesPredation, PredationDimensions);
            FGIPredOut.Metadata["DisplayName"] = "Predation Interactions Functional Groups";
            FGIPredOut.Metadata["MissingValue"] = _MV;

            //Add variable to SDS
            var CIPredOut = _FeedingInteractionsDS.AddVariable<double>("Predation Interactions Cohort Index", CIndicesPredation, PredationDimensions);
            CIPredOut.Metadata["DisplayName"] = "Predation Interactions Cohort Index";
            CIPredOut.Metadata["MissingValue"] = _MV;

            //Add variable to SDS
            var BAPredOut = _FeedingInteractionsDS.AddVariable<double>("Predation Interactions Biomass Assimilated", BiomassAssimilatedPredation, PredationDimensions);
            BAPredOut.Metadata["DisplayName"] = "Predation Interactions Biomass Assimilated";
            BAPredOut.Metadata["MissingValue"] = _MV;

            //Add variable to SDS
            var BIPredOut = _FeedingInteractionsDS.AddVariable<double>("Predation Interactions Biomass Ingested", BiomassIngestedPredation, PredationDimensions);
            BIPredOut.Metadata["DisplayName"] = "Predation Interactions Biomass Ingested";
            BIPredOut.Metadata["MissingValue"] = _MV;

            //Add variable to SDS
            var FGIHerbOut = _FeedingInteractionsDS.AddVariable<double>("Herbivory Interactions Functional Groups", FGIndicesHerbivory, HerbivoryDimensions);
            FGIHerbOut.Metadata["DisplayName"] = "Herbivory Interactions Functional Groups";
            FGIHerbOut.Metadata["MissingValue"] = _MV;

            //Add variable to SDS
            var CIHerbOut = _FeedingInteractionsDS.AddVariable<double>("Herbivory Interactions Cohort Index", CIndicesHerbivory, HerbivoryDimensions);
            CIHerbOut.Metadata["DisplayName"] = "Herbivory Interactions Cohort Index";
            CIHerbOut.Metadata["MissingValue"] = _MV;

            //Add variable to SDS
            var BAHerbOut = _FeedingInteractionsDS.AddVariable<double>("Herbivory Interactions Biomass Assimilated", BiomassAssimilatedHerbivory, HerbivoryDimensions);
            BAHerbOut.Metadata["DisplayName"] = "Herbivory Interactions Biomass Assimilated";
            BAHerbOut.Metadata["MissingValue"] = _MV;

            //Add variable to SDS
            var BIHerbOut = _FeedingInteractionsDS.AddVariable<double>("Herbivory Interactions Biomass Ingested", BiomassIngestedHerbivory, HerbivoryDimensions);
            BIHerbOut.Metadata["DisplayName"] = "Herbivory Interactions Biomass Ingested";
            BIHerbOut.Metadata["MissingValue"] = _MV;

            //Commit changes
            _FeedingInteractionsDS.Commit();
        }
Beispiel #9
0
        /// <summary>
        /// Copies given dataset into another dataset.
        /// </summary>
        /// <param name="src">Original dataset to copy.</param>
        /// <param name="dst">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, DataSet dst, ProgressUpdater updater)
        {
            if (src == null) throw new ArgumentNullException("src");
            if (dst == null) throw new ArgumentNullException("dst");
            if (dst.IsReadOnly)
                throw new NotSupportedException("Destination DataSet is read-only");

            // Maximum memory capacity in bytes
            ulong N = 200 * 1024 * 1024;
            // Estimated size of a single string in bytes
            int sizeofString = 100 * 1024;

            /***********************************************************************************
             * Preparing output
            ***********************************************************************************/
            bool isAutoCommit = dst.IsAutocommitEnabled;
            try
            {
                dst.IsAutocommitEnabled = false;

                DataSetSchema srcSchema = src.GetSchema();
                Dictionary<int, int> IDs = new Dictionary<int, int>();

                // Creating empty variables and copying global metadata and scalar variables
                if (updater != null)
                    updater(0, "Creating structure and copying global metadata and scalar variables...");
                VariableSchema globalMetadataVar = null;
                foreach (VariableSchema v in srcSchema.Variables)
                {
                    if (v.ID == DataSet.GlobalMetadataVariableID)
                    {
                        globalMetadataVar = v;
                        continue;
                    }

                    Variable t = dst.AddVariable(v.TypeOfData, v.Name, null, v.Dimensions.AsNamesArray());
                    IDs.Add(v.ID, t.ID);

                    foreach (var attr in v.Metadata)
                        t.Metadata[attr.Key] = attr.Value;

                    if (t.Rank == 0) // scalar
                        t.PutData(src.Variables.GetByID(v.ID).GetData());
                }
                if (globalMetadataVar != null)
                {
                    // Copying global metadata
                    foreach (var attr in globalMetadataVar.Metadata)
                        dst.Metadata[attr.Key] = attr.Value;
                }
                dst.Commit();
                // Console.Out.WriteLine("Done.\n");
                /***********************************************************************************
                 * Adjusting dimensions deltas
                ***********************************************************************************/
                Dimension[] srcDims = srcSchema.GetDimensions();
                Dictionary<string, int> deltas = new Dictionary<string, int>(srcDims.Length);
                foreach (var d in srcDims)
                    deltas[d.Name] = d.Length;

                // Console.Out.WriteLine("Total memory capacity: " + (N / 1024.0 / 1024.0).ToString("F2") + " Mb");
                ulong totalSize;
                do
                {
                    totalSize = 0;
                    foreach (var var in srcSchema.Variables)
                    {
                        if (var.Rank == 0) continue; // scalar
                        int typeSize = SizeOf(var.TypeOfData, sizeofString);

                        ulong count = 0;
                        foreach (var vdim in var.Dimensions)
                        {
                            int dimDelta = deltas[vdim.Name];
                            if (count == 0) count = (ulong)dimDelta;
                            else count *= (ulong)dimDelta;
                        }
                        totalSize += (ulong)typeSize * count;
                    }
                    if (totalSize > N)
                    {
                        string maxDim = null;
                        int max = int.MinValue;
                        foreach (var dim in deltas)
                            if (dim.Value > max)
                            {
                                max = dim.Value;
                                maxDim = dim.Key;
                            }
                        if (maxDim == null || max <= 1)
                            throw new NotSupportedException("Cannot copy the DataSet: it is too large to be copied entirely by the utility for the provided memory capacity");
                        deltas[maxDim] = max >> 1;
                    }
                } while (totalSize > N);

                // Printing deltas
                if (updater != null) updater(0, String.Format("Deltas for the dimensions adjusted (max iteration capacity: " + (totalSize / 1024.0 / 1024.0).ToString("F2") + " Mb)"));

                /***********************************************************************************
                 * Copying data
                ***********************************************************************************/
                // Console.WriteLine();
                if (updater != null) updater(0, "Copying data ...");
                Dictionary<int, int[]> origins = new Dictionary<int, int[]>(srcSchema.Variables.Length);
                Dictionary<int, int[]> shapes = new Dictionary<int, int[]>(srcSchema.Variables.Length);
                List<VariableSchema> copyVars = srcSchema.Variables.Where(vs =>
                    (vs.Rank > 0 && vs.ID != DataSet.GlobalMetadataVariableID)).ToList();

                Dictionary<string, int> dimOrigin = new Dictionary<string, int>(srcDims.Length);
                foreach (var d in srcDims)
                    dimOrigin[d.Name] = 0;

                Array.Sort(srcDims, (d1, d2) => d1.Length - d2.Length);
                int totalDims = srcDims.Length;

                do
                {
                    // for each variable:
                    for (int varIndex = copyVars.Count; --varIndex >= 0; )
                    {
                        VariableSchema var = copyVars[varIndex];
                        bool hasChanged = false;
                        // Getting its origin
                        int[] origin;
                        if (!origins.TryGetValue(var.ID, out origin))
                        {
                            origin = new int[var.Rank];
                            origins[var.ID] = origin;
                            hasChanged = true;
                        }
                        // Getting its shape
                        int[] shape;
                        if (!shapes.TryGetValue(var.ID, out shape))
                        {
                            shape = new int[var.Rank];
                            for (int i = 0; i < var.Dimensions.Count; i++)
                                shape[i] = deltas[var.Dimensions[i].Name];
                            shapes.Add(var.ID, shape);
                        }

                        // Updating origin for the variable:
                        if (!hasChanged)
                            for (int i = 0; i < shape.Length; i++)
                            {
                                int o = dimOrigin[var.Dimensions[i].Name];
                                if (origin[i] != o)
                                {
                                    hasChanged = true;
                                    origin[i] = o;
                                }
                            }
                        if (!hasChanged) // this block is already copied
                            continue;

                        bool doCopy = false;
                        bool shapeUpdated = false;
                        for (int i = 0; i < shape.Length; i++)
                        {
                            int s = origin[i] + shape[i];
                            int len = var.Dimensions[i].Length;
                            if (s > len)
                            {
                                if (!shapeUpdated)
                                {
                                    shapeUpdated = true;
                                    shape = (int[])shape.Clone();
                                }
                                shape[i] = len - origin[i];
                            }
                            if (shape[i] > 0) doCopy = true;
                        }

                        if (doCopy)
                        {
                            Array data = src.Variables.GetByID(var.ID).GetData(origin, shape);
                            // Compute real size here for strings
                            dst.Variables.GetByID(IDs[var.ID]).PutData(origin, data);
                        }
                        else // variable is copied
                        {
                            copyVars.RemoveAt(varIndex);
                        }
                    }
                    dst.Commit();

                    // Updating dimensions origin
                    bool isOver = true;
                    for (int i = 0; i < totalDims; i++)
                    {
                        Dimension dim = srcDims[i];
                        int origin = dimOrigin[dim.Name] + deltas[dim.Name];
                        if (origin < dim.Length)
                        {
                            dimOrigin[dim.Name] = origin;
                            isOver = false;
                            // Progress indicator
                            if (i == totalDims - 1)
                            {
                                double perc = (double)origin / dim.Length * 100.0;
                                if (updater != null) updater(perc, "Copying data ...");
                            }
                            break;
                        }
                        dimOrigin[dim.Name] = 0;
                    }
                    if (isOver) break;
                } while (copyVars.Count > 0);

                if (updater != null) updater(100.0, "Done.");
            }
            finally
            {
                dst.IsAutocommitEnabled = isAutoCommit;
            }

            return dst;
        }