MOOSE - Multiscale Object Oriented Simulation Environment
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
HDF5DataWriter.cpp
Go to the documentation of this file.
1 // HDF5DataWriter.cpp ---
2 //
3 // Filename: HDF5DataWriter.cpp
4 // Description:
5 // Author: Subhasis Ray
6 // Maintainer: Dilawar Singh
7 // Created: Sat Feb 25 16:03:59 2012 (+0530)
8 
9 // Code:
10 
11 #ifdef USE_HDF5
12 
13 #include "hdf5.h"
14 
15 #include "header.h"
16 #include "../utility/utility.h"
17 
18 #include "HDF5DataWriter.h"
19 
22  "requestOut",
23  "Sends request for a field to target object"
24  );
25  return &requestOut;
26 }
27 
28 const Cinfo * HDF5DataWriter::initCinfo()
29 {
30  static DestFinfo process(
31  "process",
32  "Handle process calls. Gets data from connected fields into a local"
33  " buffer and dumps them to `filename` if the buffer length exceeds"
34  " `flushLimit`",
35  new ProcOpFunc<HDF5DataWriter>( &HDF5DataWriter::process)
36  );
37  static DestFinfo reinit(
38  "reinit",
39  "Reinitialize the object. If the current file handle is valid, it tries"
40  " to close that and open the file specified in current filename field.",
41  new ProcOpFunc<HDF5DataWriter>( &HDF5DataWriter::reinit )
42  );
43  static Finfo * processShared[] = {
44  &process, &reinit
45  };
46 
47  static SharedFinfo proc(
48  "proc",
49  "Shared message to receive process and reinit",
50  processShared, sizeof( processShared ) / sizeof( Finfo* ));
51 
53  "flushLimit",
54  "Buffer size limit for flushing the data from memory to file. Default"
55  " is 4M doubles.",
56  &HDF5DataWriter::setFlushLimit,
57  &HDF5DataWriter::getFlushLimit);
58 
59  static Finfo * finfos[] = {
60  requestOut(),
61  &flushLimit,
62  &proc,
63  };
64 
65 
66 
67  static string doc[] = {
68  "Name", "HDF5DataWriter",
69  "Author", "Subhasis Ray",
70  "Description", "HDF5 file writer for saving field values from multiple objects."
71  "\n"
72  "\nConnect the `requestOut` field of this object to the"
73  " `get{Fieldname}` of other objects where `fieldname` is the"
74  " target value field of type double. The HDF5DataWriter collects the"
75  " current values of the fields in all the targets at each time step in"
76  " a local buffer. When the buffer size exceeds `flushLimit` (default"
77  " 4M), it will write the data into the HDF5 file specified in its"
78  " `filename` field (default moose_output.h5). You can explicitly force"
79  " writing by calling the `flush` function."
80  "\n"
81  "The dataset location in the output file replicates the MOOSE element"
82  " tree structure. Thus, if you record the Vm field from"
83  " `/model[0]/neuron[0]/soma[0], the dataset path will be"
84  " `/model[0]/neuron[0]/soma[0]/vm`"
85  "\n"
86  "\n"
87  "NOTE: The output file remains open until this object is destroyed, or"
88  " `close()` is called explicitly."
89  };
90 
91  static Dinfo< HDF5DataWriter > dinfo;
92  static Cinfo cinfo(
93  "HDF5DataWriter",
94  HDF5WriterBase::initCinfo(),
95  finfos,
96  sizeof(finfos)/sizeof(Finfo*),
97  &dinfo,
98  doc, sizeof( doc ) / sizeof( string ));
99  return &cinfo;
100 }
101 
102 static const Cinfo * hdf5dataWriterCinfo = HDF5DataWriter::initCinfo();
103 
104 HDF5DataWriter::HDF5DataWriter(): flushLimit_(4*1024*1024), steps_(0)
105 {
106 }
107 
108 HDF5DataWriter::~HDF5DataWriter()
109 {
110  close();
111 }
112 
113 void HDF5DataWriter::close()
114 {
115  if (filehandle_ < 0){
116  return;
117  }
118  this->flush();
119  for (map < string, hid_t >::iterator ii = nodemap_.begin();
120  ii != nodemap_.end(); ++ii){
121  if (ii->second >= 0){
122  herr_t status = H5Dclose(ii->second);
123  if (status < 0){
124  cerr << "Warning: closing dataset for "
125  << ii->first << ", returned status = "
126  << status << endl;
127  }
128  }
129  }
130  HDF5WriterBase::close();
131 }
132 
133 void HDF5DataWriter::flush()
134 {
135  if (filehandle_ < 0){
136  cerr << "HDF5DataWriter::flush() - "
137  "Filehandle invalid. Cannot write data." << endl;
138  return;
139  }
140 
141  for (unsigned int ii = 0; ii < datasets_.size(); ++ii){
142  herr_t status = appendToDataset(datasets_[ii], data_[ii]);
143  data_[ii].clear();
144  if (status < 0){
145  cerr << "Warning: appending data for object " << src_[ii]
146  << " returned status " << status << endl;
147  }
148  }
149  HDF5WriterBase::flush();
150  H5Fflush(filehandle_, H5F_SCOPE_LOCAL);
151 }
152 
156 void HDF5DataWriter::process(const Eref & e, ProcPtr p)
157 {
158  if (filehandle_ < 0){
159  return;
160  }
161 
162  vector <double> dataBuf;
163  requestOut()->send(e, &dataBuf);
164  for (unsigned int ii = 0; ii < dataBuf.size(); ++ii){
165  data_[ii].push_back(dataBuf[ii]);
166  }
167  ++steps_;
168  if (steps_ >= flushLimit_){
169  steps_ = 0;
170  for (unsigned int ii = 0; ii < datasets_.size(); ++ii){
171  herr_t status = appendToDataset(datasets_[ii], data_[ii]);
172  data_[ii].clear();
173  if (status < 0){
174  cerr << "Warning: appending data for object " << src_[ii]
175  << " returned status " << status << endl;
176  }
177  }
178  }
179 }
180 
181 void HDF5DataWriter::reinit(const Eref & e, ProcPtr p)
182 {
183  steps_ = 0;
184  for (unsigned int ii = 0; ii < data_.size(); ++ii){
185  H5Dclose(datasets_[ii]);
186  }
187  data_.clear();
188  src_.clear();
189  func_.clear();
190  datasets_.clear();
191  unsigned int numTgt = e.element()->getMsgTargetAndFunctions(e.dataIndex(),
192  requestOut(),
193  src_,
194  func_);
195  assert(numTgt == src_.size());
196  // TODO: what to do when reinit is called? Close the existing file
197  // and open a new one in append mode? Or keep adding to the
198  // current file?
199  if (filename_.empty()){
200  filename_ = "moose_data.h5";
201  }
202  if (filehandle_ > 0 ){
203  close();
204  }
205  if (numTgt == 0){
206  return;
207  }
208  openFile();
209  for (unsigned int ii = 0; ii < src_.size(); ++ii){
210  string varname = func_[ii];
211  size_t found = varname.find("get");
212  if (found == 0){
213  varname = varname.substr(3);
214  if (varname.length() == 0){
215  varname = func_[ii];
216  } else {
217  // TODO: there is no way we can get back the original
218  // field-name case. tolower will get the right name in
219  // most cases as field names start with lower case by
220  // convention in MOOSE.
221  varname[0] = tolower(varname[0]);
222  }
223  }
224  assert(varname.length() > 0);
225  string path = src_[ii].path() + "/" + varname;
226  hid_t dataset_id = getDataset(path);
227  datasets_.push_back(dataset_id);
228  }
229  data_.resize(src_.size());
230 }
231 
235 hid_t HDF5DataWriter::getDataset(string path)
236 {
237  if (filehandle_ < 0){
238  return -1;
239  }
240  herr_t status = H5Eset_auto2(H5E_DEFAULT, NULL, NULL);
241  // Create the groups corresponding to this path
242  string::size_type lastslash = path.find_last_of("/");
243  vector<string> pathTokens;
244  moose::tokenize(path, "/", pathTokens);
245  hid_t prev_id = filehandle_;
246  hid_t id = -1;
247  for ( unsigned int ii = 0; ii < pathTokens.size()-1; ++ii ){
248  // check if object exists
249  htri_t exists = H5Lexists(prev_id, pathTokens[ii].c_str(),
250  H5P_DEFAULT);
251  if (exists > 0){
252  // try to open existing group
253  id = H5Gopen2(prev_id, pathTokens[ii].c_str(), H5P_DEFAULT);
254  } else if (exists == 0) {
255  // If that fails, try to create a group
256  id = H5Gcreate2(prev_id, pathTokens[ii].c_str(),
257  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
258  }
259  if ((exists < 0) || (id < 0)){
260  // Failed to open/create a group, print the
261  // offending path (for debugging; the error is
262  // perhaps at the level of hdf5 or file system).
263  cerr << "Error: failed to open/create group: ";
264  for (unsigned int jj = 0; jj <= ii; ++jj){
265  cerr << "/" << pathTokens[jj];
266  }
267  cerr << endl;
268  prev_id = -1;
269  }
270  if (prev_id >= 0 && prev_id != filehandle_){
271  // Successfully opened/created new group, close the old group
272  status = H5Gclose(prev_id);
273  assert( status >= 0 );
274  }
275  prev_id = id;
276  }
277  string name = pathTokens[pathTokens.size()-1];
278  htri_t exists = H5Lexists(prev_id, name.c_str(), H5P_DEFAULT);
279  hid_t dataset_id = -1;
280  if (exists > 0){
281  dataset_id = H5Dopen2(prev_id, name.c_str(), H5P_DEFAULT);
282  } else if (exists == 0){
283  dataset_id = createDoubleDataset(prev_id, name);
284  } else {
285  cerr << "Error: H5Lexists returned "
286  << exists << " for path \""
287  << path << "\"" << endl;
288  }
289  return dataset_id;
290 }
291 
292 void HDF5DataWriter::setFlushLimit(unsigned int value)
293 {
294  flushLimit_ = value;
295 }
296 
297 unsigned int HDF5DataWriter::getFlushLimit() const
298 {
299  return flushLimit_;
300 }
301 
302 #endif // USE_HDF5
303 //
304 // HDF5DataWriter.cpp ends here
uint32_t value
Definition: moosemodule.h:42
Definition: Dinfo.h:60
unsigned int dataIndex() const
Definition: Eref.h:50
unsigned int getMsgTargetAndFunctions(DataId srcDataId, const SrcFinfo *finfo, vector< ObjId > &tgt, vector< string > &func) const
Definition: Element.cpp:772
Element * element() const
Definition: Eref.h:42
Definition: Eref.h:26
static char name[]
Definition: mfield.cpp:401
void tokenize(const string &str, const string &delimiters, vector< string > &tokens)
Definition: strutil.cpp:19
static char id[]
Definition: mfield.cpp:404
Definition: Cinfo.h:18
static char path[]
Definition: mfield.cpp:403
Definition: Finfo.h:12
static SrcFinfo1< vector< double > * > * requestOut()
Definition: Function.cpp:82