Hdf5DataWriter.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2009
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 * Implementation file for Hdf5DataWriter class.
00030 *
00031 */
00032 
00033 #include "Hdf5DataWriter.hpp"
00034 
00035 #include "Exception.hpp"
00036 #include "OutputFileHandler.hpp"
00037 #include "PetscTools.hpp"
00038 
00039 
00040 Hdf5DataWriter::Hdf5DataWriter(DistributedVectorFactory& rVectorFactory,
00041                                const std::string& rDirectory,
00042                                const std::string& rBaseName,
00043                                bool cleanDirectory)
00044     : mrVectorFactory(rVectorFactory),
00045       mDirectory(rDirectory),
00046       mBaseName(rBaseName),
00047       mCleanDirectory(cleanDirectory),
00048       mIsInDefineMode(true),
00049       mIsFixedDimensionSet(false),
00050       mIsUnlimitedDimensionSet(false),
00051       mFileFixedDimensionSize(0u),
00052       mDataFixedDimensionSize(0u),
00053       mLo(0u),
00054       mHi(0u),
00055       mNumberOwned(0u),
00056       mOffset(0u),
00057       mIsDataComplete(true),
00058       mNeedExtend(false),
00059       mCurrentTimeStep(0)
00060 {
00061 }
00062 
00063 Hdf5DataWriter::~Hdf5DataWriter()
00064 {
00065 }
00066 
00067 void Hdf5DataWriter::DefineFixedDimension(long dimensionSize)
00068 {
00069     if (!mIsInDefineMode)
00070     {
00071         EXCEPTION("Cannot define variables when not in Define mode");
00072     }
00073     if (dimensionSize < 1)
00074     {
00075         EXCEPTION("Fixed dimension must be at least 1 long");
00076     }
00077     if (mIsFixedDimensionSet)
00078     {
00079         EXCEPTION("Fixed dimension already set");
00080     }
00081 
00082     // Work out the ownership details
00083     mLo = mrVectorFactory.GetLow();
00084     mHi = mrVectorFactory.GetHigh();
00085     mNumberOwned = mrVectorFactory.GetLocalOwnership();
00086     mOffset = mLo;
00087     mFileFixedDimensionSize = dimensionSize;
00088     mDataFixedDimensionSize = dimensionSize;
00089     mIsFixedDimensionSet = true;
00090 }
00091 
00092 void Hdf5DataWriter::DefineFixedDimension(const std::vector<unsigned>& rNodesToOuput, long vecSize)
00093 {
00094     unsigned vector_size = rNodesToOuput.size();
00095 
00096     for (unsigned index=0; index < vector_size-1; index++)
00097     {
00098         if (rNodesToOuput[index] >= rNodesToOuput[index+1])
00099         {
00100             EXCEPTION("Input should be monotonic increasing");
00101         }
00102     }
00103 
00104     if ((int) rNodesToOuput.back() >= vecSize)
00105     {
00106         EXCEPTION("Vector size doesn't match nodes to output");
00107     }
00108 
00109     DefineFixedDimension(vecSize);
00110 
00111     mFileFixedDimensionSize = vector_size;
00112     mIsDataComplete = false;
00113     mIncompleteNodeIndices = rNodesToOuput;
00114     mOffset = 0;
00115     mNumberOwned = 0;
00116 
00117     // Compute the offset for writing the data
00118     for (unsigned i=0; i<mIncompleteNodeIndices.size(); i++)
00119     {
00120         if (mIncompleteNodeIndices[i] < mLo)
00121         {
00122             mOffset++;
00123         }
00124         else if (mIncompleteNodeIndices[i] < mHi)
00125         {
00126             mNumberOwned++;
00127         }
00128      }
00129 }
00130 
00131 int Hdf5DataWriter::DefineVariable(const std::string& rVariableName,
00132                                    const std::string& rVariableUnits)
00133 {
00134     if (!mIsInDefineMode)
00135     {
00136         EXCEPTION("Cannot define variables when not in Define mode");
00137     }
00138 
00139     CheckVariableName(rVariableName);
00140     CheckUnitsName(rVariableUnits);
00141 
00142     // Check for the variable being already defined
00143     for (unsigned index=0; index<mVariables.size(); index++)
00144     {
00145         if (mVariables[index].mVariableName == rVariableName)
00146         {
00147             EXCEPTION("Variable name already exists");
00148         }
00149     }
00150 
00151     DataWriterVariable new_variable;
00152     new_variable.mVariableName = rVariableName;
00153     new_variable.mVariableUnits = rVariableUnits;
00154     int variable_id;
00155 
00156     // Add the variable to the variable vector
00157     mVariables.push_back(new_variable);
00158 
00159     // Use the index of the variable vector as the variable ID.
00160     // This is ok since there is no way to remove variables.
00161     variable_id = mVariables.size() - 1;
00162 
00163     return variable_id;
00164 }
00165 
00166 void Hdf5DataWriter::CheckVariableName(const std::string& rName)
00167 {
00168     if (rName.length() == 0)
00169     {
00170         EXCEPTION("Variable name not allowed: may not be blank.");
00171     }
00172     CheckUnitsName(rName);
00173 }
00174 
00175 void Hdf5DataWriter::CheckUnitsName(const std::string& rName)
00176 {
00177     for (unsigned i=0; i<rName.length(); i++)
00178     {
00179         if (!isalnum(rName[i]) && !(rName[i]=='_'))
00180         {
00181             std::string error = "Variable name/units '" + rName + "' not allowed: may only contain alphanumeric characters or '_'.";
00182             EXCEPTION(error);
00183         }
00184     }
00185 }
00186 
00187 void Hdf5DataWriter::EndDefineMode()
00188 {
00189     //Check that at least one variable has been defined
00190     if (mVariables.size() < 1)
00191     {
00192         EXCEPTION("Cannot end define mode. No variables have been defined.");
00193     }
00194 
00195     //Check that a fixed dimension has been defined
00196     if (mIsFixedDimensionSet == false)
00197     {
00198         EXCEPTION("Cannot end define mode. One fixed dimension should be defined.");
00199     }
00200 
00201     mIsInDefineMode = false;
00202 
00203     OutputFileHandler output_file_handler(mDirectory, mCleanDirectory);
00204     std::string results_dir = output_file_handler.GetOutputDirectoryFullPath();
00205     std::string file_name = results_dir + mBaseName + ".h5";
00206 
00207     // Set up a property list saying how we'll open the file
00208     hid_t property_list_id = H5Pcreate(H5P_FILE_ACCESS);
00209     H5Pset_fapl_mpio(property_list_id, PETSC_COMM_WORLD, MPI_INFO_NULL);
00210 
00211     // Create a file (collectively) and free the property list
00212     mFileId = H5Fcreate(file_name.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, property_list_id);
00213     H5Pclose(property_list_id);
00214 
00215     /*
00216      *  Create "Data" dataset
00217      */
00218     // Create the dataspace for the dataset.
00219     mDatasetDims[0] = 1; // While developing we got a non-documented "only the first dimension can be extendible" error.
00220     mDatasetDims[1] = mFileFixedDimensionSize;
00221     mDatasetDims[2] = mVariables.size();
00222 
00223     hsize_t* max_dims = NULL;
00224     hsize_t dataset_max_dims[DATASET_DIMS]; // dataset max dimensions
00225 
00226     hid_t cparms = H5P_DEFAULT;
00227 
00228     if (mIsUnlimitedDimensionSet)
00229     {
00230         dataset_max_dims[0] = H5S_UNLIMITED;
00231         dataset_max_dims[1] = mDatasetDims[1];
00232         dataset_max_dims[2] = mDatasetDims[2];
00233         max_dims = dataset_max_dims;
00234 
00235         // Modify dataset creation properties to enable chunking.
00236         hsize_t chunk_dims[DATASET_DIMS] ={1, mDatasetDims[1], mDatasetDims[2]};
00237         cparms = H5Pcreate (H5P_DATASET_CREATE);
00238         H5Pset_chunk( cparms, DATASET_DIMS, chunk_dims);
00239     }
00240 
00241     hid_t filespace = H5Screate_simple(DATASET_DIMS, mDatasetDims, max_dims);
00242 
00243     // Create the dataset and close filespace
00244     mDatasetId = H5Dcreate(mFileId, "Data", H5T_NATIVE_DOUBLE, filespace, cparms);
00245     H5Sclose(filespace);
00246 
00247     // Create dataspace for the name, unit attribute
00248     const unsigned MAX_STRING_SIZE = 100;
00249     hsize_t columns[1] = {mVariables.size()};
00250     hid_t colspace = H5Screate_simple(1, columns, NULL);
00251 
00252     // Create attribute for variable names
00253     char* col_data = (char*) malloc(mVariables.size() * sizeof(char) * MAX_STRING_SIZE);
00254 
00255     char* col_data_offset = col_data;
00256     for (unsigned var=0; var<mVariables.size(); var++)
00257     {
00258         std::string full_name = mVariables[var].mVariableName + "(" + mVariables[var].mVariableUnits + ")";
00259         strcpy (col_data_offset, full_name.c_str());
00260         col_data_offset += sizeof(char) * MAX_STRING_SIZE;
00261     }
00262 
00263     // Create the type 'string'
00264     hid_t string_type = H5Tcopy(H5T_C_S1);
00265     H5Tset_size(string_type, MAX_STRING_SIZE );
00266     hid_t attr = H5Acreate(mDatasetId, "Variable Details", string_type, colspace, H5P_DEFAULT);
00267 
00268     // Write to the attribute
00269     H5Awrite(attr, string_type, col_data);
00270 
00271     // Close dataspace & attribute
00272     free(col_data);
00273     H5Sclose(colspace);
00274     H5Aclose(attr);
00275 
00276     // Create "boolean" attribute telling the data to be incomplete or not
00277     columns[0] = 1;
00278     colspace = H5Screate_simple(1, columns, NULL);
00279     attr = H5Acreate(mDatasetId, "IsDataComplete", H5T_NATIVE_UINT, colspace, H5P_DEFAULT);
00280 
00281     // Write to the attribute - note that the native boolean is not predictable
00282     unsigned is_data_complete= mIsDataComplete ? 1 : 0;
00283     H5Awrite(attr, H5T_NATIVE_UINT, &is_data_complete);
00284 
00285     H5Sclose(colspace);
00286     H5Aclose(attr);
00287 
00288     if (!mIsDataComplete)
00289     {
00290         // We need to write a map
00291         // Create "unsigned" attribute with the map
00292         columns[0] = mFileFixedDimensionSize;
00293         colspace = H5Screate_simple(1, columns, NULL);
00294         attr = H5Acreate(mDatasetId, "NodeMap", H5T_NATIVE_UINT, colspace, H5P_DEFAULT);
00295 
00296         // Write to the attribute
00297         H5Awrite(attr, H5T_NATIVE_UINT, &mIncompleteNodeIndices[0]);
00298 
00299         H5Sclose(colspace);
00300         H5Aclose(attr);
00301     }
00302 
00303     /*
00304      *  Create "Time" dataset
00305      */
00306     if (mIsUnlimitedDimensionSet)
00307     {
00308         hsize_t time_dataset_dims[1] = {1};
00309         hsize_t time_dataset_max_dims[1] = {H5S_UNLIMITED};
00310 
00311         // Modify dataset creation properties to enable chunking.
00312         hsize_t time_chunk_dims[1] ={1};
00313         hid_t time_cparms = H5Pcreate (H5P_DATASET_CREATE);
00314         H5Pset_chunk( time_cparms, 1, time_chunk_dims);
00315 
00316         hid_t time_filespace = H5Screate_simple(1, time_dataset_dims, time_dataset_max_dims);
00317 
00318         // Create the dataset
00319         mTimeDatasetId = H5Dcreate(mFileId, mUnlimitedDimensionName.c_str(), H5T_NATIVE_DOUBLE, time_filespace, time_cparms);
00320 
00321         // Create the dataspace for the attribute
00322         hsize_t one = 1;
00323         hid_t one_column_space = H5Screate_simple(1, &one, NULL);
00324 
00325         // Create an attribute for the time unit
00326         hid_t unit_attr = H5Acreate(mTimeDatasetId, "Unit", string_type, one_column_space, H5P_DEFAULT);
00327 
00328         // Copy the unit to a string MAX_STRING_SIZE long and write it
00329         char unit_string[MAX_STRING_SIZE];
00330         strcpy(unit_string, mUnlimitedDimensionUnit.c_str());
00331         H5Awrite(unit_attr, string_type, unit_string);
00332 
00333         // Close the filespace
00334         H5Sclose(one_column_space);
00335         H5Aclose(unit_attr);
00336         H5Sclose(time_filespace);
00337     }
00338 }
00339 
00340 void Hdf5DataWriter::PutVector(int variableID, Vec petscVector)
00341 {
00342     if (mIsInDefineMode)
00343     {
00344         EXCEPTION("Cannot write data while in define mode.");
00345     }
00346 
00347     int vector_size;
00348     VecGetSize(petscVector, &vector_size);
00349 
00350     if ((unsigned) vector_size != mDataFixedDimensionSize)
00351     {
00352         EXCEPTION("Vector size doesn't match fixed dimension");
00353     }
00354 
00355     // Make sure that everything is actually extended to the correct dimension.
00356     PossiblyExtend();
00357 
00358     // Define a dataset in memory for this process
00359     hid_t memspace=0;
00360     if (mNumberOwned !=0)
00361     {
00362         hsize_t v_size[1] = {mNumberOwned};
00363         memspace = H5Screate_simple(1, v_size, NULL);
00364     }
00365     // Select hyperslab in the file
00366     hsize_t count[DATASET_DIMS] = {1, mNumberOwned, 1};
00367     hsize_t offset_dims[DATASET_DIMS] = {mCurrentTimeStep, mOffset, variableID};
00368     hid_t file_dataspace = H5Dget_space(mDatasetId);
00369 
00370     // Create property list for collective dataset
00371     hid_t property_list_id = H5Pcreate(H5P_DATASET_XFER);
00372     H5Pset_dxpl_mpio(property_list_id, H5FD_MPIO_COLLECTIVE);
00373 
00374     H5Sselect_hyperslab(file_dataspace, H5S_SELECT_SET, offset_dims, NULL, count, NULL);
00375 
00376     double *p_petsc_vector;
00377     VecGetArray(petscVector, &p_petsc_vector);
00378 
00379     if (mIsDataComplete)
00380     {
00381         H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, file_dataspace, property_list_id, p_petsc_vector);
00382     }
00383     else
00384     {
00385         // Make a local copy of the data you own
00386         double local_data[mNumberOwned];
00387         for (unsigned i=0; i<mNumberOwned; i++)
00388         {
00389             local_data[i] = p_petsc_vector[ mIncompleteNodeIndices[mOffset+i]-mLo ];
00390 
00391         }
00392         H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, file_dataspace, property_list_id, local_data);
00393     }
00394 
00395     VecRestoreArray(petscVector, &p_petsc_vector);
00396 
00397     H5Pclose(property_list_id);
00398     H5Sclose(file_dataspace);
00399     if (mNumberOwned !=0)
00400     {
00401         H5Sclose(memspace);
00402     }
00403 }
00404 
00405 void Hdf5DataWriter::PutStripedVector(int firstVariableID, int secondVariableID, Vec petscVector)
00406 {
00407     if (mIsInDefineMode)
00408     {
00409         EXCEPTION("Cannot write data while in define mode.");
00410     }
00411 
00412     int NUM_STRIPES=2;
00413 
00414     // Currently the method only works with consecutive columns, can be extended if needed
00415     if (secondVariableID-firstVariableID != 1)
00416     {
00417         EXCEPTION("Columns should be consecutive. Try reordering them.");
00418     }
00419 
00420     int vector_size;
00421     VecGetSize(petscVector, &vector_size);
00422 
00423     if ((unsigned) vector_size != NUM_STRIPES*mDataFixedDimensionSize)
00424     {
00425         EXCEPTION("Vector size doesn't match fixed dimension");
00426     }
00427 
00428     // Make sure that everything is actually extended to the correct dimension
00429     PossiblyExtend();
00430 
00431     // Define a dataset in memory for this process
00432     hid_t memspace=0;
00433     if (mNumberOwned !=0)
00434     {
00435         hsize_t v_size[1] = {mNumberOwned*NUM_STRIPES};
00436         memspace = H5Screate_simple(1, v_size, NULL);
00437     }
00438 
00439     // Select hyperslab in the file
00440     hsize_t start[DATASET_DIMS] = {mCurrentTimeStep, mOffset, firstVariableID};
00441     hsize_t stride[DATASET_DIMS] = {1, 1, secondVariableID-firstVariableID};
00442     hsize_t block_size[DATASET_DIMS] = {1, mNumberOwned, 1};
00443     hsize_t number_blocks[DATASET_DIMS] = {1, 1, NUM_STRIPES};
00444 
00445     hid_t hyperslab_space = H5Dget_space(mDatasetId);
00446     H5Sselect_hyperslab(hyperslab_space, H5S_SELECT_SET, start, stride, number_blocks, block_size);
00447 
00448     // Create property list for collective dataset write, and write! Finally.
00449     hid_t property_list_id = H5Pcreate(H5P_DATASET_XFER);
00450     H5Pset_dxpl_mpio(property_list_id, H5FD_MPIO_COLLECTIVE);
00451 
00452     double *p_petsc_vector;
00453     VecGetArray(petscVector, &p_petsc_vector);
00454 
00455     if (mIsDataComplete)
00456     {
00457         H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, property_list_id, p_petsc_vector);
00458     }
00459     else
00460     {
00461         // Make a local copy of the data you own
00462         double local_data[mNumberOwned*NUM_STRIPES];
00463         for (unsigned i=0; i<mNumberOwned; i++)
00464         {
00466             unsigned local_node_number=mIncompleteNodeIndices[mOffset+i] - mLo;
00467             local_data[NUM_STRIPES*i]   = p_petsc_vector[ local_node_number*NUM_STRIPES ];
00468             local_data[NUM_STRIPES*i+1] = p_petsc_vector[ local_node_number*NUM_STRIPES + 1];
00469         }
00470         H5Dwrite(mDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, property_list_id, local_data);
00471     }
00472 
00473     VecRestoreArray(petscVector, &p_petsc_vector);
00474 
00475     H5Sclose(hyperslab_space);
00476     if (mNumberOwned != 0)
00477     {
00478         H5Sclose(memspace);
00479     }
00480     H5Pclose(property_list_id);
00481 }
00482 
00483 void Hdf5DataWriter::PutUnlimitedVariable(double value)
00484 {
00485     if (mIsInDefineMode)
00486     {
00487         EXCEPTION("Cannot write data while in define mode.");
00488     }
00489 
00490     if (!mIsUnlimitedDimensionSet)
00491     {
00492         EXCEPTION("PutUnlimitedVariable() called but no unlimited dimension has been set");
00493     }
00494 
00495     // This data is only written by the master
00496     if (!PetscTools::AmMaster())
00497     {
00498         return;
00499     }
00500 
00501     // Make sure that everything is actually extended to the correct dimension.
00502     PossiblyExtend();
00503 
00504     hsize_t size[1] = {1};
00505     hid_t memspace = H5Screate_simple(1, size, NULL);
00506 
00507     // Select hyperslab in the file.
00508     hsize_t count[1] = {1};
00509     hsize_t offset[1] = {mCurrentTimeStep};
00510     hid_t hyperslab_space = H5Dget_space(mTimeDatasetId);
00511     H5Sselect_hyperslab(hyperslab_space, H5S_SELECT_SET, offset, NULL, count, NULL);
00512 
00513     H5Dwrite(mTimeDatasetId, H5T_NATIVE_DOUBLE, memspace, hyperslab_space, H5P_DEFAULT, &value);
00514 
00515     H5Sclose(hyperslab_space);
00516     H5Sclose(memspace);
00517 }
00518 
00519 void Hdf5DataWriter::Close()
00520 {
00521     if (mIsInDefineMode)
00522     {
00523         return; // Should this throw an exception?  Is it an error to begin defining a writer and then to attempt to close it properly?
00524     }
00525     H5Dclose(mDatasetId);
00526 
00527     if (mIsUnlimitedDimensionSet)
00528     {
00529         H5Dclose(mTimeDatasetId);
00530     }
00531 
00532     H5Fclose(mFileId);
00533 }
00534 
00535 void Hdf5DataWriter::DefineUnlimitedDimension(const std::string& rVariableName,
00536                                               const std::string& rVariableUnits)
00537 {
00538     if (mIsUnlimitedDimensionSet)
00539     {
00540         EXCEPTION("Unlimited dimension already set. Cannot be defined twice");
00541     }
00542 
00543     if (!mIsInDefineMode)
00544     {
00545         EXCEPTION("Cannot define variables when not in Define mode");
00546     }
00547 
00548     mIsUnlimitedDimensionSet = true;
00549     mUnlimitedDimensionName = rVariableName;
00550     mUnlimitedDimensionUnit = rVariableUnits;
00551 }
00552 
00553 void Hdf5DataWriter::AdvanceAlongUnlimitedDimension()
00554 {
00555     if (!mIsUnlimitedDimensionSet)
00556     {
00557         EXCEPTION("Trying to advance along an unlimited dimension without having defined any");
00558     }
00559 
00560     // Extend the dataset
00561     mDatasetDims[0]++;
00562     mNeedExtend = true;
00563 
00564     mCurrentTimeStep++;
00565 }
00566 
00567 void Hdf5DataWriter::PossiblyExtend()
00568 {
00569     if (mNeedExtend)
00570     {
00571         H5Dextend (mDatasetId, mDatasetDims);
00572         H5Dextend (mTimeDatasetId, mDatasetDims);
00573     }
00574     mNeedExtend = false;
00575 }

Generated on Tue Aug 4 16:10:22 2009 for Chaste by  doxygen 1.5.5