Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
state_manager.hpp
Go to the documentation of this file.
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Serac Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
13 #pragma once
14 
15 #include <optional>
16 #include <unordered_map>
17 #include <array>
18 #include <cstddef>
19 #include <cstdint>
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 #include "mfem.hpp"
25 #include "axom/sidre/core/MFEMSidreDataCollection.hpp"
33 #include "serac/numerics/functional/geometry.hpp"
34 
35 namespace serac {
36 
41 class StateManager {
42  public:
48  static void initialize(axom::sidre::DataStore& ds, const std::string& output_directory);
49 
55  static bool hasState(const std::string& name) { return named_states_.find(name) != named_states_.end(); }
56 
68  template <typename FunctionSpace>
69  static FiniteElementState newState(FunctionSpace space, const std::string& state_name, const std::string& mesh_tag)
70  {
71  SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first");
72  SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag));
73  SLIC_ERROR_ROOT_IF(hasState(state_name),
74  axom::fmt::format("StateManager already contains a state named '{}'", state_name));
75 
76  FiniteElementState state(mesh(mesh_tag), space, state_name);
77 
78  storeState(state);
79  return state;
80  }
81 
89  static FiniteElementState newState(const mfem::ParFiniteElementSpace& space, const std::string& state_name);
90 
96  static void storeState(FiniteElementState& state);
97 
105  template <typename T>
106  static void storeQuadratureData(const std::string& mesh_tag, std::shared_ptr<QuadratureData<T>> qdata)
107  {
108  SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first");
109  SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag),
110  axom::fmt::format("Serac's state manager does not have a mesh with given tag '{}'", mesh_tag));
111 
112  constexpr const char* qds_group_name = "quadraturedatas";
113 
114  // Get Sidre location for quadrature data inside data collection
115  auto& datacoll = datacolls_.at(mesh_tag);
116  axom::sidre::Group* bp_group = datacoll.GetBPGroup(); // mesh_datacoll
117  // For each geometry type, use i to get both type and name from matching arrays
118  for (std::size_t i = 0; i < detail::qdata_geometries.size(); ++i) {
119  auto geom_type = detail::qdata_geometries[i];
120 
121  // Check if geometry type has any data
122  if ((*qdata).data.find(geom_type) != (*qdata).data.end()) {
123  auto geom_name = detail::qdata_geometry_names[i];
124 
125  // Get axom::Array of states in map
126  auto states = (*qdata)[geom_type];
127 
128  // Get various size information
129  auto num_states = static_cast<axom::IndexType>(states.size());
130  SLIC_ERROR_ROOT_IF(num_states == 0, "Number of States should be more than 0 at this point.");
131  auto state_size = static_cast<axom::IndexType>(sizeof(*(states.begin())));
132  auto total_size = num_states * state_size;
133  // Sidre treats information as an array of uint8s
134  auto num_uint8s = total_size / static_cast<axom::IndexType>(sizeof(std::uint8_t));
135 
136  if (!is_restart_) {
137  axom::sidre::Group* qdatas_group = bp_group->createGroup(qds_group_name);
138 
139  // Create Sidre group, store basic information, and point Sidre at the array external to Sidre
140  // Note: Sidre will not own this data.
141  axom::sidre::Group* geom_group = qdatas_group->createGroup(std::string(geom_name));
142  geom_group->createViewScalar("num_states", num_states);
143  geom_group->createViewScalar("state_size", state_size);
144  geom_group->createViewScalar("total_size", total_size);
145 
146  // Tell Sidre where the external array is, how large it is (calculated above), and what is in it (faking
147  // uint8)
148  axom::sidre::View* states_view = geom_group->createView("states");
149  states_view->setExternalDataPtr(axom::sidre::UINT8_ID, num_uint8s, states.data());
150  } else {
151  // Get Sidre group of where the states were stored.
152  // Note: this data is not owned by Sidre and the array should have been created at this point but
153  // the previous data has not been loaded yet into the array.
154  SLIC_ERROR_ROOT_IF(!bp_group->hasGroup(qds_group_name),
155  axom::fmt::format("Loaded Sidre Datastore did not have group for Quadrature Datas"));
156  axom::sidre::Group* qdatas_group = bp_group->getGroup(qds_group_name);
157  SLIC_ERROR_ROOT_IF(
158  !qdatas_group->hasGroup(std::string(geom_name)),
159  axom::fmt::format("Loaded Sidre Datastore did not have group for Quadrature Data geometry type '{}'",
160  std::string(geom_name)));
161  axom::sidre::Group* geom_group = qdatas_group->getGroup(std::string(geom_name));
162 
163  // Verify size correctness
164  auto verify_size = [](axom::sidre::Group* group, int value, const std::string& view_name,
165  const std::string& err_msg) {
166  SLIC_ERROR_IF(
167  !group->hasView(view_name),
168  axom::fmt::format("Loaded Sidre Datastore does not have value '{}' for Quadrature Data.", view_name));
169  auto prev_value = group->getView(view_name)->getData<axom::IndexType>();
170  SLIC_ERROR_IF(value != prev_value, axom::fmt::format(err_msg, value, prev_value));
171  };
172  verify_size(geom_group, num_states, "num_states",
173  "Current number of Quadrature Data States '{}' does not match value in restart '{}'.");
174  verify_size(geom_group, state_size, "state_size",
175  "Current size of Quadrature Data State '{}' does not match value in restart '{}'.");
176  verify_size(geom_group, total_size, "total_size",
177  "Current total size of Quadrature Data States '{}' does not match value in restart '{}'.");
178 
179  // Tell Sidre where the external array is
180  SLIC_ERROR_ROOT_IF(!geom_group->hasView("states"),
181  "Loaded Quadrature Data geometry Sidre view did not have 'states'");
182  axom::sidre::View* states_view = geom_group->getView("states");
183  states_view->setExternalDataPtr(states.data());
184 
185  // TODO: swap this code for the one below after updating Axom
186  // Load this set of quadrature data only
187  // std::string group_name_to_load = axom::fmt::format("{0}/{1}", qds_group_name, geom_name);
188  // datacoll.LoadExternalData("", group_name_to_load);
189  }
190  }
191  }
192  if (is_restart_) {
193  // NOTE: This call will reload all external buffers from file stored in the DataStore
194  // TODO: This should be changed to load only the current material quadrature data after
195  // MFEMSidreDatacollection::LoadExternalData is enhanced to allow loading the
196  // external data piecemeal. After Axom PR#1555 goes in, uncomment the loadExternalData call
197  // above and remove this if block.
198  datacoll.LoadExternalData();
199  }
200  }
201 
213  template <typename T>
214  static std::shared_ptr<QuadratureData<T>> newQuadratureDataBuffer(const std::string& mesh_tag, const Domain& domain,
215  int order, int dim, T initial_state)
216  {
217  int Q = order + 1;
218 
219  std::array<uint32_t, mfem::Geometry::NUM_GEOMETRIES> elems = geometry_counts(domain);
220  std::array<uint32_t, mfem::Geometry::NUM_GEOMETRIES> qpts_per_elem{};
221 
222  std::vector<mfem::Geometry::Type> geometries;
223  if (dim == 2) {
224  geometries = {mfem::Geometry::TRIANGLE, mfem::Geometry::SQUARE};
225  } else {
226  geometries = {mfem::Geometry::TETRAHEDRON, mfem::Geometry::CUBE};
227  }
228 
229  for (auto geom : geometries) {
230  qpts_per_elem[size_t(geom)] = uint32_t(num_quadrature_points(geom, Q));
231  }
232 
233  auto qdata = std::make_shared<QuadratureData<T>>(elems, qpts_per_elem, initial_state);
234  storeQuadratureData<T>(mesh_tag, qdata);
235  return qdata;
236  }
237 
243  static bool hasDual(const std::string& name) { return named_duals_.find(name) != named_duals_.end(); }
244 
256  template <typename FunctionSpace>
257  static FiniteElementDual newDual(FunctionSpace space, const std::string& dual_name, const std::string& mesh_tag)
258  {
259  SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first");
260  SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag));
261  SLIC_ERROR_ROOT_IF(hasDual(dual_name),
262  axom::fmt::format("StateManager already contains a dual named '{}'", dual_name));
263 
264  auto dual = FiniteElementDual(mesh(mesh_tag), space, dual_name);
265 
266  storeDual(dual);
267  return dual;
268  }
276  static FiniteElementDual newDual(const mfem::ParFiniteElementSpace& space, const std::string& dual_name);
277 
283  static void storeDual(FiniteElementDual& dual);
284 
293  static void updateState(const FiniteElementState& state)
294  {
295  SLIC_ERROR_ROOT_IF(!hasState(state.name()),
296  axom::fmt::format("State manager does not contain state named '{}'", state.name()));
297 
298  state.fillGridFunction(*named_states_[state.name()]);
299  }
300 
309  static void updateDual(const FiniteElementDual& dual)
310  {
311  SLIC_ERROR_ROOT_IF(!hasDual(dual.name()),
312  axom::fmt::format("State manager does not contain dual named '{}'", dual.name()));
313 
314  dual.space().GetRestrictionMatrix()->MultTranspose(dual, *named_duals_[dual.name()]);
315  }
316 
323  static void save(const double t, const int cycle, const std::string& mesh_tag);
324 
331  static double load(const int cycle_to_load, const std::string& mesh_tag)
332  {
333  // FIXME: Assumes that if one DataCollection is going to be reloaded all DataCollections will be
334  is_restart_ = true;
335  return newDataCollection(mesh_tag, cycle_to_load);
336  }
337 
347  static void reset()
348  {
349  named_states_.clear();
350  named_duals_.clear();
351  shape_displacements_.clear();
352  shape_displacement_duals_.clear();
353  datacolls_.clear();
354  output_dir_.clear();
355  is_restart_ = false;
356  ds_ = nullptr;
357  };
358 
364  static bool hasMesh(const std::string& mesh_tag) { return datacolls_.find(mesh_tag) != datacolls_.end(); }
365 
372  static mfem::ParMesh& setMesh(std::unique_ptr<mfem::ParMesh> pmesh, const std::string& mesh_tag);
373 
379  static mfem::ParMesh& mesh(const std::string& mesh_tag);
380 
390  static FiniteElementState& shapeDisplacement(const std::string& mesh_tag);
391 
401  static FiniteElementDual& shapeDisplacementDual(const std::string& mesh_tag);
402 
409  static void loadCheckpointedStates(int cycle_to_load, std::vector<FiniteElementState*> states_to_load);
410 
420  static FiniteElementDual& shapeDisplacementSensitivity(const std::string& mesh_tag);
421 
430  static std::string collectionID(const mfem::ParMesh* pmesh);
431 
433  static bool isRestart() { return is_restart_; }
434 
443  static int cycle(std::string mesh_tag);
444 
453  static double time(std::string mesh_tag);
454 
455  private:
462  static double newDataCollection(const std::string& mesh_tag, const std::optional<int> cycle_to_load = {});
463 
469  static std::string getCollectionName(const std::string& mesh_tag) { return mesh_tag + "_datacoll"; }
470 
476  static void constructShapeFields(const std::string& mesh_tag);
477 
482  static std::unordered_map<std::string, axom::sidre::MFEMSidreDataCollection> datacolls_;
483 
485  static std::unordered_map<std::string, std::unique_ptr<FiniteElementState>> shape_displacements_;
487  static std::unordered_map<std::string, std::unique_ptr<FiniteElementDual>> shape_displacement_duals_;
488 
492  static bool is_restart_;
493 
495  static axom::sidre::DataStore* ds_;
497  static std::string output_dir_;
498 
500  static std::unordered_map<std::string, mfem::ParGridFunction*> named_states_;
502  static std::unordered_map<std::string, mfem::ParGridFunction*> named_duals_;
503 };
504 
506 void checkMesh(const mfem::ParMesh& pmesh, bool is_restart = false);
507 
508 } // namespace serac
Class for encapsulating the dual vector space of a finite element space (i.e. the space of linear for...
Class for encapsulating the critical MFEM components of a primal finite element field.
void fillGridFunction(mfem::ParGridFunction &grid_function) const
Fill a user-provided grid function based on the underlying true vector.
std::string name() const
Returns the name of the FEState (field)
Manages the lifetimes of FEState objects such that restarts are abstracted from physics modules.
static void loadCheckpointedStates(int cycle_to_load, std::vector< FiniteElementState * > states_to_load)
loads the finite element states from a previously checkpointed cycle
static bool hasDual(const std::string &name)
Checks if StateManager has a dual with the given name.
static FiniteElementDual & shapeDisplacementDual(const std::string &mesh_tag)
Get the shape displacement finite element dual.
static void initialize(axom::sidre::DataStore &ds, const std::string &output_directory)
Initializes the StateManager with a sidre DataStore (into which state will be written/read)
static void updateState(const FiniteElementState &state)
Updates the StateManager-owned grid function using the values from a given FiniteElementState.
static bool isRestart()
Returns true if data was loaded into a DataCollection.
static std::shared_ptr< QuadratureData< T > > newQuadratureDataBuffer(const std::string &mesh_tag, const Domain &domain, int order, int dim, T initial_state)
Create a shared ptr to a quadrature data buffer for the given material type.
static void updateDual(const FiniteElementDual &dual)
Updates the StateManager-owned grid function using the values from a given FiniteElementDual.
static void storeDual(FiniteElementDual &dual)
Store a pre-constructed finite element dual in the state manager.
static int cycle(std::string mesh_tag)
Get the current cycle (iteration number) from the underlying datacollection.
static bool hasState(const std::string &name)
Checks if StateManager has a state with the given name.
static double load(const int cycle_to_load, const std::string &mesh_tag)
Loads an existing DataCollection.
static void storeQuadratureData(const std::string &mesh_tag, std::shared_ptr< QuadratureData< T >> qdata)
Store a pre-constructed Quadrature Data in the state manager.
static double time(std::string mesh_tag)
Get the current simulation time from the underlying datacollection.
static bool hasMesh(const std::string &mesh_tag)
Checks if StateManager has a mesh with the given mesh_tag.
static FiniteElementState newState(FunctionSpace space, const std::string &state_name, const std::string &mesh_tag)
Factory method for creating a new FEState object.
static std::string collectionID(const mfem::ParMesh *pmesh)
Returns the datacollection ID for a given mesh.
static void reset()
Resets the underlying global datacollection object.
static FiniteElementDual newDual(FunctionSpace space, const std::string &dual_name, const std::string &mesh_tag)
Factory method for creating a new FEDual object.
static mfem::ParMesh & mesh(const std::string &mesh_tag)
Returns a non-owning reference to mesh held by StateManager.
static void storeState(FiniteElementState &state)
Store a pre-constructed finite element state in the state manager.
static void save(const double t, const int cycle, const std::string &mesh_tag)
Updates the Conduit Blueprint state in the datastore and saves to a file.
static FiniteElementState & shapeDisplacement(const std::string &mesh_tag)
Get the shape displacement finite element state.
static mfem::ParMesh & setMesh(std::unique_ptr< mfem::ParMesh > pmesh, const std::string &mesh_tag)
Gives ownership of mesh to StateManager.
static FiniteElementDual & shapeDisplacementSensitivity(const std::string &mesh_tag)
Get the shape displacement sensitivity finite element dual.
many of the functions in this file amount to extracting element indices from an mesh_t like
Defines common types and helper functions for using the residual and scalar_objective classes.
This file contains helper traits and enumerations for classifying finite elements.
This contains a class that represents the dual of a finite element vector space, i....
This file contains the declaration of structure that manages the MFEM objects that make up the state ...
This file contains the all the necessary functions and macros required for logging as well as a helpe...
Accelerator functionality.
Definition: serac.cpp:36
constexpr int num_quadrature_points(mfem::Geometry::Type g, int Q)
return the number of quadrature points in a Gauss-Legendre rule with parameter "Q"
Definition: geometry.hpp:31
std::array< uint32_t, mfem::Geometry::NUM_GEOMETRIES > geometry_counts(const Domain &domain)
count the number of elements of each geometry in a domain
Definition: domain.hpp:300
void checkMesh(const mfem::ParMesh &pmesh, bool is_restart)
Check that a mesh satisfies our required properties.
dual(double, T) -> dual< T >
class template argument deduction guide for type dual.
This file contains the declaration of the structures that manage quadrature point data.
a class for representing a geometric region that can be used for integration
Definition: domain.hpp:33
a small POD class for tracking function space metadata
A class for storing and access user-defined types at quadrature points.
Dual number struct (value plus gradient)
Definition: dual.hpp:28