13 #include "axom/core.hpp"
15 #include "smith/smith_config.hpp"
22 std::unordered_map<std::string, axom::sidre::MFEMSidreDataCollection> StateManager::datacolls_;
23 std::unordered_map<std::string, std::unique_ptr<FiniteElementState>> StateManager::shape_displacements_;
24 std::unordered_map<std::string, std::unique_ptr<FiniteElementDual>> StateManager::shape_displacement_duals_;
25 bool StateManager::is_restart_ =
false;
26 axom::sidre::DataStore* StateManager::ds_ =
nullptr;
27 std::string StateManager::output_dir_ =
"";
28 std::unordered_map<std::string, mfem::ParGridFunction*> StateManager::named_states_;
29 std::unordered_map<std::string, mfem::ParGridFunction*> StateManager::named_duals_;
31 double StateManager::newDataCollection(
const std::string& mesh_tag,
const std::optional<int> cycle_to_load)
33 SLIC_ERROR_ROOT_IF(!ds_,
"Cannot construct a DataCollection without a DataStore");
34 std::string coll_name = getCollectionName(mesh_tag);
36 auto global_grp = ds_->getRoot()->createGroup(coll_name +
"_global");
37 auto bp_index_grp = global_grp->createGroup(
"blueprint_index/" + coll_name);
38 auto domain_grp = ds_->getRoot()->createGroup(coll_name);
41 constexpr
bool owns_mesh_data =
true;
42 auto [iter, _] = datacolls_.emplace(std::piecewise_construct, std::forward_as_tuple(mesh_tag),
43 std::forward_as_tuple(coll_name, bp_index_grp, domain_grp, owns_mesh_data));
44 auto& datacoll = iter->second;
45 datacoll.SetComm(MPI_COMM_WORLD);
47 datacoll.SetPrefixPath(output_dir_);
51 datacoll.Load(*cycle_to_load);
52 datacoll.SetGroupPointers(ds_->getRoot()->getGroup(coll_name +
"_global/blueprint_index/" + coll_name),
53 ds_->getRoot()->getGroup(coll_name));
54 SLIC_ERROR_ROOT_IF(datacoll.GetBPGroup()->getNumGroups() == 0,
55 "Loaded datastore is empty, was the datastore created on a "
56 "different number of nodes?");
58 datacoll.UpdateStateFromDS();
59 datacoll.UpdateMeshAndFieldsFromDS();
64 mesh(mesh_tag).ExchangeFaceNbrData();
69 constructShapeFields(mesh_tag);
73 datacoll.SetTime(0.0);
76 return datacoll.GetTime();
82 const mfem::ParMesh* meshPtr = &(*states_to_load.begin())->
mesh();
85 std::string coll_name = getCollectionName(mesh_tag);
87 axom::sidre::MFEMSidreDataCollection previous_datacoll(coll_name);
89 previous_datacoll.SetComm(meshPtr->GetComm());
90 previous_datacoll.SetPrefixPath(output_dir_);
91 previous_datacoll.Load(cycle_to_load);
93 for (
auto state : states_to_load) {
94 meshPtr = &state->mesh();
96 "Loading FiniteElementStates from two different meshes at one time is not allowed.");
97 mfem::ParGridFunction* datacoll_owned_grid_function = previous_datacoll.GetParField(state->name());
99 state->setFromGridFunction(*datacoll_owned_grid_function);
110 output_dir_ = output_directory;
111 if (output_directory.empty()) {
113 "DataCollection output directory cannot be empty - this will result in problems if executables are run in "
120 SLIC_ERROR_ROOT_IF(shape_displacement_duals_.count(mesh_tag) == 0,
121 axom::fmt::format(
"No shape displacement dual exists on mesh named '{}'", mesh_tag));
122 return *shape_displacement_duals_[mesh_tag];
127 SLIC_ERROR_ROOT_IF(shape_displacements_.count(mesh_tag) == 0,
128 axom::fmt::format(
"No shape displacement exists on mesh named '{}'", mesh_tag));
129 return *shape_displacements_[mesh_tag];
134 SLIC_ERROR_ROOT_IF(!ds_,
"Smith's data store was not initialized - call StateManager::initialize first");
137 axom::fmt::format(
"StateManager already contains a state named '{}'", state.
name()));
138 auto& datacoll = datacolls_.at(mesh_tag);
139 const std::string name = state.
name();
140 mfem::ParGridFunction* grid_function;
142 grid_function = datacoll.GetParField(name);
145 SLIC_ERROR_ROOT_IF(datacoll.HasField(name),
146 axom::fmt::format(
"StateManager already given a field named '{0}'", name));
149 grid_function =
new mfem::ParGridFunction(&state.
space(),
static_cast<double*
>(
nullptr));
150 datacoll.RegisterField(name, grid_function);
153 named_states_[name] = grid_function;
158 std::string mesh_tag =
collectionID(space.GetParMesh());
160 SLIC_ERROR_ROOT_IF(!ds_,
"Smith's data store was not initialized - call StateManager::initialize first");
161 SLIC_ERROR_ROOT_IF(!
hasMesh(mesh_tag), axom::fmt::format(
"Mesh tag '{}' not found in the data store", mesh_tag));
162 SLIC_ERROR_ROOT_IF(
hasState(state_name),
163 axom::fmt::format(
"StateManager already contains a state named '{}'", state_name));
171 SLIC_ERROR_ROOT_IF(!ds_,
"Smith's data store was not initialized - call StateManager::initialize first");
174 axom::fmt::format(
"StateManager already contains a state named '{}'",
dual.name()));
175 auto& datacoll = datacolls_.at(mesh_tag);
176 const std::string name =
dual.name();
177 mfem::ParGridFunction* grid_function;
179 grid_function = datacoll.GetParField(name);
180 std::unique_ptr<mfem::HypreParVector> true_dofs(grid_function->GetTrueDofs());
183 SLIC_ERROR_ROOT_IF(datacoll.HasField(name),
184 axom::fmt::format(
"StateManager already given a field named '{0}'", name));
187 grid_function =
new mfem::ParGridFunction(&
dual.space(),
static_cast<double*
>(
nullptr));
188 datacoll.RegisterField(name, grid_function);
189 grid_function->SetFromTrueDofs(
dual);
191 named_duals_[name] = grid_function;
196 std::string mesh_tag =
collectionID(space.GetParMesh());
198 SLIC_ERROR_ROOT_IF(!ds_,
"Smith's data store was not initialized - call StateManager::initialize first");
199 SLIC_ERROR_ROOT_IF(!
hasMesh(mesh_tag), axom::fmt::format(
"Mesh tag '{}' not found in the data store", mesh_tag));
200 SLIC_ERROR_ROOT_IF(
hasDual(dual_name),
201 axom::fmt::format(
"StateManager already contains a dual named '{}'", dual_name));
210 SLIC_ERROR_ROOT_IF(!ds_,
"Smith's data store was not initialized - call StateManager::initialize first");
211 SLIC_ERROR_ROOT_IF(!
hasMesh(mesh_tag), axom::fmt::format(
"Mesh tag '{}' not found in the data store", mesh_tag));
212 auto& datacoll = datacolls_.at(mesh_tag);
213 std::string file_path = axom::utilities::filesystem::joinPath(datacoll.GetPrefixPath(), datacoll.GetCollectionName());
215 axom::fmt::format(
"Saving data collection at time: '{}' and cycle: '{}' to path: '{}'", t,
cycle, file_path));
218 datacoll.SetCycle(
cycle);
227 pmesh->SetNodesOwner(
false);
229 newDataCollection(mesh_tag);
230 auto& datacoll = datacolls_.at(mesh_tag);
231 datacoll.SetMesh(pmesh.release());
232 datacoll.SetOwnData(
true);
234 auto& new_pmesh =
mesh(mesh_tag);
240 constructShapeFields(mesh_tag);
245 void StateManager::constructShapeFields(
const std::string& mesh_tag)
248 auto& new_mesh =
mesh(mesh_tag);
250 int dim = new_mesh.Dimension();
253 shape_displacements_[mesh_tag] =
254 std::make_unique<FiniteElementState>(new_mesh,
SHAPE_DIM_2, mesh_tag +
"_shape_displacement");
255 }
else if (dim == 3) {
256 shape_displacements_[mesh_tag] =
257 std::make_unique<FiniteElementState>(new_mesh,
SHAPE_DIM_3, mesh_tag +
"_shape_displacement");
259 SLIC_ERROR_ROOT(axom::fmt::format(
"Mesh of dimension {} given, only dimensions 2 or 3 are available in Smith.",
260 new_mesh.Dimension()));
265 *shape_displacements_[mesh_tag] = 0.0;
268 shape_displacement_duals_[mesh_tag] =
269 std::make_unique<FiniteElementDual>(new_mesh,
SHAPE_DIM_2, mesh_tag +
"_shape_displacement_dual");
271 shape_displacement_duals_[mesh_tag] =
272 std::make_unique<FiniteElementDual>(new_mesh,
SHAPE_DIM_3, mesh_tag +
"_shape_displacement_dual");
275 storeDual(*shape_displacement_duals_[mesh_tag]);
280 SLIC_ERROR_ROOT_IF(!
hasMesh(mesh_tag), axom::fmt::format(
"Mesh tag \"{}\" not found in the data store", mesh_tag));
281 auto mesh = datacolls_.at(mesh_tag).GetMesh();
282 SLIC_ERROR_ROOT_IF(!
mesh,
"The datacollection does not contain a mesh object");
283 return static_cast<mfem::ParMesh&
>(*mesh);
288 for (
auto& [name, datacoll] : datacolls_) {
289 if (datacoll.GetMesh() == pmesh) {
293 SLIC_ERROR_ROOT(
"The mesh has not been registered with StateManager");
299 SLIC_ERROR_ROOT_IF(!
hasMesh(mesh_tag), axom::fmt::format(
"Mesh tag \"{}\" not found in the data store", mesh_tag));
300 return datacolls_.at(mesh_tag).GetCycle();
305 SLIC_ERROR_ROOT_IF(!
hasMesh(mesh_tag), axom::fmt::format(
"Mesh tag \"{}\" not found in the data store", mesh_tag));
306 return datacolls_.at(mesh_tag).GetTime();
309 void checkMesh(
const mfem::ParMesh& pmesh,
bool is_restart)
311 const mfem::GridFunction* nodes = pmesh.GetNodes();
313 SLIC_ERROR_ROOT_IF(!nodes,
314 "The mesh must have a grid function for the nodes defined. Call the EnsureNodes() method on "
315 "your mesh before setting it with the state manager.");
318 SLIC_ERROR_ROOT_IF(!pmesh.OwnsNodes(),
319 "The mesh must own its node grid function, as ownership will be passed to the state manager.");
322 SLIC_WARNING_ROOT_IF(nodes->FESpace()->FEColl()->GetContType() == mfem::FiniteElementCollection::DISCONTINUOUS,
323 "Periodic mesh detected! This will only work on translational periodic surfaces for vector H1 "
324 "fields and has not been thoroughly tested. Proceed at your own risk.");
326 const std::string ordering_string = smith::ordering == mfem::Ordering::byNODES ?
"byNODES" :
"byVDIM";
327 SLIC_ERROR_ROOT_IF(nodes->FESpace()->GetOrdering() != smith::ordering,
328 "The dof ordering of the mesh coordinates grid function must be the same as the global setting "
329 "in smith::ordering. The Smith ordering is currently " +
333 SLIC_ERROR_ROOT_IF(!pmesh.have_face_nbr_data,
334 "The mesh must have face neighbor data defined. Smith mesh building tools should ensure this "
335 "automatically. If you built your mesh with native MFEM tools, make sure to call the "
336 "ExchangeFaceNbrData() before setting it with the state manager.");
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.
void setFromGridFunction(const mfem::ParGridFunction &grid_function)
Initialize the true vector in the FiniteElementState based on an input grid function.
mfem::ParFiniteElementSpace & space()
Returns a non-owning reference to the internal FESpace.
std::string name() const
Returns the name of the FEState (field)
mfem::ParMesh & mesh()
Returns a non-owning reference to the internal mesh object.
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 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 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 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 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 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 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
This file contains the declaration of structure that manages vectors derived from an MFEM finite elem...
Accelerator functionality.
constexpr H1< SHAPE_ORDER, 3 > SHAPE_DIM_3
Function space for shape displacement on dimension 2 meshes.
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.
constexpr H1< SHAPE_ORDER, 2 > SHAPE_DIM_2
Function space for shape displacement on dimension 2 meshes.
Various helper functions and macros for profiling using Caliper.
#define SMITH_MARK_FUNCTION
This file contains the declaration of the StateManager class.
Dual number struct (value plus gradient)