MFEM  v3.3
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
sidredatacollection.hpp
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 #ifndef MFEM_SIDREDATACOLLECTION
13 #define MFEM_SIDREDATACOLLECTION
14 
15 #include "../config/config.hpp"
16 
17 #ifdef MFEM_USE_SIDRE
18 
19 #include "datacollection.hpp"
20 #include <sidre/sidre.hpp>
21 
22 namespace mfem
23 {
24 
25 /** @brief Data collection with Sidre routines following the Conduit mesh
26  blueprint specification. */
27 /** SidreDataCollection provides an HDF5-based file format for visualization or
28  restart capability. This functionality is aimed primarily at customers of
29  LLNL's ASC Toolkit that run problems at extreme scales.
30 
31  For more information, see:
32  - LLNL ASC toolkit Sidre component (to be open-sourced), http://goo.gl/cZyJdn
33  - LLNL conduit/blueprint library, https://github.com/LLNL/conduit
34  - HDF5 library, https://support.hdfgroup.org/HDF5
35 
36  The layout created in the Sidre DataStore is: (`"──"` denote groups,
37  `"─•"` denote views, `"─>"` denote links, i.e. shallow-copy view)
38 
39  <root>
40  ├── <collection-name>_global (global group)
41  │ └── blueprint_index
42  │ └── <collection-name> (bp_index group)
43  │ ├── state
44  │ │ ├─• cycle
45  │ │ ├─• time
46  │ │ └─• number_of_domains = <mesh-mpi-comm-size>
47  │ ├── coordsets
48  │ │ └── coords
49  │ │ ├─• path = "<bp-path>/coordsets/coords"
50  │ │ ├─• type ─> <bp-grp>/coordsets/coords/type = "explicit"
51  │ │ └─• coord_system = "x"|"xy"|"xyz"
52  │ ├── topologies
53  │ │ ├── mesh
54  │ │ │ ├─• path = "<bp-path>/topologies/mesh"
55  │ │ │ ├─• type ─> <bp-grp>/topologies/mesh/type = "unstructured"
56  │ │ │ ├─• coordset ─> <bp-grp>/topologies/mesh/coordset = "coords"
57  │ │ │ ├─• grid_function ─> <bp-grp>/topologies/mesh/grid_function = "<nodes-field-name>"
58  │ │ │ └─• boundary_topology ─> <bp-grp>/topologies/mesh/boundary_topology = "boundary"
59  │ │ └── boundary
60  │ │ ├─• path = "<bp-path>/topologies/mesh"
61  │ │ ├─• type ─> <bp-grp>/topologies/boundary/type = "unstructured"
62  │ │ └─• coordset ─> <bp-grp>/topologies/boundary/coordset = "coords"
63  │ └── fields
64  │ ├── mesh_material_attribute
65  │ │ ├─• path = "<bp-path>/fields/mesh_material_attribute"
66  │ │ ├─• association ─> <bp-grp>/fields/mesh_material_attribute/association = "element"
67  │ │ ├─• topology ─> <bp-grp>/fields/mesh_material_attribute/topology = "mesh"
68  │ │ └─• number_of_components = 1
69  │ ├── boundary_material_attribute
70  │ │ ├─• path = "<bp-path>/fields/boundary_material_attribute"
71  │ │ ├─• association ─> <bp-grp>/fields/boundary_material_attribute/association = "element"
72  │ │ ├─• topology ─> <bp-grp>/fields/boundary_material_attribute/topology = "boundary"
73  │ │ └─• number_of_components = 1
74  │ ├── grid-function-1
75  │ │ ├─• path = "<bp-path>/fields/grid-function-1"
76  │ │ ├─• basis ─> <bp-grp>/fields/grid-function-1/basis = "<fe-coll-name>"
77  │ │ ├─• topology ─> <bp-grp>/fields/grid-function-1/topology = "mesh"
78  │ │ └─• number_of_components = gf1->VectorDim()
79  │ ├── grid-function-2
80  │ │ ├─• path = "<bp-path>/fields/grid-function-2"
81  │ │ ├─• basis ─> <bp-grp>/fields/grid-function-2/basis = "<fe-coll-name>"
82  │ │ ├─• topology ─> <bp-grp>/fields/grid-function-2/topology = "mesh"
83  │ │ └─• number_of_components = gf2->VectorDim()
84  │ ├── ...
85  │ ...
86  └── <collection-name> (domain group)
87  ├── blueprint (blueprint group)
88  │ ├── state
89  │ │ ├─• cycle
90  │ │ ├─• time
91  │ │ ├─• domain = <mesh-mpi-rank>
92  │ │ └─• time_step
93  │ ├── coordsets
94  │ │ └── coords
95  │ │ ├─• type = "explicit"
96  │ │ └── values
97  │ │ ├─• x = view in <vertex-coords-buffer>/<ext-double-data>
98  │ │ ├─• y = view in <vertex-coords-buffer>/<ext-double-data>
99  │ │ └─• z = view in <vertex-coords-buffer>/<ext-double-data>
100  │ ├── topologies
101  │ │ ├── mesh
102  │ │ │ ├─• type = "unstructured"
103  │ │ │ ├── elements
104  │ │ │ │ ├─• shape = "points"|"lines"|...
105  │ │ │ │ └─• connectivity = <vert-idx-array>
106  │ │ │ ├─• coordset = "coords"
107  │ │ │ ├─• grid_function = "<nodes-field-name>"
108  │ │ │ └─• boundary_topology = "boundary"
109  │ │ └── boundary
110  │ │ ├─• type = "unstructured"
111  │ │ ├── elements
112  │ │ │ ├─• shape = "points"|"lines"|...
113  │ │ │ └─• connectivity = <vert-idx-array>
114  │ │ └─• coordset = "coords"
115  │ └── fields
116  │ ├── mesh_material_attribute
117  │ │ ├─• association = "element"
118  │ │ ├─• topology = "mesh"
119  │ │ └─• values = <attr-array>
120  │ ├── boundary_material_attribute
121  │ │ ├─• association = "element"
122  │ │ ├─• topology = "boundary"
123  │ │ └─• values = <attr-array>
124  │ ├── grid-function-1 (name can include path)
125  │ │ ├─• basis = "<fe-coll-name>"
126  │ │ ├─• topology = "mesh"
127  │ │ └─• values = <ext-double-array>/<named-buffer> (vdim == 1)
128  │ ├── grid-function-2 (name can include path)
129  │ │ ├─• basis = "<fe-coll-name>"
130  │ │ ├─• topology = "mesh"
131  │ │ └── values (vdim > 1)
132  │ │ ├─• x0 = view into <ext-double-array>/<named-buffer>
133  │ │ ├─• x1 = view into <ext-double-array>/<named-buffer>
134  │ │ └─• x2 = view into <ext-double-array>/<named-buffer>
135  │ ├── ...
136  │ ...
137  └── named_buffers (named_buffers group)
138  ├─• vertex_coords = <double-array>
139  ├─• grid-function-1 = <double-array>
140  ├─• grid-function-2 = <double-array>
141  ...
142 
143  @note blueprint_index is used both in serial and in parallel. In parallel,
144  only rank 0 will add entries to the blueprint index.
145 
146  @note QuadratureFunction%s (q-fields) are not supported.
147 
148  @note SidreDataCollection does not manage the FiniteElementSpace%s and
149  FiniteElementCollection%s associated with registered GridFunction%s.
150  Therefore, field registration is left to the user of SidreDataCollection and
151  there are no methods that automatically register GridFunction%s using just
152  the content of the Sidre DataStore. Such capabilities can be implemented in
153  a derived class, adding any desired object management routines.
154 
155  @warning This class is still _experimental_, meaning that in future
156  releases, it may not be backward compatible, and the output files generated
157  by the current version may become unreadable.
158 */
160 {
161 public:
162 
163  /// Constructor that allocates and initializes a Sidre DataStore.
164  /**
165  @param[in] collection_name Name of the collection used as a file name
166  when saving
167  @param[in] the_mesh Mesh shared by all grid functions in the
168  collection (can be NULL)
169  @param[in] owns_mesh_data Does the SidreDC own the mesh vertices?
170 
171  With this constructor, the SidreDataCollection owns the allocated Sidre
172  DataStore.
173  */
174  SidreDataCollection(const std::string& collection_name,
175  Mesh *the_mesh = NULL,
176  bool owns_mesh_data = false);
177 
178  /// Constructor that links to an external Sidre DataStore.
179  /** Specifically, the global and domain groups can be at arbitrary paths.
180 
181  @param[in] collection_name Name of the collection used as a file name
182  when saving
183  @param[in] global_grp Pointer to the global group in the datastore,
184  see the above schematic
185  @param[in] domain_grp Pointer to the domain group in the datastore,
186  see the above schematic
187  @param[in] owns_mesh_data Does the SidreDC own the mesh vertices?
188 
189  With this constructor, the SidreDataCollection does not own the Sidre
190  DataStore.
191  @note No mesh or fields are read from the given DataGroups. The mesh has
192  to be set with SetMesh() and fields registered with RegisterField().
193  */
194  SidreDataCollection(const std::string& collection_name,
195  asctoolkit::sidre::DataGroup * global_grp,
196  asctoolkit::sidre::DataGroup * domain_grp,
197  bool owns_mesh_data = false);
198 
199 #ifdef MFEM_USE_MPI
200  /// Associate an MPI communicator with the collection.
201  /** If no mesh was associated with the collection, this method should be
202  called before using any of the Load() methods to read parallel data. */
203  void SetComm(MPI_Comm comm);
204 #endif
205 
206  /// Register a GridFunction in the Sidre DataStore.
207  /** This method is a shortcut for the call
208  `RegisterField(field_name, gf, field_name, 0)`.
209  */
210  virtual void RegisterField(const std::string &field_name, GridFunction *gf)
211  {
212  RegisterField(field_name, gf, field_name, 0);
213  }
214 
215  /// Register a GridFunction in the Sidre DataStore.
216  /** The registration procedure is as follows:
217  - if (@a gf's data is NULL), allocate named buffer with the name
218  @a buffer_name with size _offset + gf->FESpace()->GetVSize()_ and use
219  its data (plus the given @a offset) to set @a gf's data;
220  - else, if (DataStore has a named buffer @a buffer_name), replace @a gf's
221  data array with that named buffer plus the given @a offset;
222  - else, use @a gf's data as external data associated with @a field_name
223  in the DataStore;
224  - register @a field_name in #field_map.
225 
226  Both the @a field_name and @a buffer_name can contain a path prefix.
227  @note If @a field_name or @a buffer_name is empty, the method does
228  nothing.
229  @note If the GridFunction pointer @a gf or it's FiniteElementSpace
230  pointer are NULL, the method does nothing.
231  */
232  void RegisterField(const std::string &field_name, GridFunction *gf,
233  const std::string &buffer_name,
234  asctoolkit::sidre::SidreLength offset);
235 
236  /// Set the name of the mesh nodes field.
237  /** This name will be used by SetMesh() to register the mesh nodes, if not
238  already registered. Also, this method should be called if the mesh nodes
239  GridFunction was or will be registered directly by the user. The default
240  value for the name is "mesh_nodes". */
241  void SetMeshNodesName(const std::string &nodes_name)
242  {
243  if (!nodes_name.empty()) { m_meshNodesGFName = nodes_name; }
244  }
245 
246  /// De-register @a field_name from the SidreDataCollection.
247  /** The field is removed from the #field_map and the DataStore, including
248  deleting it from the named_buffers group, if allocated. */
249  virtual void DeregisterField(const std::string& field_name);
250 
251  /// Delete all owned data.
252  virtual ~SidreDataCollection();
253 
254  /// Set/change the mesh associated with the collection
255  /** Uses the field name "mesh_nodes" or the value set by SetMeshNodesName()
256  to register the mesh nodes GridFunction, if the mesh uses nodes. */
257  virtual void SetMesh(Mesh *new_mesh);
258 
259  /// Reset the domain and global datastore group pointers.
260  /** These are set in the constructor, but if a host code changes the
261  datastore contents ( such as wiping out the datastore and loading in new
262  contents from a file, i.e. a restart ) these pointers will need to be
263  reset to valid groups in the datastore.
264  @sa Load(const std::string &path, const std::string &protocol).
265  */
266  void SetGroupPointers(asctoolkit::sidre::DataGroup * global_grp,
267  asctoolkit::sidre::DataGroup * domain_grp);
268 
269  asctoolkit::sidre::DataGroup * GetBPGroup() { return bp_grp; }
270  asctoolkit::sidre::DataGroup * GetBPIndexGroup() { return bp_index_grp; }
271 
272  /// Prepare the DataStore for writing
273  virtual void PrepareToSave();
274 
275  /// Save the collection to file.
276  /** This method calls `Save(collection_name, "sidre_hdf5")`. */
277  virtual void Save();
278 
279  /// Save the collection to @a filename.
280  /** The collection path prefix is prepended to the @a filename and the
281  current cycle is appended, if cycle >= 0. */
282  void Save(const std::string& filename, const std::string& protocol);
283 
284  /// Load the Sidre DataStore from file.
285  /** No mesh or fields are read from the loaded DataStore.
286 
287  If the data collection created the datastore, it knows the layout of
288  where the domain and global groups are, and can restore them after the
289  Load().
290 
291  If, however, the data collection does not own the datastore (e.g. it did
292  not create the datastore), the host code must reset these pointers after
293  the load operation, using SetGroupPointers(), and also reset the state
294  variables, using UpdateStateFromDS().
295  */
296  void Load(const std::string& path, const std::string& protocol);
297 
298  /// Load SidreDataCollection from file.
299  /** The used file path is based on the current prefix path, collection name,
300  and the given @a cycle_. The protocol is "sidre_hdf5".
301  @sa Load(const std::string &path, const std::string &protocol).
302  */
303  virtual void Load(int cycle_ = 0)
304  {
305  SetCycle(cycle_);
306  Load(get_file_path(name), "sidre_hdf5");
307  }
308 
309  /// Load external data after registering externally owned fields.
310  void LoadExternalData(const std::string& path);
311 
312  /** @brief Updates the DataCollection's cycle, time, and time-step variables
313  with the values from the data store. */
314  void UpdateStateFromDS();
315 
316  /** @brief Updates the data store's cycle, time, and time-step variables with
317  the values from the SidreDataCollection. */
318  void UpdateStateToDS();
319 
320  /** @name Methods for named buffer access and manipulation. */
321  ///@{
322 
323  /** @brief Get a pointer to the sidre::DataView holding the named buffer for
324  @a buffer_name. */
325  /** If such named buffer is not allocated, the method returns NULL.
326  @note To access the underlying pointer, use DataView::getData().
327  @note To query the size of the buffer, use DataView::getNumElements().
328  */
329  asctoolkit::sidre::DataView *
330  GetNamedBuffer(const std::string& buffer_name) const
331  { return named_buffers_grp()->getView(buffer_name); }
332 
333  /// Return newly allocated or existing named buffer for @a buffer_name.
334  /** The buffer is stored in the named_buffers group. If the currently
335  allocated buffer size is smaller than @a sz, then the buffer is
336  reallocated with size @a sz, destroying its contents.
337  @note To access the underlying pointer, use DataView::getData().
338  */
339  asctoolkit::sidre::DataView *
340  AllocNamedBuffer(const std::string& buffer_name,
341  asctoolkit::sidre::SidreLength sz,
342  asctoolkit::sidre::TypeID type =
343  asctoolkit::sidre::DOUBLE_ID);
344 
345  /// Deallocate the named buffer @a buffer_name.
346  void FreeNamedBuffer(const std::string& buffer_name)
347  { named_buffers_grp()->destroyViewAndData(buffer_name); }
348 
349  ///@}
350 
351 private:
352  // Used if the Sidre data collection is providing the datastore itself.
353  const bool m_owns_datastore;
354 
355  // TODO - Need to evaluate if this bool member can be combined with own_data
356  // in parent data collection class. m_owns_mesh_data indicates whether the
357  // Sidre dc owns the mesh element data and node positions gf. The DC base
358  // class own_data indicates if the dc owns the mesh object pointer itself and
359  // GF objects. Can we use one flag and just have DC own all objects vs none?
360  const bool m_owns_mesh_data;
361 
362  // Name to be used for registering the mesh nodes in the SidreDataCollection.
363  // This name is used by SetMesh() and can be overwritten by the method
364  // SetMeshNodesName().
365  // Default value: "mesh_nodes".
366  std::string m_meshNodesGFName;
367 
368  // If the data collection owns the datastore, it will store a pointer to it.
369  // Otherwise, this pointer is NULL.
370  asctoolkit::sidre::DataStore * m_datastore_ptr;
371 
372 #ifdef MFEM_USE_MPI
373  MPI_Comm m_comm;
374 #endif
375 
376 protected:
377  asctoolkit::sidre::DataGroup *named_buffers_grp() const;
378 
379  asctoolkit::sidre::DataView *
380  alloc_view(asctoolkit::sidre::DataGroup *grp,
381  const std::string &view_name);
382 
383  asctoolkit::sidre::DataView *
384  alloc_view(asctoolkit::sidre::DataGroup *grp,
385  const std::string &view_name,
386  const asctoolkit::sidre::DataType &dtype);
387 
388  asctoolkit::sidre::DataGroup *
389  alloc_group(asctoolkit::sidre::DataGroup *grp,
390  const std::string &group_name);
391 
392  // return the filename based on prefix_path, collection name and cycle.
393  std::string get_file_path(const std::string &filename) const;
394 
395 private:
396  // If the data collection does not own the datastore, it will need pointers
397  // to the blueprint and blueprint index group to use.
398  asctoolkit::sidre::DataGroup * bp_grp;
399  asctoolkit::sidre::DataGroup * bp_index_grp;
400 
401  // This is stored for convenience.
402  asctoolkit::sidre::DataGroup * named_bufs_grp;
403 
404  // Private helper functions
405 
406  void RegisterFieldInBPIndex(const std::string& field_name,
407  GridFunction *gf);
408  void DeregisterFieldInBPIndex(const std::string & field_name);
409 
410  /** @brief Return a string with the conduit blueprint name for the given
411  Element::Type. */
412  std::string getElementName( Element::Type elementEnum );
413 
414  /**
415  * \brief A private helper function to set up the views associated with the
416  data of a scalar valued grid function in the blueprint style.
417  * \pre gf is not null
418  * \note This function is expected to be called by RegisterField()
419  * \note Handles cases where hierarchy is already set up,
420  * where the data was allocated by this data collection
421  * and where the gridfunction data is external to Sidre
422  */
423  void addScalarBasedGridFunction(const std::string& field_name,
424  GridFunction* gf,
425  const std::string &buffer_name,
426  asctoolkit::sidre::SidreLength offset);
427 
428  /**
429  * \brief A private helper function to set up the views associated with the
430  data of a vector valued grid function in the blueprint style.
431  * \pre gf is not null
432  * \note This function is expected to be called by RegisterField()
433  * \note Handles cases where hierarchy is already set up,
434  * where the data was allocated by this data collection
435  * and where the gridfunction data is external to Sidre
436  */
437  void addVectorBasedGridFunction(const std::string& field_name,
438  GridFunction* gf,
439  const std::string &buffer_name,
440  asctoolkit::sidre::SidreLength offset);
441 
442  /// Sets up the four main mesh blueprint groups.
443  /**
444  * \param hasBP Indicates whether the blueprint has already been set up.
445  */
446  void createMeshBlueprintStubs(bool hasBP);
447 
448  /// Sets up the mesh blueprint 'state' group.
449  /**
450  * \param hasBP Indicates whether the blueprint has already been set up.
451  */
452  void createMeshBlueprintState(bool hasBP);
453 
454  /// Sets up the mesh blueprint 'coordsets' group.
455  /**
456  * \param hasBP Indicates whether the blueprint has already been set up.
457  */
458  void createMeshBlueprintCoordset(bool hasBP);
459 
460  /// Sets up the mesh blueprint 'topologies' group.
461  /**
462  * This method is called from SetMesh().
463  * \param hasBP Indicates whether the blueprint has already been set up.
464  * \param mesh_name The name of the topology.
465  * \note Valid values for @a mesh_name are "mesh" and "boundary" and the
466  former has to be created with this method before the latter.
467  */
468  void createMeshBlueprintTopologies(bool hasBP, const std::string& mesh_name);
469 
470  /// Verifies that the contents of the mesh blueprint data is valid.
471  void verifyMeshBlueprint();
472 };
473 
474 } // end namespace mfem
475 
476 #endif
477 
478 #endif
asctoolkit::sidre::DataView * AllocNamedBuffer(const std::string &buffer_name, asctoolkit::sidre::SidreLength sz, asctoolkit::sidre::TypeID type=asctoolkit::sidre::DOUBLE_ID)
Return newly allocated or existing named buffer for buffer_name.
asctoolkit::sidre::DataGroup * alloc_group(asctoolkit::sidre::DataGroup *grp, const std::string &group_name)
Class for grid function - Vector with associated FE space.
Definition: gridfunc.hpp:27
void Load(const std::string &path, const std::string &protocol)
Load the Sidre DataStore from file.
void SetCycle(int c)
Set time cycle (for time-dependent simulations)
virtual void SetMesh(Mesh *new_mesh)
Set/change the mesh associated with the collection.
asctoolkit::sidre::DataGroup * GetBPIndexGroup()
virtual void RegisterField(const std::string &field_name, GridFunction *gf)
Register a GridFunction in the Sidre DataStore.
asctoolkit::sidre::DataView * GetNamedBuffer(const std::string &buffer_name) const
Get a pointer to the sidre::DataView holding the named buffer for buffer_name.
asctoolkit::sidre::DataView * alloc_view(asctoolkit::sidre::DataGroup *grp, const std::string &view_name)
SidreDataCollection(const std::string &collection_name, Mesh *the_mesh=NULL, bool owns_mesh_data=false)
Constructor that allocates and initializes a Sidre DataStore.
void LoadExternalData(const std::string &path)
Load external data after registering externally owned fields.
virtual void DeregisterField(const std::string &field_name)
De-register field_name from the SidreDataCollection.
asctoolkit::sidre::DataGroup * named_buffers_grp() const
asctoolkit::sidre::DataGroup * GetBPGroup()
Data collection with Sidre routines following the Conduit mesh blueprint specification.
void SetMeshNodesName(const std::string &nodes_name)
Set the name of the mesh nodes field.
Type
Constants for the classes derived from Element.
Definition: element.hpp:37
void SetGroupPointers(asctoolkit::sidre::DataGroup *global_grp, asctoolkit::sidre::DataGroup *domain_grp)
Reset the domain and global datastore group pointers.
void UpdateStateToDS()
Updates the data store&#39;s cycle, time, and time-step variables with the values from the SidreDataColle...
void UpdateStateFromDS()
Updates the DataCollection&#39;s cycle, time, and time-step variables with the values from the data store...
virtual ~SidreDataCollection()
Delete all owned data.
std::string get_file_path(const std::string &filename) const
virtual void PrepareToSave()
Prepare the DataStore for writing.
virtual void Save()
Save the collection to file.
void FreeNamedBuffer(const std::string &buffer_name)
Deallocate the named buffer buffer_name.
void SetComm(MPI_Comm comm)
Associate an MPI communicator with the collection.
std::string name
Name of the collection, used as a directory name when saving.
virtual void Load(int cycle_=0)
Load SidreDataCollection from file.