Smith  0.1
Smith is an implicit thermal structural mechanics simulation code.
state_manager.hpp
Go to the documentation of this file.
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Smith 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 "smith/numerics/functional/geometry.hpp"
34 
35 namespace smith {
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_, "Smith's data store was not initialized - call StateManager::initialize first");
72  SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), std::format("Mesh tag '{}' not found in the data store", mesh_tag));
73  SLIC_ERROR_ROOT_IF(hasState(state_name),
74  std::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_, "Smith's data store was not initialized - call StateManager::initialize first");
109  SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag),
110  std::format("Smith'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  std::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  std::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  verifyQuadratureDataSize(
164  geom_group, num_states, "num_states",
165  "Current number of Quadrature Data States '{}' does not match value in restart '{}'.");
166  verifyQuadratureDataSize(geom_group, state_size, "state_size",
167  "Current size of Quadrature Data State '{}' does not match value in restart '{}'.");
168  verifyQuadratureDataSize(
169  geom_group, total_size, "total_size",
170  "Current total size of Quadrature Data States '{}' does not match value in restart '{}'.");
171 
172  // Tell Sidre where the external array is
173  SLIC_ERROR_ROOT_IF(!geom_group->hasView("states"),
174  "Loaded Quadrature Data geometry Sidre view did not have 'states'");
175  axom::sidre::View* states_view = geom_group->getView("states");
176  states_view->setExternalDataPtr(states.data());
177 
178  // TODO: swap this code for the one below after updating Axom
179  // Load this set of quadrature data only
180  // std::string group_name_to_load = std::format("{0}/{1}", qds_group_name, geom_name);
181  // datacoll.LoadExternalData("", group_name_to_load);
182  }
183  }
184  }
185  if (is_restart_) {
186  // NOTE: This call will reload all external buffers from file stored in the DataStore
187  // TODO: This should be changed to load only the current material quadrature data after
188  // MFEMSidreDatacollection::LoadExternalData is enhanced to allow loading the
189  // external data piecemeal. After Axom PR#1555 goes in, uncomment the loadExternalData call
190  // above and remove this if block.
191  datacoll.LoadExternalData();
192  }
193  }
194 
206  template <typename T>
207  static std::shared_ptr<QuadratureData<T>> newQuadratureDataBuffer(const std::string& mesh_tag, const Domain& domain,
208  int order, int dim, T initial_state)
209  {
210  int Q = order + 1;
211 
212  std::array<uint32_t, mfem::Geometry::NUM_GEOMETRIES> elems = geometry_counts(domain);
213  std::array<uint32_t, mfem::Geometry::NUM_GEOMETRIES> qpts_per_elem{};
214 
215  std::vector<mfem::Geometry::Type> geometries;
216  if (dim == 2) {
217  geometries = {mfem::Geometry::TRIANGLE, mfem::Geometry::SQUARE};
218  } else {
219  geometries = {mfem::Geometry::TETRAHEDRON, mfem::Geometry::CUBE};
220  }
221 
222  for (auto geom : geometries) {
223  qpts_per_elem[size_t(geom)] = uint32_t(num_quadrature_points(geom, Q));
224  }
225 
226  auto qdata = std::make_shared<QuadratureData<T>>(elems, qpts_per_elem, initial_state);
227  storeQuadratureData<T>(mesh_tag, qdata);
228  return qdata;
229  }
230 
236  static bool hasDual(const std::string& name) { return named_duals_.find(name) != named_duals_.end(); }
237 
249  template <typename FunctionSpace>
250  static FiniteElementDual newDual(FunctionSpace space, const std::string& dual_name, const std::string& mesh_tag)
251  {
252  SLIC_ERROR_ROOT_IF(!ds_, "Smith's data store was not initialized - call StateManager::initialize first");
253  SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), std::format("Mesh tag '{}' not found in the data store", mesh_tag));
254  SLIC_ERROR_ROOT_IF(hasDual(dual_name), std::format("StateManager already contains a dual named '{}'", dual_name));
255 
256  auto dual = FiniteElementDual(mesh(mesh_tag), space, dual_name);
257 
258  storeDual(dual);
259  return dual;
260  }
268  static FiniteElementDual newDual(const mfem::ParFiniteElementSpace& space, const std::string& dual_name);
269 
275  static void storeDual(FiniteElementDual& dual);
276 
285  static void updateState(const FiniteElementState& state)
286  {
287  SLIC_ERROR_ROOT_IF(!hasState(state.name()),
288  std::format("State manager does not contain state named '{}'", state.name()));
289 
290  state.fillGridFunction(*named_states_[state.name()]);
291  }
292 
301  static void updateDual(const FiniteElementDual& dual)
302  {
303  SLIC_ERROR_ROOT_IF(!hasDual(dual.name()),
304  std::format("State manager does not contain dual named '{}'", dual.name()));
305 
306  dual.space().GetRestrictionMatrix()->MultTranspose(dual, *named_duals_[dual.name()]);
307  }
308 
315  static void save(const double t, const int cycle, const std::string& mesh_tag);
316 
323  static double load(const int cycle_to_load, const std::string& mesh_tag)
324  {
325  // FIXME: Assumes that if one DataCollection is going to be reloaded all DataCollections will be
326  is_restart_ = true;
327  return newDataCollection(mesh_tag, cycle_to_load);
328  }
329 
339  static void reset()
340  {
341  named_states_.clear();
342  named_duals_.clear();
343  shape_displacements_.clear();
344  shape_displacement_duals_.clear();
345  datacolls_.clear();
346  output_dir_.clear();
347  is_restart_ = false;
348  ds_ = nullptr;
349  };
350 
356  static bool hasMesh(const std::string& mesh_tag) { return datacolls_.find(mesh_tag) != datacolls_.end(); }
357 
364  static mfem::ParMesh& setMesh(std::unique_ptr<mfem::ParMesh> pmesh, const std::string& mesh_tag);
365 
371  static mfem::ParMesh& mesh(const std::string& mesh_tag);
372 
382  static FiniteElementState& shapeDisplacement(const std::string& mesh_tag);
383 
393  static FiniteElementDual& shapeDisplacementDual(const std::string& mesh_tag);
394 
401  static void loadCheckpointedStates(int cycle_to_load, std::vector<FiniteElementState*> states_to_load);
402 
412  static FiniteElementDual& shapeDisplacementSensitivity(const std::string& mesh_tag);
413 
422  static std::string collectionID(const mfem::ParMesh* pmesh);
423 
425  static bool isRestart() { return is_restart_; }
426 
435  static int cycle(std::string mesh_tag);
436 
445  static double time(std::string mesh_tag);
446 
447  private:
456  static void verifyQuadratureDataSize(axom::sidre::Group* group, axom::IndexType value, const char* view_name,
457  const char* err_msg)
458  {
459  SLIC_ERROR_IF(!group->hasView(view_name),
460  std::format("Loaded Sidre Datastore does not have value '{}' for Quadrature Data.", view_name));
461  auto prev_value = group->getView(view_name)->getData<axom::IndexType>();
462  SLIC_ERROR_IF(value != prev_value, std::vformat(err_msg, std::make_format_args(value, prev_value)));
463  }
464 
471  static double newDataCollection(const std::string& mesh_tag, const std::optional<int> cycle_to_load = {});
472 
478  static std::string getCollectionName(const std::string& mesh_tag) { return mesh_tag + "_datacoll"; }
479 
485  static void constructShapeFields(const std::string& mesh_tag);
486 
491  static std::unordered_map<std::string, axom::sidre::MFEMSidreDataCollection> datacolls_;
492 
494  static std::unordered_map<std::string, std::unique_ptr<FiniteElementState>> shape_displacements_;
496  static std::unordered_map<std::string, std::unique_ptr<FiniteElementDual>> shape_displacement_duals_;
497 
501  static bool is_restart_;
502 
504  static axom::sidre::DataStore* ds_;
506  static std::string output_dir_;
507 
509  static std::unordered_map<std::string, mfem::ParGridFunction*> named_states_;
511  static std::unordered_map<std::string, mfem::ParGridFunction*> named_duals_;
512 };
513 
515 void checkMesh(const mfem::ParMesh& pmesh, bool is_restart = false);
516 
517 } // namespace smith
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 storeState(FiniteElementState &state)
Store a pre-constructed finite element state in the state manager.
static FiniteElementState & shapeDisplacement(const std::string &mesh_tag)
Get the shape displacement finite element state.
static FiniteElementDual & shapeDisplacementSensitivity(const std::string &mesh_tag)
Get the shape displacement sensitivity finite element dual.
static std::string collectionID(const mfem::ParMesh *pmesh)
Returns the datacollection ID for a given mesh.
static FiniteElementDual newDual(FunctionSpace space, const std::string &dual_name, const std::string &mesh_tag)
Factory method for creating a new FEDual object.
static void reset()
Resets the underlying global datacollection object.
static void updateDual(const FiniteElementDual &dual)
Updates the StateManager-owned grid function using the values from a given FiniteElementDual.
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 bool isRestart()
Returns true if data was loaded into a DataCollection.
static int cycle(std::string mesh_tag)
Get the current cycle (iteration number) from the underlying datacollection.
static bool hasMesh(const std::string &mesh_tag)
Checks if StateManager has a mesh with the given mesh_tag.
static void updateState(const FiniteElementState &state)
Updates the StateManager-owned grid function using the values from a given FiniteElementState.
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 mfem::ParMesh & setMesh(std::unique_ptr< mfem::ParMesh > pmesh, const std::string &mesh_tag)
Gives ownership of mesh to StateManager.
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 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 bool hasState(const std::string &name)
Checks if StateManager has a state with the given name.
static double time(std::string mesh_tag)
Get the current simulation time from the underlying datacollection.
static FiniteElementState newState(FunctionSpace space, const std::string &state_name, const std::string &mesh_tag)
Factory method for creating a new FEState object.
static double load(const int cycle_to_load, const std::string &mesh_tag)
Loads an existing DataCollection.
static mfem::ParMesh & mesh(const std::string &mesh_tag)
Returns a non-owning reference to mesh held by StateManager.
static void storeDual(FiniteElementDual &dual)
Store a pre-constructed finite element dual in the state manager.
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 loadCheckpointedStates(int cycle_to_load, std::vector< FiniteElementState * > states_to_load)
loads the finite element states from a previously checkpointed cycle
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: smith.cpp:36
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:312
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
dual(double, T) -> dual< T >
class template argument deduction guide for type dual.
void checkMesh(const mfem::ParMesh &pmesh, bool is_restart)
Check that a mesh satisfies our required properties.
mfem::ParFiniteElementSpace & space(FieldState field)
Get the space from the primal field of a field states.
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