MFEM  v3.2
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
datacollection.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
3 // reserved. See file COPYRIGHT for details.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability see http://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the GNU Lesser General Public License (as published by the Free
10 // Software Foundation) version 2.1 dated February 1999.
11 
12 #include "fem.hpp"
13 #include "picojson.h"
14 
15 #include <fstream>
16 #include <cerrno> // errno
17 #ifndef _WIN32
18 #include <sys/stat.h> // mkdir
19 #else
20 #include <direct.h> // _mkdir
21 #define mkdir(dir, mode) _mkdir(dir)
22 #endif
23 
24 namespace mfem
25 {
26 
27 using namespace std;
28 
29 // Helper string functions. Will go away in C++11
30 
31 string to_string(int i)
32 {
33  stringstream ss;
34  ss << i;
35 
36  // trim leading spaces
37  string out_str = ss.str();
38  out_str = out_str.substr(out_str.find_first_not_of(" \t"));
39  return out_str;
40 }
41 
42 string to_padded_string(int i, int digits)
43 {
44  ostringstream oss;
45  oss << setw(digits) << setfill('0') << i;
46  return oss.str();
47 }
48 
49 int to_int(string str)
50 {
51  int i;
52  stringstream(str) >> i;
53  return i;
54 }
55 
56 // class DataCollection implementation
57 
58 DataCollection::DataCollection(const char *collection_name)
59 {
60  name = collection_name;
61  // leave prefix_path empty
62  mesh = NULL;
63  myid = 0;
64  num_procs = 1;
65  serial = true;
66  own_data = false;
67  cycle = -1;
68  time = 0.0;
69  precision = precision_default;
70  pad_digits = pad_digits_default;
71  error = NO_ERROR;
72 }
73 
74 DataCollection::DataCollection(const char *collection_name, Mesh *_mesh)
75 {
76  name = collection_name;
77  // leave prefix_path empty
78  mesh = _mesh;
79  myid = 0;
80  num_procs = 1;
81  serial = true;
82 #ifdef MFEM_USE_MPI
83  ParMesh *par_mesh = dynamic_cast<ParMesh*>(mesh);
84  if (par_mesh)
85  {
86  myid = par_mesh->GetMyRank();
87  num_procs = par_mesh->GetNRanks();
88  serial = false;
89  }
90 #endif
91  own_data = false;
92  cycle = -1;
93  time = 0.0;
94  precision = precision_default;
95  pad_digits = pad_digits_default;
96  error = NO_ERROR;
97 }
98 
100 {
101  if (own_data) { delete mesh; }
102  mesh = new_mesh;
103  myid = 0;
104  num_procs = 1;
105  serial = true;
106 #ifdef MFEM_USE_MPI
107  ParMesh *par_mesh = dynamic_cast<ParMesh*>(mesh);
108  if (par_mesh)
109  {
110  myid = par_mesh->GetMyRank();
111  num_procs = par_mesh->GetNRanks();
112  serial = false;
113  }
114 #endif
115 }
116 
117 void DataCollection::RegisterField(const char* name, GridFunction *gf)
118 {
119  if (own_data && HasField(name))
120  {
121  delete field_map[name];
122  }
123  field_map[name] = gf;
124 }
125 
126 GridFunction *DataCollection::GetField(const char *field_name)
127 {
128  if (HasField(field_name))
129  {
130  return field_map[field_name];
131  }
132  else
133  {
134  return NULL;
135  }
136 }
137 
138 void DataCollection::SetPrefixPath(const char *prefix)
139 {
140  if (prefix)
141  {
142  prefix_path = prefix;
143  if (!prefix_path.empty() && prefix_path[prefix_path.size()-1] != '/')
144  {
145  prefix_path += '/';
146  }
147  }
148  else
149  {
150  prefix_path.clear();
151  }
152 }
153 
155 {
156  SaveMesh();
157 
158  if (error) { return; }
159 
160  for (map<string,GridFunction*>::iterator it = field_map.begin();
161  it != field_map.end(); ++it)
162  {
163  SaveOneField(it);
164  }
165 }
166 
167 static int create_directory(const string &dir_name, const Mesh *mesh, int myid)
168 {
169  int err;
170 #ifndef MFEM_USE_MPI
171  err = mkdir(dir_name.c_str(), 0777);
172  err = (err && (errno != EEXIST)) ? 1 : 0;
173 #else
174  const ParMesh *pmesh = dynamic_cast<const ParMesh*>(mesh);
175  if (myid == 0 || pmesh == NULL)
176  {
177  err = mkdir(dir_name.c_str(), 0777);
178  err = (err && (errno != EEXIST)) ? 1 : 0;
179  if (pmesh)
180  {
181  MPI_Bcast(&err, 1, MPI_INT, 0, pmesh->GetComm());
182  }
183  }
184  else
185  {
186  // Wait for rank 0 to create the directory
187  MPI_Bcast(&err, 1, MPI_INT, 0, pmesh->GetComm());
188  }
189 #endif
190  return err;
191 }
192 
194 {
195  int err;
196  if (!prefix_path.empty())
197  {
198  err = create_directory(prefix_path, mesh, myid);
199  if (err)
200  {
201  error = WRITE_ERROR;
202  MFEM_WARNING("Error creating directory: " << prefix_path);
203  return; // do not even try to write the mesh
204  }
205  }
206 
207  string dir_name = prefix_path;
208  if (cycle == -1)
209  {
210  dir_name += name;
211  }
212  else
213  {
214  dir_name += name + "_" + to_padded_string(cycle, pad_digits);
215  }
216  err = create_directory(dir_name, mesh, myid);
217  if (err)
218  {
219  error = WRITE_ERROR;
220  MFEM_WARNING("Error creating directory: " << dir_name);
221  return; // do not even try to write the mesh
222  }
223 
224  string mesh_name;
225  if (serial)
226  {
227  mesh_name = dir_name + "/mesh";
228  }
229  else
230  {
231  mesh_name = dir_name + "/mesh." + to_padded_string(myid, pad_digits);
232  }
233  ofstream mesh_file(mesh_name.c_str());
234  mesh_file.precision(precision);
235  mesh->Print(mesh_file);
236  if (!mesh_file)
237  {
238  error = WRITE_ERROR;
239  MFEM_WARNING("Error writing mesh to file: " << mesh_name);
240  }
241 }
242 
244  const std::map<std::string,GridFunction*>::iterator &it)
245 {
246  string dir_name = prefix_path;
247  if (cycle == -1)
248  {
249  dir_name += name;
250  }
251  else
252  {
253  dir_name += name + "_" + to_padded_string(cycle, pad_digits);
254  }
255 
256  string file_name;
257  if (serial)
258  {
259  file_name = dir_name + "/" + it->first;
260  }
261  else
262  {
263  file_name = dir_name + "/" + it->first + "." +
264  to_padded_string(myid, pad_digits);
265  }
266  ofstream field_file(file_name.c_str());
267  field_file.precision(precision);
268  (it->second)->Save(field_file);
269  if (!field_file)
270  {
271  error = WRITE_ERROR;
272  MFEM_WARNING("Error writting field to file: " << it->first);
273  }
274 }
275 
276 void DataCollection::SaveField(const char *field_name)
277 {
278  const map<string,GridFunction*>::iterator it = field_map.find(field_name);
279  if (it != field_map.end())
280  {
281  SaveOneField(it);
282  }
283 }
284 
286 {
287  if (own_data)
288  {
289  delete mesh;
290  }
291  mesh = NULL;
292  for (map<string,GridFunction*>::iterator it = field_map.begin();
293  it != field_map.end(); ++it)
294  {
295  if (own_data)
296  {
297  delete it->second;
298  }
299  it->second = NULL;
300  }
301  own_data = false;
302 }
303 
305 {
306  DeleteData();
307  field_map.clear();
308 }
309 
311 {
312  if (own_data)
313  {
314  delete mesh;
315  for (map<string,GridFunction*>::iterator it = field_map.begin();
316  it != field_map.end(); ++it)
317  {
318  delete it->second;
319  }
320  }
321 }
322 
323 
324 // class VisItDataCollection implementation
325 
326 VisItDataCollection::VisItDataCollection(const char *collection_name)
327  : DataCollection(collection_name)
328 {
329  serial = false; // always include rank in file names
330  cycle = 0; // always include cycle in directory names
331 
332  spatial_dim = 0;
333  topo_dim = 0;
335 }
336 
337 VisItDataCollection::VisItDataCollection(const char *collection_name,
338  Mesh *mesh)
339  : DataCollection(collection_name, mesh)
340 {
341  serial = false; // always include rank in file names
342  cycle = 0; // always include cycle in directory names
343 
344  spatial_dim = mesh->SpaceDimension();
345  topo_dim = mesh->Dimension();
347 }
348 
350 {
351  DataCollection::SetMesh(new_mesh);
352  serial = false; // always include rank in file names
353 
354  spatial_dim = mesh->SpaceDimension();
355  topo_dim = mesh->Dimension();
356 }
357 
359 {
361  field_info_map[name] = VisItFieldInfo("nodes", gf->VectorDim());
362 }
363 
364 void VisItDataCollection::SetMaxLevelsOfDetail(int max_levels_of_detail)
365 {
366  visit_max_levels_of_detail = max_levels_of_detail;
367 }
368 
370 {
371  field_info_map.clear();
373 }
374 
376 {
378  SaveRootFile();
379 }
380 
382 {
383  if (myid == 0)
384  {
385  string root_name = prefix_path + name + "_" +
386  to_padded_string(cycle, pad_digits) + ".mfem_root";
387  ofstream root_file(root_name.c_str());
388  root_file << GetVisItRootString();
389  if (!root_file)
390  {
391  error = WRITE_ERROR;
392  MFEM_WARNING("Error writting VisIt Root file: " << root_name);
393  }
394  }
395 }
396 
398 {
399  DeleteAll();
400  cycle = _cycle;
401  string root_name = prefix_path + name + "_" +
402  to_padded_string(cycle, pad_digits) + ".mfem_root";
403  LoadVisItRootFile(root_name);
404  if (!error)
405  {
406  LoadMesh();
407  }
408  if (!error)
409  {
410  LoadFields();
411  }
412  if (!error)
413  {
414  own_data = true;
415  }
416  else
417  {
418  DeleteAll();
419  }
420 }
421 
423 {
424  ifstream root_file(root_name.c_str());
425  stringstream buffer;
426  buffer << root_file.rdbuf();
427  if (!buffer)
428  {
429  error = READ_ERROR;
430  MFEM_WARNING("Error reading the VisIt Root file: " << root_name);
431  }
432  else
433  {
434  ParseVisItRootString(buffer.str());
435  }
436 }
437 
439 {
440  string mesh_fname = prefix_path + name + "_" +
442  "/mesh." + to_padded_string(myid, pad_digits);
443  ifstream file(mesh_fname.c_str());
444  if (!file)
445  {
446  error = READ_ERROR;
447  MFEM_WARNING("Unable to open mesh file: " << mesh_fname);
448  return;
449  }
450  // TODO: 1) load parallel mesh on one processor
451  // 2) load parallel mesh on the same number of processors
452  mesh = new Mesh(file, 1, 1);
453  spatial_dim = mesh->SpaceDimension();
454  topo_dim = mesh->Dimension();
455 }
456 
458 {
459  string path_left = prefix_path + name + "_" +
461  string path_right = "." + to_padded_string(myid, pad_digits);
462 
463  field_map.clear();
464  for (map<string,VisItFieldInfo>::iterator it = field_info_map.begin();
465  it != field_info_map.end(); ++it)
466  {
467  string fname = path_left + it->first + path_right;
468  ifstream file(fname.c_str());
469  if (!file)
470  {
471  error = READ_ERROR;
472  MFEM_WARNING("Unable to open field file: " << fname);
473  return;
474  }
475  // TODO: 1) load parallel GridFunction on one processor
476  // 2) load parallel GridFunction on the same number of processors
477  field_map[it->first] = new GridFunction(mesh, file);
478  }
479 }
480 
482 {
483  // Get the path string (relative to where the root file is, i.e. no prefix).
484  string path_str = name + "_" + to_padded_string(cycle, pad_digits) + "/";
485 
486  // We have to build the json tree inside out to get all the values in there
487  picojson::object top, dsets, main, mesh, fields, field, mtags, ftags;
488 
489  // Build the mesh data
490  string file_ext_format = ".%0" + to_string(pad_digits) + "d";
491  mtags["spatial_dim"] = picojson::value(to_string(spatial_dim));
492  mtags["topo_dim"] = picojson::value(to_string(topo_dim));
493  mtags["max_lods"] = picojson::value(to_string(visit_max_levels_of_detail));
494  mesh["path"] = picojson::value(path_str + "mesh" + file_ext_format);
495  mesh["tags"] = picojson::value(mtags);
496 
497  // Build the fields data entries
498  for (map<string,VisItFieldInfo>::iterator it = field_info_map.begin();
499  it != field_info_map.end(); ++it)
500  {
501  ftags["assoc"] = picojson::value((it->second).association);
502  ftags["comps"] = picojson::value(to_string((it->second).num_components));
503  field["path"] = picojson::value(path_str + it->first + file_ext_format);
504  field["tags"] = picojson::value(ftags);
505  fields[it->first] = picojson::value(field);
506  }
507 
508  main["cycle"] = picojson::value(double(cycle));
509  main["time"] = picojson::value(time);
510  main["domains"] = picojson::value(double(num_procs));
511  main["mesh"] = picojson::value(mesh);
512  if (!field_info_map.empty())
513  {
514  main["fields"] = picojson::value(fields);
515  }
516 
517  dsets["main"] = picojson::value(main);
518  top["dsets"] = picojson::value(dsets);
519 
520  return picojson::value(top).serialize(true);
521 }
522 
524 {
525  picojson::value top, dsets, main, mesh, fields;
526  string parse_err = picojson::parse(top, json);
527  if (!parse_err.empty())
528  {
529  error = READ_ERROR;
530  MFEM_WARNING("Unable to parse visit root data.");
531  return;
532  }
533 
534  // Process "main"
535  dsets = top.get("dsets");
536  main = dsets.get("main");
537  cycle = int(main.get("cycle").get<double>());
538  time = main.get("time").get<double>();
539  num_procs = int(main.get("domains").get<double>());
540  mesh = main.get("mesh");
541  fields = main.get("fields");
542 
543  // ... Process "mesh"
544 
545  // Set the DataCollection::name using the mesh path
546  string path = mesh.get("path").get<string>();
547  size_t right_sep = path.find('_');
548  if (right_sep == string::npos)
549  {
550  error = READ_ERROR;
551  MFEM_WARNING("Unable to parse visit root data.");
552  return;
553  }
554  name = path.substr(0, right_sep);
555 
556  spatial_dim = to_int(mesh.get("tags").get("spatial_dim").get<string>());
557  topo_dim = to_int(mesh.get("tags").get("topo_dim").get<string>());
559  to_int(mesh.get("tags").get("max_lods").get<string>());
560 
561  // ... Process "fields"
562  field_info_map.clear();
563  if (fields.is<picojson::object>())
564  {
565  picojson::object fields_obj = fields.get<picojson::object>();
566  for (picojson::object::iterator it = fields_obj.begin();
567  it != fields_obj.end(); ++it)
568  {
569  picojson::value tags = it->second.get("tags");
570  field_info_map[it->first] =
571  VisItFieldInfo(tags.get("assoc").get<string>(),
572  to_int(tags.get("comps").get<string>()));
573  }
574  }
575 }
576 
577 }
int to_int(string str)
virtual void SaveMesh()
Save the mesh, creating the collection directory.
std::map< std::string, GridFunction * > field_map
The fields and their names (used when saving)
Class for grid function - Vector with associated FE space.
Definition: gridfunc.hpp:27
int error
Error state.
virtual void RegisterField(const char *field_name, GridFunction *gf)
Add a grid function to the collection and update the root file.
string to_padded_string(int i, int digits)
void DeleteAll()
Delete data owned by the DataCollection including field information.
int VectorDim() const
Definition: gridfunc.cpp:248
Helper class for VisIt visualization data.
virtual void Save()
Save the collection and a VisIt root file.
bool own_data
Should the collection delete its mesh and fields.
int main(int argc, char *argv[])
double time
Physical time (for time-dependent simulations)
int GetNRanks() const
Definition: pmesh.hpp:97
Mesh * mesh
The (common) mesh for the collected fields.
bool serial
Serial or parallel run? If false, append rank (myid) to file names.
void ParseVisItRootString(std::string json)
Read in a VisIt root file in JSON format.
GridFunction * GetField(const char *field_name)
void DeleteData()
Delete data owned by the DataCollection keeping field information.
virtual void SaveField(const char *field_name)
Save one field, assuming the collection directory already exists.
void Load(int _cycle=0)
Load the collection based on its VisIt data (described in its root file)
int Dimension() const
Definition: mesh.hpp:523
int SpaceDimension() const
Definition: mesh.hpp:524
virtual void Print(std::ostream &out=std::cout) const
Print the mesh to the given stream using the default MFEM mesh format.
Definition: mesh.cpp:6736
std::map< std::string, VisItFieldInfo > field_info_map
void LoadVisItRootFile(std::string root_name)
int GetMyRank() const
Definition: pmesh.hpp:98
MPI_Comm GetComm() const
Definition: pmesh.hpp:96
void SetPrefixPath(const char *prefix)
Set the path where the DataCollection will be saved.
void SetMaxLevelsOfDetail(int max_levels_of_detail)
Set VisIt parameter: maximum levels of detail for the MultiresControl.
virtual ~DataCollection()
Delete the mesh and fields if owned by the collection.
void SaveRootFile()
Save a VisIt root file for the collection.
VisItDataCollection(const char *collection_name)
virtual void RegisterField(const char *field_name, GridFunction *gf)
Add a grid function to the collection.
DataCollection(const char *collection_name)
Create an empty collection with the given name.
std::string prefix_path
A path where the directory with results is saved.
int pad_digits
Number of digits used for the cycle and MPI rank in filenames.
virtual void SetMesh(Mesh *new_mesh)
Set/change the mesh associated with the collection.
std::string name
Name of the collection, used as a directory name when saving.
virtual void SetMesh(Mesh *new_mesh)
Set/change the mesh associated with the collection.
void SaveOneField(const std::map< std::string, GridFunction * >::iterator &it)
Save one field to disk, assuming the collection directory exists.
string to_string(int i)
int num_procs
Number of MPI ranks (in parallel)
Class for parallel meshes.
Definition: pmesh.hpp:28
std::string GetVisItRootString()
Prepare the VisIt root file in JSON format for the current collection.