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  owned_states_.clear();
344  owned_duals_.clear();
345  shape_displacements_.clear();
346  shape_displacement_duals_.clear();
347  datacolls_.clear();
348  meshes_.clear();
349  output_dir_.clear();
350  is_restart_ = false;
351  ds_ = nullptr;
352  };
353 
359  static bool hasMesh(const std::string& mesh_tag) { return datacolls_.find(mesh_tag) != datacolls_.end(); }
360 
367  static mfem::ParMesh& setMesh(std::shared_ptr<mfem::ParMesh> pmesh, const std::string& mesh_tag);
368 
375  static mfem::ParMesh& setMesh(std::unique_ptr<mfem::ParMesh> pmesh, const std::string& mesh_tag);
376 
389  static mfem::ParMesh& mesh(const std::string& mesh_tag);
390 
400  static FiniteElementState& shapeDisplacement(const std::string& mesh_tag);
401 
411  static FiniteElementDual& shapeDisplacementDual(const std::string& mesh_tag);
412 
419  static void loadCheckpointedStates(int cycle_to_load, std::vector<FiniteElementState*> states_to_load);
420 
430  static FiniteElementDual& shapeDisplacementSensitivity(const std::string& mesh_tag);
431 
440  static std::string collectionID(const mfem::ParMesh* pmesh);
441 
443  static bool isRestart() { return is_restart_; }
444 
453  static int cycle(std::string mesh_tag);
454 
463  static double time(std::string mesh_tag);
464 
465  private:
474  static void verifyQuadratureDataSize(axom::sidre::Group* group, axom::IndexType value, const char* view_name,
475  const char* err_msg)
476  {
477  SLIC_ERROR_IF(!group->hasView(view_name),
478  std::format("Loaded Sidre Datastore does not have value '{}' for Quadrature Data.", view_name));
479  auto prev_value = group->getView(view_name)->getData<axom::IndexType>();
480  SLIC_ERROR_IF(value != prev_value, std::vformat(err_msg, std::make_format_args(value, prev_value)));
481  }
482 
489  static double newDataCollection(const std::string& mesh_tag, const std::optional<int> cycle_to_load = {});
490 
496  static std::string getCollectionName(const std::string& mesh_tag) { return mesh_tag + "_datacoll"; }
497 
503  static void constructShapeFields(const std::string& mesh_tag);
504 
509  static std::unordered_map<std::string, axom::sidre::MFEMSidreDataCollection> datacolls_;
514  static std::unordered_map<std::string, std::shared_ptr<mfem::ParMesh>> meshes_;
515 
517  static std::unordered_map<std::string, std::unique_ptr<FiniteElementState>> shape_displacements_;
519  static std::unordered_map<std::string, std::unique_ptr<FiniteElementDual>> shape_displacement_duals_;
520 
524  static bool is_restart_;
525 
527  static axom::sidre::DataStore* ds_;
529  static std::string output_dir_;
530 
532  static std::unordered_map<std::string, mfem::ParGridFunction*> named_states_;
534  static std::unordered_map<std::string, mfem::ParGridFunction*> named_duals_;
537  static std::vector<std::unique_ptr<mfem::ParGridFunction>> owned_states_;
540  static std::vector<std::unique_ptr<mfem::ParGridFunction>> owned_duals_;
541 };
542 
544 void checkMesh(const mfem::ParMesh& pmesh, bool is_restart = false);
545 
546 } // 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::shared_ptr< mfem::ParMesh > pmesh, const std::string &mesh_tag)
Shares ownership of mesh with 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 the registered mesh.
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