Hdf5DataWriter.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2011
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00026 
00027 */
00028 
00029 /*
00030  * Implementation file for Hdf5DataWriter class.
00031  *
00032  */
00033 #include <set>
00034 #include <cstring> //For strcmp etc. Needed in gcc-4.4
00035 
00036 #include "Hdf5DataWriter.hpp"
00037 
00038 #include "Exception.hpp"
00039 #include "OutputFileHandler.hpp"
00040 #include "PetscTools.hpp"
00041 #include "Version.hpp"
00042 
00043 Hdf5DataWriter::Hdf5DataWriter(DistributedVectorFactory& rVectorFactory,
00044                                const std::string& rDirectory,
00045                                const std::string& rBaseName,
00046                                bool cleanDirectory,
00047                                bool extendData)
00048     : mrVectorFactory(rVectorFactory),
00049       mDirectory(rDirectory),
00050       mBaseName(rBaseName),
00051       mCleanDirectory(cleanDirectory),
00052       mIsInDefineMode(true),
00053       mIsFixedDimensionSet(false),
00054       mIsUnlimitedDimensionSet(false),
00055       mEstimatedUnlimitedLength(1u),
00056       mFileFixedDimensionSize(0u),
00057       mDataFixedDimensionSize(0u),
00058       mLo(mrVectorFactory.GetLow()),
00059       mHi(mrVectorFactory.GetHigh()),
00060       mNumberOwned(0u),
00061       mOffset(0u),
00062       mIsDataComplete(true),
00063       mNeedExtend(false),
00064       mUseMatrixForIncompleteData(false),
00065       mCurrentTimeStep(0),
00066       mSinglePermutation(NULL),
00067       mDoublePermutation(NULL),
00068       mSingleIncompleteOutputMatrix(NULL),
00069       mDoubleIncompleteOutputMatrix(NULL),
00070       mUseOptimalChunkSizeAlgorithm(true),
00071       mFixedChunkSize(0)
00072 {
00073     if (extendData && cleanDirectory)
00074     {
00075         EXCEPTION("You are asking to delete a file and then extend it, change arguments to constructor.");
00076     }
00077 
00078     if (extendData)
00079     {
00080         // Where to find the file
00081         OutputFileHandler output_file_handler(mDirectory, false);
00082         std::string results_dir = output_file_handler.GetOutputDirectoryFullPath();
00083         std::string file_name = results_dir + mBaseName + ".h5";
00084 
00085         // Set up a property list saying how we'll open the file
00086         hid_t property_list_id = H5Pcreate(H5P_FILE_ACCESS);
00087         H5Pset_fapl_mpio(property_list_id, PETSC_COMM_WORLD, MPI_INFO_NULL);
00088 
00089         // Open the file and free the property list
00090         mFileId = H5Fopen(file_name.c_str(), H5F_ACC_RDWR, property_list_id);
00091         H5Pclose(property_list_id);
00092 
00093         if (mFileId < 0)
00094         {
00095             mDatasetId = 0;
00096             EXCEPTION("Hdf5DataWriter could not open " + file_name);
00097         }
00098 
00099         // Open the main dataset, and figure out its size/shape
00100         mDatasetId = H5Dopen(mFileId, "Data");
00101         hid_t variables_dataspace = H5Dget_space(mDatasetId);
00102         //unsigned variables_dataset_rank = H5Sget_simple_extent_ndims(variables_dataspace);
00103         hsize_t dataset_max_sizes[DATASET_DIMS];
00104         H5Sget_simple_extent_dims(variables_dataspace, mDatasetDims, dataset_max_sizes);
00105         H5Sclose(variables_dataspace);
00106 
00107         // Check that an unlimited dimension is defined
00108         if (dataset_max_sizes[0] != H5S_UNLIMITED)
00109         {
00110             H5Dclose(mDatasetId);
00111             H5Fclose(mFileId);
00112             EXCEPTION("Tried to open a datafile for extending which doesn't have an unlimited dimension.");
00113         }
00114         mIsUnlimitedDimensionSet = true;
00115 
00116         // Sanity check other dimension sizes
00117         for (unsigned i=1; i<DATASET_DIMS; i++)  // Zero is excluded since it is unlimited
00118         {
00119             assert(mDatasetDims[i] == dataset_max_sizes[i]);
00120         }
00121         mFileFixedDimensionSize = mDatasetDims[1];
00122         mIsFixedDimensionSet = true;
00123         mVariables.reserve(mDatasetDims[2]);
00124 
00125         // Figure out what the variables are
00126         hid_t attribute_id = H5Aopen_name(mDatasetId, "Variable Details");
00127         hid_t attribute_type  = H5Aget_type(attribute_id);
00128         hid_t attribute_space = H5Aget_space(attribute_id);
00129         hsize_t attr_dataspace_dim;
00130         H5Sget_simple_extent_dims(attribute_space, &attr_dataspace_dim, NULL);
00131         unsigned num_columns = H5Sget_simple_extent_npoints(attribute_space);
00132         assert(num_columns == mDatasetDims[2]); // I think...
00133 
00134         char* string_array = (char *)malloc(sizeof(char)*MAX_STRING_SIZE*(int)num_columns);
00135         H5Aread(attribute_id, attribute_type, string_array);
00136 
00137         // Loop over columns/variables
00138         for (unsigned index=0; index<num_columns; index++)
00139         {
00140             // Read the string from the raw vector
00141             std::string column_name_unit(&string_array[MAX_STRING_SIZE*index]);
00142 
00143             // Find location of unit name
00144             size_t name_length = column_name_unit.find('(');
00145             size_t unit_length = column_name_unit.find(')') - name_length - 1;
00146 
00147             // Create the variable
00148             DataWriterVariable var;
00149             var.mVariableName = column_name_unit.substr(0, name_length);
00150             var.mVariableUnits = column_name_unit.substr(name_length+1, unit_length);
00151             mVariables.push_back(var);
00152         }
00153 
00154         // Free memory, release ids
00155         free(string_array);
00156         H5Tclose(attribute_type);
00157         H5Sclose(attribute_space);
00158         H5Aclose(attribute_id);
00159 
00160         // Now deal with time
00161         mUnlimitedDimensionName = "Time"; // Assumed by the reader...
00162         mTimeDatasetId = H5Dopen(mFileId, mUnlimitedDimensionName.c_str());
00163         mUnlimitedDimensionUnit = "ms"; // Assumed by Chaste...
00164 
00165         // How many time steps have been written so far?
00166         hid_t timestep_dataspace = H5Dget_space(mTimeDatasetId);
00167         hsize_t num_timesteps;
00168         H5Sget_simple_extent_dims(timestep_dataspace, &num_timesteps, NULL);
00169         H5Sclose(timestep_dataspace);
00170         mCurrentTimeStep = (long)num_timesteps - 1;
00171 
00172         // Incomplete data?
00173         attribute_id = H5Aopen_name(mDatasetId, "IsDataComplete");
00174         if (attribute_id < 0)
00175         {
00176 #define COVERAGE_IGNORE
00177             // Old format, before we added the attribute.
00178             EXCEPTION("Extending old-format files isn't supported.");
00179 #undef COVERAGE_IGNORE
00180         }
00181         else
00182         {
00183             attribute_type = H5Aget_type(attribute_id);
00184             attribute_space = H5Aget_space(attribute_id);
00185             unsigned is_data_complete;
00186             H5Aread(attribute_id, H5T_NATIVE_UINT, &is_data_complete);
00187             mIsDataComplete = (is_data_complete == 1) ? true : false;
00188             H5Tclose(attribute_type);
00189             H5Sclose(attribute_space);
00190             H5Aclose(attribute_id);
00191         }
00192         if (mIsDataComplete)
00193         {
00194             mNumberOwned = mrVectorFactory.GetLocalOwnership();
00195             mOffset = mLo;
00196             mDataFixedDimensionSize = mFileFixedDimensionSize;
00197         }
00198         else
00199         {
00200             // Read which nodes appear in the file (mIncompleteNodeIndices)
00201             attribute_id = H5Aopen_name(mDatasetId, "NodeMap");
00202             attribute_type  = H5Aget_type(attribute_id);
00203             attribute_space = H5Aget_space(attribute_id);
00204 
00205             // Get the dataset/dataspace dimensions
00206             unsigned num_node_indices = H5Sget_simple_extent_npoints(attribute_space);
00207 
00208             // Read data from hyperslab in the file into the hyperslab in memory
00209             mIncompleteNodeIndices.clear();
00210             mIncompleteNodeIndices.resize(num_node_indices);
00211             H5Aread(attribute_id, H5T_NATIVE_UINT, &mIncompleteNodeIndices[0]);
00212 
00213             // Release ids
00214             H5Tclose(attribute_type);
00215             H5Sclose(attribute_space);
00216             H5Aclose(attribute_id);
00217 
00218             // Set up what data we can
00219             mNumberOwned = mrVectorFactory.GetLocalOwnership();
00220             ComputeIncompleteOffset();
00224             mDataFixedDimensionSize = UINT_MAX;
00225             H5Dclose(mDatasetId);
00226             H5Dclose(mTimeDatasetId);
00227             H5Fclose(mFileId);
00228             EXCEPTION("Unable to extend an incomplete data file at present.");
00229         }
00230 
00231         // Done
00232         mIsInDefineMode = false;
00233         AdvanceAlongUnlimitedDimension();
00234     }
00235 }
00236 
00237 Hdf5DataWriter::~Hdf5DataWriter()
00238 {
00239     Close();
00240 
00241     if (mSinglePermutation)
00242     {
00243         MatDestroy(mSinglePermutation);
00244     }
00245     if (mDoublePermutation)
00246     {
00247         MatDestroy(mDoublePermutation);
00248     }
00249     if (mSingleIncompleteOutputMatrix)
00250     {
00251         MatDestroy(mSingleIncompleteOutputMatrix);
00252     }
00253     if (mDoubleIncompleteOutputMatrix)
00254     {
00255         MatDestroy(mDoubleIncompleteOutputMatrix);
00256     }
00257 }
00258 
00259 void Hdf5DataWriter::DefineFixedDimension(long dimensionSize)
00260 {
00261     if (!mIsInDefineMode)
00262     {
00263         EXCEPTION("Cannot define variables when not in Define mode");
00264     }
00265     if (dimensionSize < 1)
00266     {
00267         EXCEPTION("Fixed dimension must be at least 1 long");
00268     }
00269     if (mIsFixedDimensionSet)
00270     {
00271         EXCEPTION("Fixed dimension already set");
00272     }
00273 
00274     // Work out the ownership details
00275     mLo = mrVectorFactory.GetLow();
00276     mHi = mrVectorFactory.GetHigh();
00277     mNumberOwned = mrVectorFactory.GetLocalOwnership();
00278     mOffset = mLo;
00279     mFileFixedDimensionSize = dimensionSize;
00280     mDataFixedDimensionSize = dimensionSize;
00281     mIsFixedDimensionSet = true;
00282 }
00283 
00284 void Hdf5DataWriter::DefineFixedDimension(const std::vector<unsigned>& rNodesToOuput, long vecSize)
00285 {
00286     unsigned vector_size = rNodesToOuput.size();
00287 
00288     for (unsigned index=0; index < vector_size-1; index++)
00289     {
00290         if (rNodesToOuput[index] >= rNodesToOuput[index+1])
00291         {
00292             EXCEPTION("Input should be monotonic increasing");
00293         }
00294     }
00295 
00296     if ((int) rNodesToOuput.back() >= vecSize)
00297     {
00298         EXCEPTION("Vector size doesn't match nodes to output");
00299     }
00300 
00301     DefineFixedDimension(vecSize);
00302 
00303     mFileFixedDimensionSize = vector_size;
00304     mIsDataComplete = false;
00305     mIncompleteNodeIndices = rNodesToOuput;
00306     ComputeIncompleteOffset();
00307 }
00308 
00309 void Hdf5DataWriter::DefineFixedDimensionUsingMatrix(const std::vector<unsigned>& rNodesToOuput, long vecSize)
00310 {
00311     unsigned vector_size = rNodesToOuput.size();
00312 
00313     for (unsigned index=0; index < vector_size-1; index++)
00314     {
00315         if (rNodesToOuput[index] >= rNodesToOuput[index+1])
00316         {
00317             EXCEPTION("Input should be monotonic increasing");
00318         }
00319     }
00320 
00321     if ((int) rNodesToOuput.back() >= vecSize)
00322     {
00323         EXCEPTION("Vector size doesn't match nodes to output");
00324     }
00325 
00326     DefineFixedDimension(vecSize);
00327 
00328     mFileFixedDimensionSize = vector_size;
00329     mIsDataComplete = false;
00330     mIncompleteNodeIndices = rNodesToOuput;
00331     mUseMatrixForIncompleteData = true;
00332     ComputeIncompleteOffset();
00333 
00334     // Make sure we've not done it already
00335     assert(mSingleIncompleteOutputMatrix == NULL);
00336     assert(mDoubleIncompleteOutputMatrix == NULL);
00337     PetscTools::SetupMat(mSingleIncompleteOutputMatrix,   mFileFixedDimensionSize,   mDataFixedDimensionSize, 2,  mNumberOwned,  mHi - mLo);
00338     PetscTools::SetupMat(mDoubleIncompleteOutputMatrix, 2*mFileFixedDimensionSize, 2*mDataFixedDimensionSize, 4,  2*mNumberOwned, 2*(mHi - mLo));
00339 
00340     // Only do local rows
00341     for (unsigned row_index = mOffset; row_index < mOffset + mNumberOwned; row_index++)
00342     {
00343         // Put zero on the diagonal
00344         MatSetValue(mSingleIncompleteOutputMatrix, row_index, row_index, 0.0, INSERT_VALUES);
00345 
00346         // Put one at (i,j)
00347         MatSetValue(mSingleIncompleteOutputMatrix, row_index, rNodesToOuput[row_index], 1.0, INSERT_VALUES);
00348 
00349         unsigned bi_index = 2*row_index;
00350         unsigned perm_index = 2*rNodesToOuput[row_index];
00351 
00352         // Put zeroes on the diagonal
00353         MatSetValue(mDoubleIncompleteOutputMatrix, bi_index, bi_index, 0.0, INSERT_VALUES);
00354         MatSetValue(mDoubleIncompleteOutputMatrix, bi_index+1, bi_index+1, 0.0, INSERT_VALUES);
00355 
00356         // Put ones at (i,j)
00357         MatSetValue(mDoubleIncompleteOutputMatrix, bi_index, perm_index, 1.0, INSERT_VALUES);
00358         MatSetValue(mDoubleIncompleteOutputMatrix, bi_index+1, perm_index+1, 1.0, INSERT_VALUES);
00359     }
00360     MatAssemblyBegin(mSingleIncompleteOutputMatrix, MAT_FINAL_ASSEMBLY);
00361     MatAssemblyBegin(mDoubleIncompleteOutputMatrix, MAT_FINAL_ASSEMBLY);
00362     MatAssemblyEnd(mSingleIncompleteOutputMatrix, MAT_FINAL_ASSEMBLY);
00363     MatAssemblyEnd(mDoubleIncompleteOutputMatrix, MAT_FINAL_ASSEMBLY);
00364 
00365 //    MatView(mSingleIncompleteOutputMatrix, PETSC_VIEWER_STDOUT_WORLD);
00366 }
00367 
00368 void Hdf5DataWriter::ComputeIncompleteOffset()
00369 {
00370     mOffset = 0;
00371     mNumberOwned = 0;
00372     for (unsigned i=0; i<mIncompleteNodeIndices.size(); i++)
00373     {
00374         if (mIncompleteNodeIndices[i] < mLo)
00375         {
00376             mOffset++;
00377         }
00378         else if (mIncompleteNodeIndices[i] < mHi)
00379         {
00380             mNumberOwned++;
00381         }
00382     }
00383 }
00384 
00385 int Hdf5DataWriter::DefineVariable(const std::string& rVariableName,
00386                                    const std::string& rVariableUnits)
00387 {
00388     if (!mIsInDefineMode)
00389     {
00390         EXCEPTION("Cannot define variables when not in Define mode");
00391     }
00392 
00393     CheckVariableName(rVariableName);
00394     CheckUnitsName(rVariableUnits);
00395 
00396     // Check for the variable being already defined
00397     for (unsigned index=0; index<mVariables.size(); index++)
00398     {
00399         if (mVariables[index].mVariableName == rVariableName)
00400         {
00401             EXCEPTION("Variable name already exists");
00402         }
00403     }
00404 
00405     DataWriterVariable new_variable;
00406     new_variable.mVariableName = rVariableName;
00407     new_variable.mVariableUnits = rVariableUnits;
00408     int variable_id;
00409 
00410     // Add the variable to the variable vector
00411     mVariables.push_back(new_variable);
00412 
00413     // Use the index of the variable vector as the variable ID.
00414     // This is ok since there is no way to remove variables.
00415     variable_id = mVariables.size() - 1;
00416 
00417     return variable_id;
00418 }
00419 
00420 void Hdf5DataWriter::CheckVariableName(const std::string& rName)
00421 {
00422     if (rName.length() == 0)
00423     {
00424         EXCEPTION("Variable name not allowed: may not be blank.");
00425     }
00426     CheckUnitsName(rName);
00427 }
00428 
00429 void Hdf5DataWriter::CheckUnitsName(const std::string& rName)
00430 {
00431     for (unsigned i=0; i<rName.length(); i++)
00432     {
00433         if (!isalnum(rName[i]) && !(rName[i]=='_'))
00434         {
00435             std::string error = "Variable name/units '" + rName + "' not allowed: may only contain alphanumeric characters or '_'.";
00436             EXCEPTION(error);
00437         }
00438     }
00439 }
00440 
00441 void Hdf5DataWriter::EndDefineMode()
00442 {
00443     // Check that at least one variable has been defined
00444     if (mVariables.size() < 1)
00445     {
00446         EXCEPTION("Cannot end define mode. No variables have been defined.");
00447     }
00448 
00449     // Check that a fixed dimension has been defined
00450     if (mIsFixedDimensionSet == false)
00451     {
00452         EXCEPTION("Cannot end define mode. One fixed dimension should be defined.");
00453     }
00454 
00455     OutputFileHandler output_file_handler(mDirectory, mCleanDirectory);
00456     std::string results_dir = output_file_handler.GetOutputDirectoryFullPath();
00457     std::string file_name = results_dir + mBaseName + ".h5";
00458 
00459     // Set up a property list saying how we'll open the file
00460     hid_t property_list_id = H5Pcreate(H5P_FILE_ACCESS);
00461     H5Pset_fapl_mpio(property_list_id, PETSC_COMM_WORLD, MPI_INFO_NULL);
00462 
00463     // Create a file (collectively) and free the property list
00464     mFileId = H5Fcreate(file_name.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, property_list_id);
00465     H5Pclose(property_list_id);
00466     if (mFileId < 0)
00467     {
00468         EXCEPTION("Hdf5DataWriter could not create " + file_name);
00469     }
00470     mIsInDefineMode = false;
00471 
00472     /*
00473      * Create "Data" dataset
00474      */
00475 
00476     // Create the dataspace for the dataset.
00477     mDatasetDims[0] = 1; // While developing we got a non-documented "only the first dimension can be extendible" error.
00478     mDatasetDims[1] = mFileFixedDimensionSize;
00479     mDatasetDims[2] = mVariables.size();
00480 
00481     hsize_t* max_dims = NULL;
00482     hsize_t dataset_max_dims[DATASET_DIMS]; // dataset max dimensions
00483 
00484     hid_t cparms = H5P_DEFAULT;
00485 
00486     if (mIsUnlimitedDimensionSet)
00487     {
00488         dataset_max_dims[0] = H5S_UNLIMITED;
00489         dataset_max_dims[1] = mDatasetDims[1];
00490         dataset_max_dims[2] = mDatasetDims[2];
00491         max_dims = dataset_max_dims;
00492 
00493         hsize_t chunk_size;
00494         if (mUseOptimalChunkSizeAlgorithm)
00495         {
00496             /*
00497              * Modify dataset creation properties to enable chunking. We don't want
00498              * more than 100 chunks, as performance degrades significantly if there
00499              * are too many, where "too many" appears to be about 1000. HDF5's caching
00500              * won't apply if the chunks are too large, but this seems to have less of
00501              * an impact.
00502              */
00503             chunk_size = mEstimatedUnlimitedLength/100;
00504             if (chunk_size < 100)
00505             {
00506                 chunk_size = 100;
00507             }
00508         }
00509         else
00510         {
00511             chunk_size = mFixedChunkSize;
00512         }
00513         /*
00514          * If the size of a chunk in bytes is bigger than 4GB then there may be problems.
00515          * HDF5 1.6.x does not check for this - but there may be snags further down the line.
00516          * HDF5 1.8.x does more error checking and produces std::cout errors and a file with
00517          * no data in it.
00518          */
00519         if (chunk_size * mDatasetDims[1] * mDatasetDims[2] > (uint64_t)0xffffffff)
00520         {
00521             /*
00522              * Note: this exception can be avoided by altering the lines above
00523              * where chunk_size is set (at a loss of efficiency).
00524              */
00525             mIsInDefineMode = true; // To stop things that would be created below from being deleted on Close()
00526             H5Fclose(mFileId); // This is the one thing which we have made
00527             EXCEPTION("HDF5 may be writing more than 4GB to disk at any time and would fail. It may be possible to tune the Chaste code to get around this");
00528         }
00529 
00530         hsize_t chunk_dims[DATASET_DIMS] = {chunk_size, mDatasetDims[1], mDatasetDims[2]};
00531         cparms = H5Pcreate (H5P_DATASET_CREATE);
00532 
00533         H5Pset_chunk( cparms, DATASET_DIMS, chunk_dims);
00534     }
00535 
00536     hid_t filespace = H5Screate_simple(DATASET_DIMS, mDatasetDims, max_dims);
00537 
00538     // Create the dataset and close filespace
00539     mDatasetId = H5Dcreate(mFileId, "Data", H5T_NATIVE_DOUBLE, filespace, cparms);
00540     H5Sclose(filespace);
00541 
00542     // Create dataspace for the name, unit attribute
00543     const unsigned MAX_STRING_SIZE = 100;
00544     hsize_t columns[1] = {mVariables.size()};
00545     hid_t colspace = H5Screate_simple(1, columns, NULL);
00546 
00547     // Create attribute for variable names
00548     char* col_data = (char*) malloc(mVariables.size() * sizeof(char) * MAX_STRING_SIZE);
00549 
00550     char* col_data_offset = col_data;
00551     for (unsigned var=0; var<mVariables.size(); var++)
00552     {
00553         std::string full_name = mVariables[var].mVariableName + "(" + mVariables[var].mVariableUnits + ")";
00554         strcpy (col_data_offset, full_name.c_str());
00555         col_data_offset += sizeof(char) * MAX_STRING_SIZE;
00556     }
00557 
00558     // Create the type 'string'
00559     hid_t string_type = H5Tcopy(H5T_C_S1);
00560     H5Tset_size(string_type, MAX_STRING_SIZE );
00561     hid_t attr = H5Acreate(mDatasetId, "Variable Details", string_type, colspace, H5P_DEFAULT);
00562 
00563     // Write to the attribute
00564     H5Awrite(attr, string_type, col_data);
00565 
00566     // Close dataspace & attribute
00567     free(col_data);
00568     H5Sclose(colspace);
00569     H5Aclose(attr);
00570 
00571     // Create "boolean" attribute telling the data to be incomplete or not
00572     columns[0] = 1;
00573     colspace = H5Screate_simple(1, columns, NULL);
00574     attr = H5Acreate(mDatasetId, "IsDataComplete", H5T_NATIVE_UINT, colspace, H5P_DEFAULT);
00575 
00576     // Write to the attribute - note that the native boolean is not predictable
00577     unsigned is_data_complete = mIsDataComplete ? 1 : 0;
00578     H5Awrite(attr, H5T_NATIVE_UINT, &is_data_complete);
00579 
00580     H5Sclose(colspace);
00581     H5Aclose(attr);
00582 
00583     if (!mIsDataComplete)
00584     {
00585         // We need to write a map
00586         // Create "unsigned" attribute with the map
00587         columns[0] = mFileFixedDimensionSize;
00588         colspace = H5Screate_simple(1, columns, NULL);
00589         attr = H5Acreate(mDatasetId, "NodeMap", H5T_NATIVE_UINT, colspace, H5P_DEFAULT);
00590 
00591         // Write to the attribute
00592         H5Awrite(attr, H5T_NATIVE_UINT, &mIncompleteNodeIndices[0]);
00593 
00594         H5Sclose(colspace);
00595         H5Aclose(attr);
00596     }
00597 
00598     /*
00599      * Create "Time" dataset
00600      */
00601     if (mIsUnlimitedDimensionSet)
00602     {
00603         hsize_t time_dataset_dims[1] = {1};
00604         hsize_t time_dataset_max_dims[1] = {H5S_UNLIMITED};
00605 
00606         // Modify dataset creation properties to enable chunking.
00607         hsize_t time_chunk_dims[1] ={1};
00608         hid_t time_cparms = H5Pcreate (H5P_DATASET_CREATE);
00609         H5Pset_chunk( time_cparms, 1, time_chunk_dims);
00610 
00611         hid_t time_filespace = H5Screate_simple(1, time_dataset_dims, time_dataset_max_dims);
00612 
00613         // Create the dataset
00614         mTimeDatasetId = H5Dcreate(mFileId, mUnlimitedDimensionName.c_str(), H5T_NATIVE_DOUBLE, time_filespace, time_cparms);
00615 
00616         // Create the dataspace for the attribute
00617         hsize_t one = 1;
00618         hid_t one_column_space = H5Screate_simple(1, &one, NULL);
00619 
00620         // Create an attribute for the time unit
00621         hid_t unit_attr = H5Acreate(mTimeDatasetId, "Unit", string_type, one_column_space, H5P_DEFAULT);
00622 
00623         // Copy the unit to a string MAX_STRING_SIZE long and write it
00624         char unit_string[MAX_STRING_SIZE];
00625         strcpy(unit_string, mUnlimitedDimensionUnit.c_str());
00626         H5Awrite(unit_attr, string_type, unit_string);
00627 
00628         // Close the filespace
00629         H5Sclose(one_column_space);
00630         H5Aclose(unit_attr);
00631         H5Sclose(time_filespace);
00632     }
00633 
00634     /*
00635      * Create the provenance attribute
00636      */
00637 
00638     // Create a longer type for 'string'
00639     const unsigned MAX_PROVENANCE_STRING_SIZE = 1023;
00640     hid_t long_string_type = H5Tcopy(H5T_C_S1);
00641     H5Tset_size(long_string_type, MAX_PROVENANCE_STRING_SIZE );
00642     hsize_t prov_columns[1] = {1};
00643     hid_t provenance_space = H5Screate_simple(1, prov_columns, NULL);
00644     char* provenance_data = (char*) malloc(sizeof(char) * MAX_PROVENANCE_STRING_SIZE);
00645     assert( ChasteBuildInfo::GetProvenanceString().length() < MAX_PROVENANCE_STRING_SIZE);
00646 
00647     strcpy(provenance_data,  ChasteBuildInfo::GetProvenanceString().c_str());
00648     hid_t prov_attr = H5Acreate(mDatasetId, "Chaste Provenance", long_string_type, provenance_space, H5P_DEFAULT);
00649 
00650     // Write to the attribute
00651     H5Awrite(prov_attr, long_string_type, provenance_data);
00652 
00653     // Close dataspace & attribute
00654     free(provenance_data);
00655     H5Sclose(provenance_space);
00656     H5Aclose(prov_attr);
00657 }
00658 
00659 void Hdf5DataWriter::PutVector(int variableID, Vec petscVector)
00660 {
00661     if (mIsInDefineMode)
00662     {
00663         EXCEPTION("Cannot write data while in define mode.");
00664     }
00665 
00666     int vector_size;
00667     VecGetSize(petscVector, &vector_size);
00668 
00669     if ((unsigned) vector_size != mDataFixedDimensionSize)
00670     {
00671         EXCEPTION("Vector size doesn't match fixed dimension");
00672     }
00673 
00674     // Make sure that everything is actually extended to the correct dimension.
00675     PossiblyExtend();
00676 
00677     Vec output_petsc_vector;
00678 
00679     // Decide what to write
00680     if (mSinglePermutation == NULL)
00681     {
00682         // No permutation - just write
00683         output_petsc_vector = petscVector;
00684     }
00685     else
00686     {
00687         assert(mIsDataComplete);
00688         // Make a vector with the same pattern (doesn't copy the data)
00689         VecDuplicate(petscVector, &output_petsc_vector);
00690 
00691         // Apply the permutation matrix
00692         MatMult(mSinglePermutation, petscVector, output_petsc_vector);
00693     }
00694 
00695     // Define a dataset in memory for this process
00696     hid_t memspace=0;
00697     if (mNumberOwned != 0)
00698     {
00699         hsize_t v_size[1] = {mNumberOwned};
00700         memspace = H5Screate_simple(1, v_size, NULL);
00701     }
00702 
00703     // Select hyperslab in the file
00704     hsize_t count[DATASET_DIMS] = {1, mNumberOwned, 1};
00705     hsize_t offset_dims[DATASET_DIMS] = {mCurrentTimeStep, mOffset, variableID};
00706     hid_t file_dataspace = H5Dget_space(mDatasetId);
00707 
00708     // Create property list for collective dataset
00709     hid_t property_list_id = H5Pcreate(H5P_DATASET_XFER);
00710     H5Pset_dxpl_mpio(property_list_id, H5FD_MPIO_COLLECTIVE);
00711 
00712     H5Sselect_hyperslab(file_dataspace, H5S_SELECT_SET, offset_dims, NULL, count, NULL);
00713 
00714     double* p_petsc_vector;
00715     VecGetArray(output_petsc_vector, &p_petsc_vector);
00716 
00717     if (mIsDataComplete)
00718     {
00719         H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, file_dataspace, property_list_id, p_petsc_vector);
00720     }
00721     else
00722     {
00723         if (mUseMatrixForIncompleteData)
00724         {
00725             // Make a vector of the required size
00726             output_petsc_vector = PetscTools::CreateVec(mFileFixedDimensionSize, mNumberOwned);
00727 
00728             // Fill the vector by multiplying complete data by incomplete output matrix
00729             MatMult(mSingleIncompleteOutputMatrix, petscVector, output_petsc_vector);
00730 
00731             double* p_petsc_vector_incomplete;
00732             VecGetArray(output_petsc_vector, &p_petsc_vector_incomplete);
00733             H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, file_dataspace, property_list_id, p_petsc_vector_incomplete);
00734         }
00735         else
00736         {
00737             // Make a local copy of the data you own
00738             double local_data[mNumberOwned];
00739             for (unsigned i=0; i<mNumberOwned; i++)
00740             {
00741                 local_data[i] = p_petsc_vector[ mIncompleteNodeIndices[mOffset+i]-mLo ];
00742 
00743             }
00744             H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, file_dataspace, property_list_id, local_data);
00745         }
00746     }
00747 
00748     VecRestoreArray(output_petsc_vector, &p_petsc_vector);
00749 
00750     H5Pclose(property_list_id);
00751     H5Sclose(file_dataspace);
00752     if (mNumberOwned !=0)
00753     {
00754         H5Sclose(memspace);
00755     }
00756 
00757     if (petscVector != output_petsc_vector)
00758     {
00759         // Free local vector
00760         VecDestroy(output_petsc_vector);
00761     }
00762 }
00763 
00764 void Hdf5DataWriter::PutStripedVector(std::vector<int> variableIDs, Vec petscVector)
00765 {
00766     if (mIsInDefineMode)
00767     {
00768         EXCEPTION("Cannot write data while in define mode.");
00769     }
00770 
00771     if (variableIDs.size() <= 1)
00772     {
00773         EXCEPTION("The PutStripedVector method requires at least two variables ID. If only one is needed, use PutVector method instead");
00774     }
00775 
00776     const int NUM_STRIPES=variableIDs.size();
00777 
00778     int firstVariableID=variableIDs[0];
00779 
00780     // Currently the method only works with consecutive columns, can be extended if needed
00781     for (unsigned i = 1; i < variableIDs.size(); i++)
00782     {
00783         if (variableIDs[i]-variableIDs[i-1] != 1)
00784         {
00785             EXCEPTION("Columns should be consecutive. Try reordering them.");
00786         }
00787     }
00788 
00789     int vector_size;
00790     VecGetSize(petscVector, &vector_size);
00791 
00792     if ((unsigned) vector_size != NUM_STRIPES*mDataFixedDimensionSize)
00793     {
00794         EXCEPTION("Vector size doesn't match fixed dimension");
00795     }
00796 
00797     // Make sure that everything is actually extended to the correct dimension
00798     PossiblyExtend();
00799 
00800     Vec output_petsc_vector;
00801 
00802     // Decide what to write
00803     if (mDoublePermutation == NULL)
00804     {
00805         // No permutation - just write
00806         output_petsc_vector = petscVector;
00807     }
00808     else
00809     {
00810         assert(mIsDataComplete);
00811         // Make a vector with the same pattern (doesn't copy the data)
00812         VecDuplicate(petscVector, &output_petsc_vector);
00813 
00814         // Apply the permutation matrix
00815         MatMult(mDoublePermutation, petscVector, output_petsc_vector);
00816     }
00817     // Define a dataset in memory for this process
00818     hid_t memspace=0;
00819     if (mNumberOwned !=0)
00820     {
00821         hsize_t v_size[1] = {mNumberOwned*NUM_STRIPES};
00822         memspace = H5Screate_simple(1, v_size, NULL);
00823     }
00824 
00825     // Select hyperslab in the file
00826     hsize_t start[DATASET_DIMS] = {mCurrentTimeStep, mOffset, firstVariableID};
00827     hsize_t stride[DATASET_DIMS] = {1, 1, 1};//we are imposing contiguous variables, hence the stride is 1 (3rd component)
00828     hsize_t block_size[DATASET_DIMS] = {1, mNumberOwned, 1};
00829     hsize_t number_blocks[DATASET_DIMS] = {1, 1, NUM_STRIPES};
00830 
00831     hid_t hyperslab_space = H5Dget_space(mDatasetId);
00832     H5Sselect_hyperslab(hyperslab_space, H5S_SELECT_SET, start, stride, number_blocks, block_size);
00833 
00834     // Create property list for collective dataset write, and write! Finally.
00835     hid_t property_list_id = H5Pcreate(H5P_DATASET_XFER);
00836     H5Pset_dxpl_mpio(property_list_id, H5FD_MPIO_COLLECTIVE);
00837 
00838     double* p_petsc_vector;
00839     VecGetArray(output_petsc_vector, &p_petsc_vector);
00840 
00841     if (mIsDataComplete)
00842     {
00843         H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, property_list_id, p_petsc_vector);
00844     }
00845     else
00846     {
00847         if (variableIDs.size() < 3) // incomplete data and striped vector is supported only for NUM_STRIPES=2...for the moment
00848         {
00849             if (mUseMatrixForIncompleteData)
00850             {
00851                 // Make a vector of the required size
00852                 output_petsc_vector = PetscTools::CreateVec(2*mFileFixedDimensionSize, 2*mNumberOwned);
00853 
00854                 // Fill the vector by multiplying complete data by incomplete output matrix
00855                 MatMult(mDoubleIncompleteOutputMatrix, petscVector, output_petsc_vector);
00856 
00857                 double* p_petsc_vector_incomplete;
00858                 VecGetArray(output_petsc_vector, &p_petsc_vector_incomplete);
00859 
00860                 H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, property_list_id, p_petsc_vector_incomplete);
00861             }
00862             else
00863             {
00864                 // Make a local copy of the data you own
00865                 double local_data[mNumberOwned*NUM_STRIPES];
00866                 for (unsigned i=0; i<mNumberOwned; i++)
00867                 {
00868                     unsigned local_node_number = mIncompleteNodeIndices[mOffset+i] - mLo;
00869                     local_data[NUM_STRIPES*i]   = p_petsc_vector[ local_node_number*NUM_STRIPES ];
00870                     local_data[NUM_STRIPES*i+1] = p_petsc_vector[ local_node_number*NUM_STRIPES + 1];
00871                 }
00872 
00873                 H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, property_list_id, local_data);
00874             }
00875         }
00876         else
00877         {
00878             EXCEPTION("The PutStripedVector functionality for incomplete data is supported for only 2 stripes");
00879         }
00880     }
00881 
00882     VecRestoreArray(output_petsc_vector, &p_petsc_vector);
00883 
00884     H5Sclose(hyperslab_space);
00885     if (mNumberOwned != 0)
00886     {
00887         H5Sclose(memspace);
00888     }
00889     H5Pclose(property_list_id);
00890 
00891     if (petscVector != output_petsc_vector)
00892     {
00893         // Free local vector
00894         VecDestroy(output_petsc_vector);
00895     }
00896 }
00897 
00898 void Hdf5DataWriter::PutUnlimitedVariable(double value)
00899 {
00900     if (mIsInDefineMode)
00901     {
00902         EXCEPTION("Cannot write data while in define mode.");
00903     }
00904 
00905     if (!mIsUnlimitedDimensionSet)
00906     {
00907         EXCEPTION("PutUnlimitedVariable() called but no unlimited dimension has been set");
00908     }
00909 
00910     // Make sure that everything is actually extended to the correct dimension.
00911     PossiblyExtend();
00912 
00913     // This data is only written by the master
00914     if (!PetscTools::AmMaster())
00915     {
00916         return;
00917     }
00918 
00919     hsize_t size[1] = {1};
00920     hid_t memspace = H5Screate_simple(1, size, NULL);
00921 
00922     // Select hyperslab in the file.
00923     hsize_t count[1] = {1};
00924     hsize_t offset[1] = {mCurrentTimeStep};
00925     hid_t hyperslab_space = H5Dget_space(mTimeDatasetId);
00926     H5Sselect_hyperslab(hyperslab_space, H5S_SELECT_SET, offset, NULL, count, NULL);
00927 
00928     H5Dwrite(mTimeDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, H5P_DEFAULT, &value);
00929 
00930     H5Sclose(hyperslab_space);
00931     H5Sclose(memspace);
00932 }
00933 
00934 void Hdf5DataWriter::Close()
00935 {
00936     if (mIsInDefineMode)
00937     {
00938         return; // Nothing to do...
00939     }
00940 
00941     H5Dclose(mDatasetId);
00942     if (mIsUnlimitedDimensionSet)
00943     {
00944         H5Dclose(mTimeDatasetId);
00945     }
00946     H5Fclose(mFileId);
00947 
00948     // Cope with being called twice (e.g. if a user calls Close then the destructor)
00949     mIsInDefineMode = true;
00950 }
00951 
00952 void Hdf5DataWriter::DefineUnlimitedDimension(const std::string& rVariableName,
00953                                               const std::string& rVariableUnits,
00954                                               unsigned estimatedLength)
00955 {
00956     if (mIsUnlimitedDimensionSet)
00957     {
00958         EXCEPTION("Unlimited dimension already set. Cannot be defined twice");
00959     }
00960 
00961     if (!mIsInDefineMode)
00962     {
00963         EXCEPTION("Cannot define variables when not in Define mode");
00964     }
00965 
00966     mIsUnlimitedDimensionSet = true;
00967     mUnlimitedDimensionName = rVariableName;
00968     mUnlimitedDimensionUnit = rVariableUnits;
00969     mEstimatedUnlimitedLength = estimatedLength;
00970 }
00971 
00972 void Hdf5DataWriter::AdvanceAlongUnlimitedDimension()
00973 {
00974     if (!mIsUnlimitedDimensionSet)
00975     {
00976         EXCEPTION("Trying to advance along an unlimited dimension without having defined any");
00977     }
00978 
00979     mCurrentTimeStep++;
00980 
00981     /*
00982      * Extend the dataset.
00983      *
00984      * If the user provided an estimate for the length of the
00985      * unlimited dimension, allocate all that space.
00986      */
00987     if ( mEstimatedUnlimitedLength > mDatasetDims[0] )
00988     {
00989         mDatasetDims[0] = mEstimatedUnlimitedLength;
00990         mNeedExtend = true;
00991     }
00992 
00993     // If you are beyond the user estimate increment step by step
00994     if ( mCurrentTimeStep >= (long) mEstimatedUnlimitedLength )
00995     {
00996         mDatasetDims[0]++;
00997         mNeedExtend = true;
00998     }
00999 }
01000 
01001 void Hdf5DataWriter::PossiblyExtend()
01002 {
01003     if (mNeedExtend)
01004     {
01005         H5Dextend (mDatasetId, mDatasetDims);
01006         H5Dextend (mTimeDatasetId, mDatasetDims);
01007     }
01008     mNeedExtend = false;
01009 }
01010 
01011 int Hdf5DataWriter::GetVariableByName(const std::string& rVariableName)
01012 {
01013     int id = -1;
01014 
01015     // Check for the variable name in the existing variables
01016     for (unsigned index=0; index<mVariables.size(); index++)
01017     {
01018         if (mVariables[index].mVariableName == rVariableName)
01019         {
01020             id = index;
01021             break;
01022         }
01023     }
01024     if (id == -1)
01025     {
01026         EXCEPTION("Variable does not exist in hdf5 definitions.");
01027     }
01028     return id;
01029 }
01030 
01031 bool Hdf5DataWriter::ApplyPermutation(const std::vector<unsigned>& rPermutation)
01032 {
01033     if (!mIsInDefineMode)
01034     {
01035         EXCEPTION("Cannot define permutation when not in Define mode");
01036     }
01037 
01038     if (rPermutation.empty())
01039     {
01040         return false;
01041     }
01042 
01043     if (rPermutation.size() != mFileFixedDimensionSize ||
01044         rPermutation.size() != mDataFixedDimensionSize)
01045     {
01046         EXCEPTION("Permutation doesn't match the expected problem size");
01047     }
01048 
01049     // Permutation checker
01050     std::set<unsigned> permutation_pigeon_hole;
01051 
01052     // Fill up the pigeon holes
01053     bool identity_map = true;
01054     for (unsigned i=0; i<mDataFixedDimensionSize; i++)
01055     {
01056         permutation_pigeon_hole.insert(rPermutation[i]);
01057         if (identity_map && i != rPermutation[i])
01058         {
01059             identity_map = false;
01060         }
01061     }
01062     if (identity_map)
01063     {
01064         // Do nothing
01065         return false;
01066     }
01067 
01068     /*
01069      * The pigeon-hole principle states that each index appears exactly once
01070      * so if any don't appear then either one appears twice or something out of
01071      * scope has appeared.
01072      */
01073     for (unsigned i=0; i<mDataFixedDimensionSize; i++)
01074     {
01075         if (permutation_pigeon_hole.count(i) != 1u)
01076         {
01077             EXCEPTION("Permutation vector doesn't contain a valid permutation");
01078         }
01079     }
01080     // Make sure we've not done it already
01081     assert(mSinglePermutation == NULL);
01082     assert(mDoublePermutation == NULL);
01083     PetscTools::SetupMat(mSinglePermutation,   mDataFixedDimensionSize,   mDataFixedDimensionSize, 2, mHi - mLo, mHi - mLo);
01084     PetscTools::SetupMat(mDoublePermutation, 2*mDataFixedDimensionSize, 2*mDataFixedDimensionSize, 4, 2*(mHi - mLo), 2*(mHi - mLo));
01085 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
01086     MatSetOption(mSinglePermutation, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE);
01087     MatSetOption(mDoublePermutation, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE);
01088 #else
01089     MatSetOption(mSinglePermutation, MAT_IGNORE_OFF_PROC_ENTRIES);
01090     MatSetOption(mDoublePermutation, MAT_IGNORE_OFF_PROC_ENTRIES);
01091 #endif
01092     // Only do local rows
01093     for (unsigned row_index=mLo; row_index<mHi; row_index++)
01094     {
01095         // Put zero on the diagonal
01096         MatSetValue(mSinglePermutation, row_index, row_index, 0.0, INSERT_VALUES);
01097 
01098         // Put one at (i,j)
01099         MatSetValue(mSinglePermutation, row_index, rPermutation[row_index], 1.0, INSERT_VALUES);
01100 
01101         unsigned bi_index = 2*row_index;
01102         unsigned perm_index = 2*rPermutation[row_index];
01103 
01104         // Put zeroes on the diagonal
01105         MatSetValue(mDoublePermutation, bi_index, bi_index, 0.0, INSERT_VALUES);
01106         MatSetValue(mDoublePermutation, bi_index+1, bi_index+1, 0.0, INSERT_VALUES);
01107 
01108         // Put ones at (i,j)
01109         MatSetValue(mDoublePermutation, bi_index, perm_index, 1.0, INSERT_VALUES);
01110         MatSetValue(mDoublePermutation, bi_index+1, perm_index+1, 1.0, INSERT_VALUES);
01111     }
01112     MatAssemblyBegin(mSinglePermutation, MAT_FINAL_ASSEMBLY);
01113     MatAssemblyBegin(mDoublePermutation, MAT_FINAL_ASSEMBLY);
01114     MatAssemblyEnd(mSinglePermutation, MAT_FINAL_ASSEMBLY);
01115     MatAssemblyEnd(mDoublePermutation, MAT_FINAL_ASSEMBLY);
01116     return true;
01117 }
01118 
01119 void Hdf5DataWriter::SetFixedChunkSize(unsigned chunkSize)
01120 {
01121     assert(mIsInDefineMode);
01122 
01123     mUseOptimalChunkSizeAlgorithm = false;
01124     mFixedChunkSize = chunkSize;
01125 }
Generated on Thu Dec 22 13:00:07 2011 for Chaste by  doxygen 1.6.3