Hdf5DataWriter.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, University of Oxford.
00004 All rights reserved.
00005 
00006 University of Oxford means the Chancellor, Masters and Scholars of the
00007 University of Oxford, having an administrative office at Wellington
00008 Square, Oxford OX1 2JD, UK.
00009 
00010 This file is part of Chaste.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014  * Redistributions of source code must retain the above copyright notice,
00015    this list of conditions and the following disclaimer.
00016  * Redistributions in binary form must reproduce the above copyright notice,
00017    this list of conditions and the following disclaimer in the documentation
00018    and/or other materials provided with the distribution.
00019  * Neither the name of the University of Oxford nor the names of its
00020    contributors may be used to endorse or promote products derived from this
00021    software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034 */
00035 
00036 /*
00037  * Implementation file for Hdf5DataWriter class.
00038  *
00039  */
00040 #include <set>
00041 #include <cstring> //For strcmp etc. Needed in gcc-4.4
00042 #include <boost/scoped_array.hpp>
00043 
00044 #include "Hdf5DataWriter.hpp"
00045 
00046 #include "Exception.hpp"
00047 #include "OutputFileHandler.hpp"
00048 #include "PetscTools.hpp"
00049 #include "Version.hpp"
00050 #include "MathsCustomFunctions.hpp"
00051 
00052 Hdf5DataWriter::Hdf5DataWriter(DistributedVectorFactory& rVectorFactory,
00053                                const std::string& rDirectory,
00054                                const std::string& rBaseName,
00055                                bool cleanDirectory,
00056                                bool extendData,
00057                                std::string datasetName)
00058     : AbstractHdf5Access(rDirectory, rBaseName, datasetName),
00059       mrVectorFactory(rVectorFactory),
00060       mCleanDirectory(cleanDirectory),
00061       mUseExistingFile(extendData),
00062       mIsInDefineMode(true),
00063       mIsFixedDimensionSet(false),
00064       mEstimatedUnlimitedLength(1u),
00065       mFileFixedDimensionSize(0u),
00066       mDataFixedDimensionSize(0u),
00067       mLo(mrVectorFactory.GetLow()),
00068       mHi(mrVectorFactory.GetHigh()),
00069       mNumberOwned(0u),
00070       mOffset(0u),
00071       mNeedExtend(false),
00072       mUseMatrixForIncompleteData(false),
00073       mCurrentTimeStep(0),
00074       mSinglePermutation(NULL),
00075       mDoublePermutation(NULL),
00076       mSingleIncompleteOutputMatrix(NULL),
00077       mDoubleIncompleteOutputMatrix(NULL),
00078       mUseOptimalChunkSizeAlgorithm(true),
00079       mNumberOfChunks(0u)
00080 {
00081     mChunkSize[0] = 0;
00082     mChunkSize[1] = 0;
00083     mChunkSize[2] = 0;
00084     mFixedChunkSize[0] = 0;
00085     mFixedChunkSize[1] = 0;
00086     mFixedChunkSize[2] = 0;
00087 
00088     if (mUseExistingFile && mCleanDirectory)
00089     {
00090         EXCEPTION("You are asking to delete a file and then extend it, change arguments to constructor.");
00091     }
00092 
00093     if (!mUseExistingFile && mDatasetName != "Data")
00094     {
00095         //User is trying to add a new dataset, but they are not extending a existing file
00096         EXCEPTION("Adding new data only makes sense when extending an existing file");
00097     }
00098 
00099     if (mUseExistingFile)
00100     {
00101         // Variables should already be defined if we are extending.
00102         mIsInDefineMode = false;
00103 
00104         // If the file exists already, open it - this call will check it exists.
00105         OpenFile();
00106 
00107         // If the dataset we are interested in doesn't exist then close the file
00108         // We will go on to define variables and open the file as usual (except for it pre-existing).
00109         if (!DoesDatasetExist(mDatasetName))
00110         {
00111             //std::cout << "Dataset: " << mDatasetName << " doesn't exist in the file.\n";
00112             H5Fclose(mFileId);
00113             mIsInDefineMode = true;
00114         }
00115         // If dataset does exist then leave file open and try to extend it.
00116         else
00117         {
00118             // Where to find the file
00119             assert(mCleanDirectory==false);
00120 
00121             mVariablesDatasetId = H5Dopen(mFileId, mDatasetName.c_str());
00122             hid_t variables_dataspace = H5Dget_space(mVariablesDatasetId);
00123             //unsigned variables_dataset_rank = H5Sget_simple_extent_ndims(variables_dataspace);
00124             hsize_t dataset_max_sizes[DATASET_DIMS];
00125             H5Sget_simple_extent_dims(variables_dataspace, mDatasetDims, dataset_max_sizes); // put dims into mDatasetDims
00126             H5Sclose(variables_dataspace);
00127 
00128             // Check that an unlimited dimension is defined
00129             if (dataset_max_sizes[0] != H5S_UNLIMITED)
00130             {
00131                 H5Dclose(mVariablesDatasetId);
00132                 H5Fclose(mFileId);
00133                 EXCEPTION("Tried to open a datafile for extending which doesn't have an unlimited dimension.");
00134             }
00135             mIsUnlimitedDimensionSet = true;
00136 
00137             // Sanity check other dimension sizes
00138             for (unsigned i=1; i<DATASET_DIMS; i++)  // Zero is excluded since it is unlimited
00139             {
00140                 assert(mDatasetDims[i] == dataset_max_sizes[i]);
00141             }
00142             mFileFixedDimensionSize = mDatasetDims[1];
00143             mIsFixedDimensionSet = true;
00144             mVariables.reserve(mDatasetDims[2]);
00145 
00146             // Figure out what the variables are
00147             hid_t attribute_id = H5Aopen_name(mVariablesDatasetId, "Variable Details");
00148             hid_t attribute_type  = H5Aget_type(attribute_id);
00149             hid_t attribute_space = H5Aget_space(attribute_id);
00150             hsize_t attr_dataspace_dim;
00151             H5Sget_simple_extent_dims(attribute_space, &attr_dataspace_dim, NULL);
00152             unsigned num_columns = H5Sget_simple_extent_npoints(attribute_space);
00153             assert(num_columns == mDatasetDims[2]); // I think...
00154 
00155             char* string_array = (char *)malloc(sizeof(char)*MAX_STRING_SIZE*(int)num_columns);
00156             H5Aread(attribute_id, attribute_type, string_array);
00157 
00158             // Loop over columns/variables
00159             for (unsigned index=0; index<num_columns; index++)
00160             {
00161                 // Read the string from the raw vector
00162                 std::string column_name_unit(&string_array[MAX_STRING_SIZE*index]);
00163 
00164                 // Find location of unit name
00165                 size_t name_length = column_name_unit.find('(');
00166                 size_t unit_length = column_name_unit.find(')') - name_length - 1;
00167 
00168                 // Create the variable
00169                 DataWriterVariable var;
00170                 var.mVariableName = column_name_unit.substr(0, name_length);
00171                 var.mVariableUnits = column_name_unit.substr(name_length+1, unit_length);
00172                 mVariables.push_back(var);
00173             }
00174 
00175             // Free memory, release ids
00176             free(string_array);
00177             H5Tclose(attribute_type);
00178             H5Sclose(attribute_space);
00179             H5Aclose(attribute_id);
00180 
00181             // Now deal with time
00182             SetUnlimitedDatasetId();
00183 
00184             // How many time steps have been written so far?
00185             hid_t timestep_dataspace = H5Dget_space(mUnlimitedDatasetId);
00186             hsize_t num_timesteps;
00187             H5Sget_simple_extent_dims(timestep_dataspace, &num_timesteps, NULL);
00188             H5Sclose(timestep_dataspace);
00189             mCurrentTimeStep = (long)num_timesteps - 1;
00190 
00191             // Incomplete data?
00192             attribute_id = H5Aopen_name(mVariablesDatasetId, "IsDataComplete");
00193             if (attribute_id < 0)
00194             {
00195     #define COVERAGE_IGNORE
00196                 // Old format, before we added the attribute.
00197                 EXCEPTION("Extending old-format files isn't supported.");
00198     #undef COVERAGE_IGNORE
00199             }
00200             else
00201             {
00202                 attribute_type = H5Aget_type(attribute_id);
00203                 attribute_space = H5Aget_space(attribute_id);
00204                 unsigned is_data_complete;
00205                 H5Aread(attribute_id, H5T_NATIVE_UINT, &is_data_complete);
00206                 mIsDataComplete = (is_data_complete == 1) ? true : false;
00207                 H5Tclose(attribute_type);
00208                 H5Sclose(attribute_space);
00209                 H5Aclose(attribute_id);
00210             }
00211             if (mIsDataComplete)
00212             {
00213                 mNumberOwned = mrVectorFactory.GetLocalOwnership();
00214                 mOffset = mLo;
00215                 mDataFixedDimensionSize = mFileFixedDimensionSize;
00216             }
00217             else
00218             {
00219                 // Read which nodes appear in the file (mIncompleteNodeIndices)
00220                 attribute_id = H5Aopen_name(mVariablesDatasetId, "NodeMap");
00221                 attribute_type  = H5Aget_type(attribute_id);
00222                 attribute_space = H5Aget_space(attribute_id);
00223 
00224                 // Get the dataset/dataspace dimensions
00225                 unsigned num_node_indices = H5Sget_simple_extent_npoints(attribute_space);
00226 
00227                 // Read data from hyperslab in the file into the hyperslab in memory
00228                 mIncompleteNodeIndices.clear();
00229                 mIncompleteNodeIndices.resize(num_node_indices);
00230                 H5Aread(attribute_id, H5T_NATIVE_UINT, &mIncompleteNodeIndices[0]);
00231 
00232                 // Release ids
00233                 H5Tclose(attribute_type);
00234                 H5Sclose(attribute_space);
00235                 H5Aclose(attribute_id);
00236 
00237                 // Set up what data we can
00238                 mNumberOwned = mrVectorFactory.GetLocalOwnership();
00239                 ComputeIncompleteOffset();
00243                 mDataFixedDimensionSize = UINT_MAX;
00244                 H5Dclose(mVariablesDatasetId);
00245                 H5Dclose(mUnlimitedDatasetId);
00246                 H5Fclose(mFileId);
00247                 EXCEPTION("Unable to extend an incomplete data file at present.");
00248             }
00249 
00250             // Done
00251             AdvanceAlongUnlimitedDimension();
00252         }
00253     }
00254 }
00255 
00256 Hdf5DataWriter::~Hdf5DataWriter()
00257 {
00258     Close();
00259 
00260     if (mSinglePermutation)
00261     {
00262         PetscTools::Destroy(mSinglePermutation);
00263     }
00264     if (mDoublePermutation)
00265     {
00266         PetscTools::Destroy(mDoublePermutation);
00267     }
00268     if (mSingleIncompleteOutputMatrix)
00269     {
00270         PetscTools::Destroy(mSingleIncompleteOutputMatrix);
00271     }
00272     if (mDoubleIncompleteOutputMatrix)
00273     {
00274         PetscTools::Destroy(mDoubleIncompleteOutputMatrix);
00275     }
00276 }
00277 
00278 void Hdf5DataWriter::OpenFile()
00279 {
00280     OutputFileHandler output_file_handler(mDirectory, mCleanDirectory);
00281     std::string file_name = mDirectory.GetAbsolutePath() + mBaseName + ".h5";
00282 
00283     if (mUseExistingFile)
00284     {
00285         FileFinder h5_file(file_name, RelativeTo::Absolute);
00286         if (!h5_file.Exists())
00287         {
00288             EXCEPTION("Hdf5DataWriter could not open " + file_name + " , as it does not exist.");
00289         }
00290     }
00291 
00292     // Set up a property list saying how we'll open the file
00293     hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
00294     H5Pset_fapl_mpio(fapl, PETSC_COMM_WORLD, MPI_INFO_NULL);
00295 
00296     // Set size of each dimension in main dataset.
00297     mDatasetDims[0] = mEstimatedUnlimitedLength; // While developing we got a non-documented "only the first dimension can be extendible" error.
00298     mDatasetDims[1] = mFileFixedDimensionSize; // or should this be mDataFixedDimensionSize?
00299     mDatasetDims[2] = mVariables.size();
00300 
00301     // Open the file and free the property list
00302     std::string attempting_to;
00303     if (mUseExistingFile)
00304     {
00305         mFileId = H5Fopen(file_name.c_str(), H5F_ACC_RDWR, fapl);
00306         attempting_to = "open";
00307     }
00308     else
00309     {
00310         // Do chunk size calculation now as it lets us optimise the size of the B tree (via H5Pset_istore_k), see:
00311         // http://hdf-forum.184993.n3.nabble.com/size-of-quot-write-operation-quot-with-pHDF5-td2636129.html#a2647633
00312         SetChunkSize();
00313         hid_t fcpl = H5Pcreate(H5P_FILE_CREATE);
00314         if (mNumberOfChunks>64) // Default parameter is 32, so don't go lower than that
00315         {
00316             H5Pset_istore_k(fcpl, (mNumberOfChunks+1)/2);
00317         }
00318         /*
00319          * Align objects to disk block size. Useful for striped filesystem e.g. Lustre
00320          * Ideally this should match the chunk size (see "target_size_in_bytes" below).
00321          */
00322         // H5Pset_alignment(fapl, 0, 1024*1024); // Align to 1 M blocks
00323 
00324         /*
00325          * The stripe size can be set on the directory just before creation of the H5
00326          * file, and set back to normal just after, so that the H5 file inherits these
00327          * settings, by uncommenting the lines below. Adjust the command for your
00328          * specific filesystem!
00329          */
00330         /*
00331         std::string command;
00332         // Turn on striping for directory, which the newly created HDF5 file will inherit.
00333         if ( PetscTools::AmMaster() )
00334         {
00335             // Increase stripe count
00336             command = "lfs setstripe --size 1M --count -1 "; // -1 means use all OSTs
00337             command.append(mDirectory.GetAbsolutePath());
00338             std::cout << command << std::endl;
00339             system(command.c_str());
00340         }
00341         */
00342 
00343         // Create file
00344         mFileId = H5Fcreate(file_name.c_str(), H5F_ACC_TRUNC, fcpl, fapl);
00345 
00346         /*
00347         // Turn off striping so other output files stay unstriped.
00348         if ( PetscTools::AmMaster() )
00349         {
00350             // Use one stripe
00351             command = "lfs setstripe --size 1M --count 1 ";
00352             command.append(mDirectory.GetAbsolutePath());
00353             std::cout << command << std::endl;
00354             system(command.c_str());
00355         }
00356         PetscTools::Barrier(); // Don't let other processes run away
00357         */
00358 
00359         attempting_to = "create";
00360         H5Pclose(fcpl);
00361     }
00362 
00363     H5Pclose(fapl);
00364 
00365     if (mFileId < 0)
00366     {
00367         mVariablesDatasetId = 0;
00368         EXCEPTION("Hdf5DataWriter could not " << attempting_to << " " << file_name <<
00369                   " , H5F" << attempting_to << " error code = " << mFileId);
00370     }
00371 }
00372 
00373 void Hdf5DataWriter::DefineFixedDimension(long dimensionSize)
00374 {
00375     if (!mIsInDefineMode)
00376     {
00377         EXCEPTION("Cannot define variables when not in Define mode");
00378     }
00379     if (dimensionSize < 1)
00380     {
00381         EXCEPTION("Fixed dimension must be at least 1 long");
00382     }
00383     if (mIsFixedDimensionSet)
00384     {
00385         EXCEPTION("Fixed dimension already set");
00386     }
00387 
00388     // Work out the ownership details
00389     mLo = mrVectorFactory.GetLow();
00390     mHi = mrVectorFactory.GetHigh();
00391     mNumberOwned = mrVectorFactory.GetLocalOwnership();
00392     mOffset = mLo;
00393     mFileFixedDimensionSize = dimensionSize;
00394     mDataFixedDimensionSize = dimensionSize;
00395     mIsFixedDimensionSet = true;
00396 }
00397 
00398 void Hdf5DataWriter::DefineFixedDimension(const std::vector<unsigned>& rNodesToOuput, long vecSize)
00399 {
00400     unsigned vector_size = rNodesToOuput.size();
00401 
00402     for (unsigned index=0; index < vector_size-1; index++)
00403     {
00404         if (rNodesToOuput[index] >= rNodesToOuput[index+1])
00405         {
00406             EXCEPTION("Input should be monotonic increasing");
00407         }
00408     }
00409 
00410     if ((int) rNodesToOuput.back() >= vecSize)
00411     {
00412         EXCEPTION("Vector size doesn't match nodes to output");
00413     }
00414 
00415     DefineFixedDimension(vecSize);
00416 
00417     mFileFixedDimensionSize = vector_size;
00418     mIsDataComplete = false;
00419     mIncompleteNodeIndices = rNodesToOuput;
00420     ComputeIncompleteOffset();
00421 }
00422 
00423 void Hdf5DataWriter::DefineFixedDimensionUsingMatrix(const std::vector<unsigned>& rNodesToOuput, long vecSize)
00424 {
00425     unsigned vector_size = rNodesToOuput.size();
00426 
00427     for (unsigned index=0; index < vector_size-1; index++)
00428     {
00429         if (rNodesToOuput[index] >= rNodesToOuput[index+1])
00430         {
00431             EXCEPTION("Input should be monotonic increasing");
00432         }
00433     }
00434 
00435     if ((int) rNodesToOuput.back() >= vecSize)
00436     {
00437         EXCEPTION("Vector size doesn't match nodes to output");
00438     }
00439 
00440     DefineFixedDimension(vecSize);
00441 
00442     mFileFixedDimensionSize = vector_size;
00443     mIsDataComplete = false;
00444     mIncompleteNodeIndices = rNodesToOuput;
00445     mUseMatrixForIncompleteData = true;
00446     ComputeIncompleteOffset();
00447 
00448     // Make sure we've not done it already
00449     assert(mSingleIncompleteOutputMatrix == NULL);
00450     assert(mDoubleIncompleteOutputMatrix == NULL);
00451     PetscTools::SetupMat(mSingleIncompleteOutputMatrix,   mFileFixedDimensionSize,   mDataFixedDimensionSize, 2,  mNumberOwned,  mHi - mLo);
00452     PetscTools::SetupMat(mDoubleIncompleteOutputMatrix, 2*mFileFixedDimensionSize, 2*mDataFixedDimensionSize, 4,  2*mNumberOwned, 2*(mHi - mLo));
00453 
00454     // Only do local rows
00455     for (unsigned row_index = mOffset; row_index < mOffset + mNumberOwned; row_index++)
00456     {
00457         // Put zero on the diagonal
00458         MatSetValue(mSingleIncompleteOutputMatrix, row_index, row_index, 0.0, INSERT_VALUES);
00459 
00460         // Put one at (i,j)
00461         MatSetValue(mSingleIncompleteOutputMatrix, row_index, rNodesToOuput[row_index], 1.0, INSERT_VALUES);
00462 
00463         unsigned bi_index = 2*row_index;
00464         unsigned perm_index = 2*rNodesToOuput[row_index];
00465 
00466         // Put zeroes on the diagonal
00467         MatSetValue(mDoubleIncompleteOutputMatrix, bi_index, bi_index, 0.0, INSERT_VALUES);
00468         MatSetValue(mDoubleIncompleteOutputMatrix, bi_index+1, bi_index+1, 0.0, INSERT_VALUES);
00469 
00470         // Put ones at (i,j)
00471         MatSetValue(mDoubleIncompleteOutputMatrix, bi_index, perm_index, 1.0, INSERT_VALUES);
00472         MatSetValue(mDoubleIncompleteOutputMatrix, bi_index+1, perm_index+1, 1.0, INSERT_VALUES);
00473     }
00474     MatAssemblyBegin(mSingleIncompleteOutputMatrix, MAT_FINAL_ASSEMBLY);
00475     MatAssemblyBegin(mDoubleIncompleteOutputMatrix, MAT_FINAL_ASSEMBLY);
00476     MatAssemblyEnd(mSingleIncompleteOutputMatrix, MAT_FINAL_ASSEMBLY);
00477     MatAssemblyEnd(mDoubleIncompleteOutputMatrix, MAT_FINAL_ASSEMBLY);
00478 
00479 //    MatView(mSingleIncompleteOutputMatrix, PETSC_VIEWER_STDOUT_WORLD);
00480 }
00481 
00482 void Hdf5DataWriter::ComputeIncompleteOffset()
00483 {
00484     mOffset = 0;
00485     mNumberOwned = 0;
00486     for (unsigned i=0; i<mIncompleteNodeIndices.size(); i++)
00487     {
00488         if (mIncompleteNodeIndices[i] < mLo)
00489         {
00490             mOffset++;
00491         }
00492         else if (mIncompleteNodeIndices[i] < mHi)
00493         {
00494             mNumberOwned++;
00495         }
00496     }
00497 }
00498 
00499 int Hdf5DataWriter::DefineVariable(const std::string& rVariableName,
00500                                    const std::string& rVariableUnits)
00501 {
00502     if (!mIsInDefineMode)
00503     {
00504         EXCEPTION("Cannot define variables when not in Define mode");
00505     }
00506 
00507     CheckVariableName(rVariableName);
00508     CheckUnitsName(rVariableUnits);
00509 
00510     // Check for the variable being already defined
00511     for (unsigned index=0; index<mVariables.size(); index++)
00512     {
00513         if (mVariables[index].mVariableName == rVariableName)
00514         {
00515             EXCEPTION("Variable name already exists");
00516         }
00517     }
00518 
00519     DataWriterVariable new_variable;
00520     new_variable.mVariableName = rVariableName;
00521     new_variable.mVariableUnits = rVariableUnits;
00522     int variable_id;
00523 
00524     // Add the variable to the variable vector
00525     mVariables.push_back(new_variable);
00526 
00527     // Use the index of the variable vector as the variable ID.
00528     // This is ok since there is no way to remove variables.
00529     variable_id = mVariables.size() - 1;
00530 
00531     return variable_id;
00532 }
00533 
00534 void Hdf5DataWriter::CheckVariableName(const std::string& rName)
00535 {
00536     if (rName.length() == 0)
00537     {
00538         EXCEPTION("Variable name not allowed: may not be blank.");
00539     }
00540     CheckUnitsName(rName);
00541 }
00542 
00543 void Hdf5DataWriter::CheckUnitsName(const std::string& rName)
00544 {
00545     for (unsigned i=0; i<rName.length(); i++)
00546     {
00547         if (!isalnum(rName[i]) && !(rName[i]=='_'))
00548         {
00549             std::string error = "Variable name/units '" + rName + "' not allowed: may only contain alphanumeric characters or '_'.";
00550             EXCEPTION(error);
00551         }
00552     }
00553 }
00554 
00555 void Hdf5DataWriter::EndDefineMode()
00556 {
00557     // Check that at least one variable has been defined
00558     if (mVariables.size() < 1)
00559     {
00560         EXCEPTION("Cannot end define mode. No variables have been defined.");
00561     }
00562 
00563     // Check that a fixed dimension has been defined
00564     if (mIsFixedDimensionSet == false)
00565     {
00566         EXCEPTION("Cannot end define mode. One fixed dimension should be defined.");
00567     }
00568 
00569     OpenFile();
00570 
00571     mIsInDefineMode = false;
00572 
00573     /*
00574      * Create "Data" dataset
00575      */
00576 
00577     // Set max dims of dataset
00578     hsize_t dataset_max_dims[DATASET_DIMS];
00579     if (mIsUnlimitedDimensionSet)
00580     {
00581         dataset_max_dims[0] = H5S_UNLIMITED;
00582     }
00583     else
00584     {
00585         dataset_max_dims[0] = 1;
00586     }
00587     dataset_max_dims[1] = mDatasetDims[1];
00588     dataset_max_dims[2] = mDatasetDims[2];
00589 
00590     // If we didn't already do the chunk calculation (e.g. we're adding a dataset to an existing H5 file)
00591     if (mNumberOfChunks==0)
00592     {
00593         SetChunkSize();
00594     }
00595 
00596     // Create chunked dataset and clean up
00597     hid_t cparms = H5Pcreate (H5P_DATASET_CREATE);
00598     H5Pset_chunk( cparms, DATASET_DIMS, mChunkSize);
00599     hid_t filespace = H5Screate_simple(DATASET_DIMS, mDatasetDims, dataset_max_dims);
00600     mVariablesDatasetId = H5Dcreate(mFileId, mDatasetName.c_str(), H5T_NATIVE_DOUBLE, filespace, cparms);
00601     SetMainDatasetRawChunkCache(); // Set large cache (even though parallel drivers don't currently use it!)
00602     H5Sclose(filespace);
00603     H5Pclose(cparms);
00604 
00605     // Create dataspace for the name, unit attribute
00606     const unsigned MAX_STRING_SIZE = 100;
00607     hsize_t columns[1] = {mVariables.size()};
00608     hid_t colspace = H5Screate_simple(1, columns, NULL);
00609 
00610     // Create attribute for variable names
00611     char* col_data = (char*) malloc(mVariables.size() * sizeof(char) * MAX_STRING_SIZE);
00612 
00613     char* col_data_offset = col_data;
00614     for (unsigned var=0; var<mVariables.size(); var++)
00615     {
00616         std::string full_name = mVariables[var].mVariableName + "(" + mVariables[var].mVariableUnits + ")";
00617         strcpy (col_data_offset, full_name.c_str());
00618         col_data_offset += sizeof(char) * MAX_STRING_SIZE;
00619     }
00620 
00621     // Create the type 'string'
00622     hid_t string_type = H5Tcopy(H5T_C_S1);
00623     H5Tset_size(string_type, MAX_STRING_SIZE );
00624     hid_t attr = H5Acreate(mVariablesDatasetId, "Variable Details", string_type, colspace, H5P_DEFAULT);
00625 
00626     // Write to the attribute
00627     H5Awrite(attr, string_type, col_data);
00628 
00629     // Close dataspace & attribute
00630     free(col_data);
00631     H5Sclose(colspace);
00632     H5Aclose(attr);
00633 
00634     // Create "boolean" attribute telling the data to be incomplete or not
00635     columns[0] = 1;
00636     colspace = H5Screate_simple(1, columns, NULL);
00637     attr = H5Acreate(mVariablesDatasetId, "IsDataComplete", H5T_NATIVE_UINT, colspace, H5P_DEFAULT);
00638 
00639     // Write to the attribute - note that the native boolean is not predictable
00640     unsigned is_data_complete = mIsDataComplete ? 1 : 0;
00641     H5Awrite(attr, H5T_NATIVE_UINT, &is_data_complete);
00642 
00643     H5Sclose(colspace);
00644     H5Aclose(attr);
00645 
00646     if (!mIsDataComplete)
00647     {
00648         // We need to write a map
00649         // Create "unsigned" attribute with the map
00650         columns[0] = mFileFixedDimensionSize;
00651         colspace = H5Screate_simple(1, columns, NULL);
00652         attr = H5Acreate(mVariablesDatasetId, "NodeMap", H5T_NATIVE_UINT, colspace, H5P_DEFAULT);
00653 
00654         // Write to the attribute
00655         H5Awrite(attr, H5T_NATIVE_UINT, &mIncompleteNodeIndices[0]);
00656 
00657         H5Sclose(colspace);
00658         H5Aclose(attr);
00659     }
00660 
00661     /*
00662      * Create "Time" dataset
00663      */
00664     if (mIsUnlimitedDimensionSet)
00665     {
00666         hsize_t time_dataset_dims[1] = {mEstimatedUnlimitedLength};
00667         hsize_t time_dataset_max_dims[1] = {H5S_UNLIMITED};
00668 
00669         /*
00670          * Modify dataset creation properties to enable chunking.  Set the chunk size in the "Time"
00671          * dataset to 128 doubles, i.e. 1 KiB.  See #2336.
00672          */
00673         hsize_t time_chunk_dims[1] = {128u};
00674         hid_t time_cparms = H5Pcreate (H5P_DATASET_CREATE);
00675         H5Pset_chunk( time_cparms, 1, time_chunk_dims);
00676 
00677         hid_t time_filespace = H5Screate_simple(1, time_dataset_dims, time_dataset_max_dims);
00678 
00679         // Create the unlimited dimension dataset
00680 
00681         // * Files post r18257 (inc. Release 3.2 onwards) use "<DatasetName>_Unlimited" for "<DatasetName>"'s
00682         //   unlimited variable,
00683         //   - a new attribute "Name" has been added to the Unlimited Dataset to allow it to assign
00684         //     any name to the unlimited variable. Which can then be easily read by Hdf5DataReader.
00685 
00686         mUnlimitedDatasetId = H5Dcreate(mFileId, (mDatasetName + "_Unlimited").c_str(), H5T_NATIVE_DOUBLE, time_filespace, time_cparms);
00687 
00688         // Create the dataspace for the attribute
00689         hsize_t one = 1;
00690         hid_t one_column_space = H5Screate_simple(1, &one, NULL);
00691 
00692         // Create an attribute for the time unit
00693         hid_t unit_attr = H5Acreate(mUnlimitedDatasetId, "Unit", string_type, one_column_space, H5P_DEFAULT);
00694 
00695         // Create an attribute for the time name
00696         hid_t name_attr = H5Acreate(mUnlimitedDatasetId, "Name", string_type, one_column_space, H5P_DEFAULT);
00697 
00698         // Copy the unit to a string MAX_STRING_SIZE long and write it
00699         char unit_string[MAX_STRING_SIZE];
00700         strcpy(unit_string, mUnlimitedDimensionUnit.c_str());
00701         H5Awrite(unit_attr, string_type, unit_string);
00702 
00703         // Copy the unit to a string MAX_STRING_SIZE long and write it
00704         char name_string[MAX_STRING_SIZE];
00705         strcpy(name_string, mUnlimitedDimensionName.c_str());
00706         H5Awrite(name_attr, string_type, name_string);
00707 
00708         // Close the filespace
00709         H5Pclose(time_cparms);
00710         H5Sclose(one_column_space);
00711         H5Aclose(unit_attr);
00712         H5Aclose(name_attr);
00713         H5Sclose(time_filespace);
00714     }
00715 
00716     /*
00717      * Create the provenance attribute
00718      */
00719 
00720     // Create a longer type for 'string'
00721     const unsigned MAX_PROVENANCE_STRING_SIZE = 1023;
00722     hid_t long_string_type = H5Tcopy(H5T_C_S1);
00723     H5Tset_size(long_string_type, MAX_PROVENANCE_STRING_SIZE );
00724     hsize_t prov_columns[1] = {1};
00725     hid_t provenance_space = H5Screate_simple(1, prov_columns, NULL);
00726     char* provenance_data = (char*) malloc(sizeof(char) * MAX_PROVENANCE_STRING_SIZE);
00727     assert( ChasteBuildInfo::GetProvenanceString().length() < MAX_PROVENANCE_STRING_SIZE);
00728 
00729     strcpy(provenance_data,  ChasteBuildInfo::GetProvenanceString().c_str());
00730     hid_t prov_attr = H5Acreate(mVariablesDatasetId, "Chaste Provenance", long_string_type, provenance_space, H5P_DEFAULT);
00731 
00732     // Write to the attribute
00733     H5Awrite(prov_attr, long_string_type, provenance_data);
00734 
00735     // Close dataspace & attribute
00736     free(provenance_data);
00737     H5Sclose(provenance_space);
00738     H5Aclose(prov_attr);
00739 }
00740 
00741 void Hdf5DataWriter::PutVector(int variableID, Vec petscVector)
00742 {
00743     if (mIsInDefineMode)
00744     {
00745         EXCEPTION("Cannot write data while in define mode.");
00746     }
00747 
00748     int vector_size;
00749     VecGetSize(petscVector, &vector_size);
00750 
00751     if ((unsigned) vector_size != mDataFixedDimensionSize)
00752     {
00753         EXCEPTION("Vector size doesn't match fixed dimension");
00754     }
00755 
00756     // Make sure that everything is actually extended to the correct dimension.
00757     PossiblyExtend();
00758 
00759     Vec output_petsc_vector;
00760 
00761     // Decide what to write
00762     if (mSinglePermutation == NULL)
00763     {
00764         // No permutation - just write
00765         output_petsc_vector = petscVector;
00766     }
00767     else
00768     {
00769         assert(mIsDataComplete);
00770         // Make a vector with the same pattern (doesn't copy the data)
00771         VecDuplicate(petscVector, &output_petsc_vector);
00772 
00773         // Apply the permutation matrix
00774         MatMult(mSinglePermutation, petscVector, output_petsc_vector);
00775     }
00776 
00777     // Define a dataset in memory for this process
00778     hid_t memspace=0;
00779     if (mNumberOwned != 0)
00780     {
00781         hsize_t v_size[1] = {mNumberOwned};
00782         memspace = H5Screate_simple(1, v_size, NULL);
00783     }
00784 
00785     // Select hyperslab in the file
00786     hsize_t count[DATASET_DIMS] = {1, mNumberOwned, 1};
00787     hsize_t offset_dims[DATASET_DIMS] = {mCurrentTimeStep, mOffset, (unsigned)(variableID)};
00788     hid_t file_dataspace = H5Dget_space(mVariablesDatasetId);
00789 
00790     // Create property list for collective dataset
00791     hid_t property_list_id = H5Pcreate(H5P_DATASET_XFER);
00792     H5Pset_dxpl_mpio(property_list_id, H5FD_MPIO_COLLECTIVE);
00793 
00794     H5Sselect_hyperslab(file_dataspace, H5S_SELECT_SET, offset_dims, NULL, count, NULL);
00795 
00796     double* p_petsc_vector;
00797     VecGetArray(output_petsc_vector, &p_petsc_vector);
00798 
00799     if (mIsDataComplete)
00800     {
00801         H5Dwrite(mVariablesDatasetId, H5T_NATIVE_DOUBLE, memspace, file_dataspace, property_list_id, p_petsc_vector);
00802     }
00803     else
00804     {
00805         if (mUseMatrixForIncompleteData)
00806         {
00807             // Make a vector of the required size
00808             output_petsc_vector = PetscTools::CreateVec(mFileFixedDimensionSize, mNumberOwned);
00809 
00810             // Fill the vector by multiplying complete data by incomplete output matrix
00811             MatMult(mSingleIncompleteOutputMatrix, petscVector, output_petsc_vector);
00812 
00813             double* p_petsc_vector_incomplete;
00814             VecGetArray(output_petsc_vector, &p_petsc_vector_incomplete);
00815             H5Dwrite(mVariablesDatasetId, H5T_NATIVE_DOUBLE, memspace, file_dataspace, property_list_id, p_petsc_vector_incomplete);
00816         }
00817         else
00818         {
00819             // Make a local copy of the data you own
00820             boost::scoped_array<double> local_data(new double[mNumberOwned]);
00821             for (unsigned i=0; i<mNumberOwned; i++)
00822             {
00823                 local_data[i] = p_petsc_vector[ mIncompleteNodeIndices[mOffset+i]-mLo ];
00824 
00825             }
00826             H5Dwrite(mVariablesDatasetId, H5T_NATIVE_DOUBLE, memspace, file_dataspace, property_list_id, local_data.get());
00827         }
00828     }
00829 
00830     VecRestoreArray(output_petsc_vector, &p_petsc_vector);
00831 
00832     H5Pclose(property_list_id);
00833     H5Sclose(file_dataspace);
00834     if (mNumberOwned !=0)
00835     {
00836         H5Sclose(memspace);
00837     }
00838 
00839     if (petscVector != output_petsc_vector)
00840     {
00841         // Free local vector
00842         PetscTools::Destroy(output_petsc_vector);
00843     }
00844 }
00845 
00846 void Hdf5DataWriter::PutStripedVector(std::vector<int> variableIDs, Vec petscVector)
00847 {
00848     if (mIsInDefineMode)
00849     {
00850         EXCEPTION("Cannot write data while in define mode.");
00851     }
00852 
00853     if (variableIDs.size() <= 1)
00854     {
00855         EXCEPTION("The PutStripedVector method requires at least two variables ID. If only one is needed, use PutVector method instead");
00856     }
00857 
00858     const unsigned NUM_STRIPES=variableIDs.size();
00859 
00860     int firstVariableID=variableIDs[0];
00861 
00862     // Currently the method only works with consecutive columns, can be extended if needed
00863     for (unsigned i = 1; i < variableIDs.size(); i++)
00864     {
00865         if (variableIDs[i]-variableIDs[i-1] != 1)
00866         {
00867             EXCEPTION("Columns should be consecutive. Try reordering them.");
00868         }
00869     }
00870 
00871     int vector_size;
00872     VecGetSize(petscVector, &vector_size);
00873 
00874     if ((unsigned) vector_size != NUM_STRIPES*mDataFixedDimensionSize)
00875     {
00876         EXCEPTION("Vector size doesn't match fixed dimension");
00877     }
00878 
00879     // Make sure that everything is actually extended to the correct dimension
00880     PossiblyExtend();
00881 
00882     Vec output_petsc_vector;
00883 
00884     // Decide what to write
00885     if (mDoublePermutation == NULL)
00886     {
00887         // No permutation - just write
00888         output_petsc_vector = petscVector;
00889     }
00890     else
00891     {
00892         assert(mIsDataComplete);
00893         // Make a vector with the same pattern (doesn't copy the data)
00894         VecDuplicate(petscVector, &output_petsc_vector);
00895 
00896         // Apply the permutation matrix
00897         MatMult(mDoublePermutation, petscVector, output_petsc_vector);
00898     }
00899     // Define a dataset in memory for this process
00900     hid_t memspace=0;
00901     if (mNumberOwned !=0)
00902     {
00903         hsize_t v_size[1] = {mNumberOwned*NUM_STRIPES};
00904         memspace = H5Screate_simple(1, v_size, NULL);
00905     }
00906 
00907     // Select hyperslab in the file
00908     hsize_t start[DATASET_DIMS] = {mCurrentTimeStep, mOffset, (unsigned)(firstVariableID)};
00909     hsize_t stride[DATASET_DIMS] = {1, 1, 1};//we are imposing contiguous variables, hence the stride is 1 (3rd component)
00910     hsize_t block_size[DATASET_DIMS] = {1, mNumberOwned, 1};
00911     hsize_t number_blocks[DATASET_DIMS] = {1, 1, NUM_STRIPES};
00912 
00913     hid_t hyperslab_space = H5Dget_space(mVariablesDatasetId);
00914     H5Sselect_hyperslab(hyperslab_space, H5S_SELECT_SET, start, stride, number_blocks, block_size);
00915 
00916     // Create property list for collective dataset write, and write! Finally.
00917     hid_t property_list_id = H5Pcreate(H5P_DATASET_XFER);
00918     H5Pset_dxpl_mpio(property_list_id, H5FD_MPIO_COLLECTIVE);
00919 
00920     double* p_petsc_vector;
00921     VecGetArray(output_petsc_vector, &p_petsc_vector);
00922 
00923     if (mIsDataComplete)
00924     {
00925         H5Dwrite(mVariablesDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, property_list_id, p_petsc_vector);
00926     }
00927     else
00928     {
00929         if (variableIDs.size() < 3) // incomplete data and striped vector is supported only for NUM_STRIPES=2...for the moment
00930         {
00931             if (mUseMatrixForIncompleteData)
00932             {
00933                 // Make a vector of the required size
00934                 output_petsc_vector = PetscTools::CreateVec(2*mFileFixedDimensionSize, 2*mNumberOwned);
00935 
00936                 // Fill the vector by multiplying complete data by incomplete output matrix
00937                 MatMult(mDoubleIncompleteOutputMatrix, petscVector, output_petsc_vector);
00938 
00939                 double* p_petsc_vector_incomplete;
00940                 VecGetArray(output_petsc_vector, &p_petsc_vector_incomplete);
00941 
00942                 H5Dwrite(mVariablesDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, property_list_id, p_petsc_vector_incomplete);
00943             }
00944             else
00945             {
00946                 // Make a local copy of the data you own
00947                 boost::scoped_array<double> local_data(new double[mNumberOwned*NUM_STRIPES]);
00948                 for (unsigned i=0; i<mNumberOwned; i++)
00949                 {
00950                     unsigned local_node_number = mIncompleteNodeIndices[mOffset+i] - mLo;
00951                     local_data[NUM_STRIPES*i]   = p_petsc_vector[ local_node_number*NUM_STRIPES ];
00952                     local_data[NUM_STRIPES*i+1] = p_petsc_vector[ local_node_number*NUM_STRIPES + 1];
00953                 }
00954 
00955                 H5Dwrite(mVariablesDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, property_list_id, local_data.get());
00956             }
00957         }
00958         else
00959         {
00960             EXCEPTION("The PutStripedVector functionality for incomplete data is supported for only 2 stripes");
00961         }
00962     }
00963 
00964     VecRestoreArray(output_petsc_vector, &p_petsc_vector);
00965 
00966     H5Sclose(hyperslab_space);
00967     if (mNumberOwned != 0)
00968     {
00969         H5Sclose(memspace);
00970     }
00971     H5Pclose(property_list_id);
00972 
00973     if (petscVector != output_petsc_vector)
00974     {
00975         // Free local vector
00976         PetscTools::Destroy(output_petsc_vector);
00977     }
00978 }
00979 
00980 void Hdf5DataWriter::PutUnlimitedVariable(double value)
00981 {
00982     if (mIsInDefineMode)
00983     {
00984         EXCEPTION("Cannot write data while in define mode.");
00985     }
00986 
00987     if (!mIsUnlimitedDimensionSet)
00988     {
00989         EXCEPTION("PutUnlimitedVariable() called but no unlimited dimension has been set");
00990     }
00991 
00992     // Make sure that everything is actually extended to the correct dimension.
00993     PossiblyExtend();
00994 
00995     // This data is only written by the master
00996     if (!PetscTools::AmMaster())
00997     {
00998         return;
00999     }
01000 
01001     hsize_t size[1] = {1};
01002     hid_t memspace = H5Screate_simple(1, size, NULL);
01003 
01004     // Select hyperslab in the file.
01005     hsize_t count[1] = {1};
01006     hsize_t offset[1] = {mCurrentTimeStep};
01007     hid_t hyperslab_space = H5Dget_space(mUnlimitedDatasetId);
01008     H5Sselect_hyperslab(hyperslab_space, H5S_SELECT_SET, offset, NULL, count, NULL);
01009 
01010     H5Dwrite(mUnlimitedDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, H5P_DEFAULT, &value);
01011 
01012     H5Sclose(hyperslab_space);
01013     H5Sclose(memspace);
01014 }
01015 
01016 void Hdf5DataWriter::Close()
01017 {
01018     if (mIsInDefineMode)
01019     {
01020         return; // Nothing to do...
01021     }
01022 
01023     H5Dclose(mVariablesDatasetId);
01024     if (mIsUnlimitedDimensionSet)
01025     {
01026         H5Dclose(mUnlimitedDatasetId);
01027     }
01028     H5Fclose(mFileId);
01029 
01030     // Cope with being called twice (e.g. if a user calls Close then the destructor)
01031     mIsInDefineMode = true;
01032 }
01033 
01034 void Hdf5DataWriter::DefineUnlimitedDimension(const std::string& rVariableName,
01035                                               const std::string& rVariableUnits,
01036                                               unsigned estimatedLength)
01037 {
01038     if (mIsUnlimitedDimensionSet)
01039     {
01040         EXCEPTION("Unlimited dimension already set. Cannot be defined twice");
01041     }
01042 
01043     if (!mIsInDefineMode)
01044     {
01045         EXCEPTION("Cannot define variables when not in Define mode");
01046     }
01047 
01048     this->mIsUnlimitedDimensionSet = true;
01049     this->mUnlimitedDimensionName = rVariableName;
01050     this->mUnlimitedDimensionUnit = rVariableUnits;
01051     mEstimatedUnlimitedLength = estimatedLength;
01052 }
01053 
01054 void Hdf5DataWriter::AdvanceAlongUnlimitedDimension()
01055 {
01056     if (!mIsUnlimitedDimensionSet)
01057     {
01058         EXCEPTION("Trying to advance along an unlimited dimension without having defined any");
01059     }
01060 
01061     mCurrentTimeStep++;
01062 
01063     /*
01064      * Extend the dataset (only reached when adding to an existing dataset,
01065      * or if mEstimatedUnlimitedLength hasn't been set and has defaulted to 1).
01066      */
01067     if ( mCurrentTimeStep >= (long unsigned) mEstimatedUnlimitedLength )
01068     {
01069         mDatasetDims[0]++;
01070         mNeedExtend = true;
01071     }
01072 }
01073 
01074 void Hdf5DataWriter::PossiblyExtend()
01075 {
01076     if (mNeedExtend)
01077     {
01078 #if H5_VERS_MAJOR>=1 && H5_VERS_MINOR>=8 // HDF5 1.8+
01079         H5Dset_extent( mVariablesDatasetId, mDatasetDims );
01080         H5Dset_extent( mUnlimitedDatasetId, &mDatasetDims[0] );
01081 #else // Deprecated
01082         H5Dextend( mVariablesDatasetId, mDatasetDims );
01083         H5Dextend( mUnlimitedDatasetId, mDatasetDims );
01084 #endif
01085     }
01086     mNeedExtend = false;
01087 }
01088 
01089 int Hdf5DataWriter::GetVariableByName(const std::string& rVariableName)
01090 {
01091     int id = -1;
01092 
01093     // Check for the variable name in the existing variables
01094     for (unsigned index=0; index<mVariables.size(); index++)
01095     {
01096         if (mVariables[index].mVariableName == rVariableName)
01097         {
01098             id = index;
01099             break;
01100         }
01101     }
01102     if (id == -1)
01103     {
01104         EXCEPTION("Variable does not exist in hdf5 definitions.");
01105     }
01106     return id;
01107 }
01108 
01109 bool Hdf5DataWriter::ApplyPermutation(const std::vector<unsigned>& rPermutation, bool unsafeExtendingMode)
01110 {
01111     if (unsafeExtendingMode == false && !mIsInDefineMode)
01112     {
01113         EXCEPTION("Cannot define permutation when not in Define mode");
01114     }
01115 
01116     if (rPermutation.empty())
01117     {
01118         return false;
01119     }
01120 
01121     if (rPermutation.size() != mFileFixedDimensionSize ||
01122         rPermutation.size() != mDataFixedDimensionSize)
01123     {
01124         EXCEPTION("Permutation doesn't match the expected problem size");
01125     }
01126 
01127     // Permutation checker
01128     std::set<unsigned> permutation_pigeon_hole;
01129 
01130     // Fill up the pigeon holes
01131     bool identity_map = true;
01132     for (unsigned i=0; i<mDataFixedDimensionSize; i++)
01133     {
01134         permutation_pigeon_hole.insert(rPermutation[i]);
01135         if (identity_map && i != rPermutation[i])
01136         {
01137             identity_map = false;
01138         }
01139     }
01140     if (identity_map)
01141     {
01142         // Do nothing
01143         return false;
01144     }
01145 
01146     /*
01147      * The pigeon-hole principle states that each index appears exactly once
01148      * so if any don't appear then either one appears twice or something out of
01149      * scope has appeared.
01150      */
01151     for (unsigned i=0; i<mDataFixedDimensionSize; i++)
01152     {
01153         if (permutation_pigeon_hole.count(i) != 1u)
01154         {
01155             EXCEPTION("Permutation vector doesn't contain a valid permutation");
01156         }
01157     }
01158     // Make sure we've not done it already
01159     assert(mSinglePermutation == NULL);
01160     assert(mDoublePermutation == NULL);
01161     PetscTools::SetupMat(mSinglePermutation,   mDataFixedDimensionSize,   mDataFixedDimensionSize, 2, mHi - mLo, mHi - mLo);
01162     PetscTools::SetupMat(mDoublePermutation, 2*mDataFixedDimensionSize, 2*mDataFixedDimensionSize, 4, 2*(mHi - mLo), 2*(mHi - mLo));
01163 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
01164     MatSetOption(mSinglePermutation, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE);
01165     MatSetOption(mDoublePermutation, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE);
01166 #else
01167     MatSetOption(mSinglePermutation, MAT_IGNORE_OFF_PROC_ENTRIES);
01168     MatSetOption(mDoublePermutation, MAT_IGNORE_OFF_PROC_ENTRIES);
01169 #endif
01170     // Only do local rows
01171     for (unsigned row_index=mLo; row_index<mHi; row_index++)
01172     {
01173         // Put zero on the diagonal
01174         MatSetValue(mSinglePermutation, row_index, row_index, 0.0, INSERT_VALUES);
01175 
01176         // Put one at (i,j)
01177         MatSetValue(mSinglePermutation, row_index, rPermutation[row_index], 1.0, INSERT_VALUES);
01178 
01179         unsigned bi_index = 2*row_index;
01180         unsigned perm_index = 2*rPermutation[row_index];
01181 
01182         // Put zeroes on the diagonal
01183         MatSetValue(mDoublePermutation, bi_index, bi_index, 0.0, INSERT_VALUES);
01184         MatSetValue(mDoublePermutation, bi_index+1, bi_index+1, 0.0, INSERT_VALUES);
01185 
01186         // Put ones at (i,j)
01187         MatSetValue(mDoublePermutation, bi_index, perm_index, 1.0, INSERT_VALUES);
01188         MatSetValue(mDoublePermutation, bi_index+1, perm_index+1, 1.0, INSERT_VALUES);
01189     }
01190     MatAssemblyBegin(mSinglePermutation, MAT_FINAL_ASSEMBLY);
01191     MatAssemblyBegin(mDoublePermutation, MAT_FINAL_ASSEMBLY);
01192     MatAssemblyEnd(mSinglePermutation, MAT_FINAL_ASSEMBLY);
01193     MatAssemblyEnd(mDoublePermutation, MAT_FINAL_ASSEMBLY);
01194     return true;
01195 }
01196 
01197 void Hdf5DataWriter::SetFixedChunkSize(const unsigned& rTimestepsPerChunk,
01198                                        const unsigned& rNodesPerChunk,
01199                                        const unsigned& rVariablesPerChunk)
01200 {
01201     assert(mIsInDefineMode);
01202 
01203     mUseOptimalChunkSizeAlgorithm = false;
01204     mFixedChunkSize[0] = rTimestepsPerChunk;
01205     mFixedChunkSize[1] = rNodesPerChunk;
01206     mFixedChunkSize[2] = rVariablesPerChunk;
01207 }
01208 
01209 hsize_t Hdf5DataWriter::CalculateNumberOfChunks()
01210 {
01211     // Number of chunks for istore_k optimisation
01212     hsize_t num_chunks = 1;
01213     for (unsigned i=0; i<DATASET_DIMS; ++i)
01214     {
01215         num_chunks *= CeilDivide(mDatasetDims[i], mChunkSize[i]);
01216     }
01217     return num_chunks;
01218 }
01219 
01220 void Hdf5DataWriter::CalculateChunkDims( unsigned targetSize, unsigned* pChunkSizeInBytes, bool* pAllOneChunk )
01221 {
01222     bool all_one_chunk = true;
01223     unsigned chunk_size_in_bytes = 8u; // 8 bytes/double
01224     unsigned divisors[DATASET_DIMS];
01225     // Loop over dataset dimensions, dividing each dimension into the integer number of chunks that results
01226     // in the number of entries closest to the targetSize. This means the chunks will span the dataset with
01227     // as little waste as possible.
01228     for (unsigned i=0; i<DATASET_DIMS; ++i)
01229     {
01230         // What do I divide the dataset by to get targetSize entries per chunk?
01231         divisors[i] = CeilDivide(mDatasetDims[i], targetSize);
01232         // If I divide my dataset into divisors pieces, how big is each chunk?
01233         mChunkSize[i] = CeilDivide(mDatasetDims[i], divisors[i]);
01234         // If my chunks are this big, how many bytes is that?
01235         chunk_size_in_bytes *= mChunkSize[i];
01236         // Check if all divisors==1, which means we have one big chunk.
01237         all_one_chunk = all_one_chunk && divisors[i]==1u;
01238     }
01239     // Update pointers
01240     *pChunkSizeInBytes = chunk_size_in_bytes;
01241     *pAllOneChunk = all_one_chunk;
01242 }
01243 
01244 void Hdf5DataWriter::SetChunkSize()
01245 {
01246     /*
01247      * The size in each dimension is increased in step until the size of
01248      * the chunk exceeds a limit, or we end up with one big chunk.
01249      *
01250      * Also make sure we don't have too many chunks. Over 75 K makes the
01251      * H5Pset_istore_k optimisation above very detrimental to performance
01252      * according to "Notes from 31 July 2013" at:
01253      * http://confluence.diamond.ac.uk/display/Europroj/Ulrik+Pederson+-+Excalibur+Notes
01254      */
01255     const unsigned recommended_max_number_chunks = 75000;
01256     if (mUseOptimalChunkSizeAlgorithm)
01257     {
01258         /*
01259          * The line below sets the chunk size to (just under) 128 K chunks, which
01260          * seems to be a good compromise.
01261          *
01262          * For large problems performance usually improves with increased chunk size.
01263          * A sweet spot seems to be 1 M chunks.
01264          *
01265          * On a striped filesystem, for best performance, try to match the chunk size
01266          * and alignment (see "H5Pset_alignment" above) with the file stripe size.
01267          */
01268         unsigned target_size_in_bytes = 1024*1024/8; // 128 K
01269 
01270         unsigned target_size = 0;
01271         unsigned chunk_size_in_bytes;
01272         bool all_one_chunk;
01273 
01274         // While we have too many chunks, make target_size_in_bytes larger
01275         do
01276         {
01277             // While the chunks are too small, make mChunkSize[i]s larger, unless
01278             // we end up with a chunk that spans the dataset.
01279             do
01280             {
01281                 target_size++;
01282                 CalculateChunkDims(target_size, &chunk_size_in_bytes, &all_one_chunk);
01283             }
01284             while ( chunk_size_in_bytes < target_size_in_bytes && !all_one_chunk);
01285 
01286             // Go one step back if the target size has been exceeded
01287             if ( chunk_size_in_bytes > target_size_in_bytes && !all_one_chunk )
01288             {
01289                 target_size--;
01290                 CalculateChunkDims(target_size, &chunk_size_in_bytes, &all_one_chunk);
01291             }
01292 
01293             mNumberOfChunks = CalculateNumberOfChunks();
01294 
01295             /*
01296             if ( PetscTools::AmMaster() )
01297             {
01298                 std::cout << "Hdf5DataWriter dataset contains " << mNumberOfChunks << " chunks of " << chunk_size_in_bytes << " B." << std::endl;
01299             }
01300             */
01301 
01302             target_size_in_bytes *= 2; // Increase target size for next iteration
01303         }
01304         while ( mNumberOfChunks > recommended_max_number_chunks );
01305     }
01306     /*
01307      * ... unless the user has set chunk dimensions explicitly, in which case
01308      * use those. The program will exit if the size results in too many chunks.
01309      */
01310     else
01311     {
01312         for (unsigned i=0; i<DATASET_DIMS; ++i)
01313         {
01314             mChunkSize[i] = mFixedChunkSize[i];
01315         }
01316 
01317         mNumberOfChunks = CalculateNumberOfChunks();
01318 
01319         if ( mNumberOfChunks > recommended_max_number_chunks)
01320         {
01321             /*
01322              * The user-defined HDF5 chunk size has resulted in over 75,000 chunks,
01323              * which is known to be extremely detrimental to performance. Try
01324              * increasing the chunk dimensions or (better) not using fixed chunk sizes.
01325              */
01326             NEVER_REACHED;
01327         }
01328     }
01329 }

Generated by  doxygen 1.6.2