diff --git a/src/serac/numerics/equation_solver.cpp b/src/serac/numerics/equation_solver.cpp index 1e4fae3a4..aa8159cb4 100644 --- a/src/serac/numerics/equation_solver.cpp +++ b/src/serac/numerics/equation_solver.cpp @@ -488,8 +488,8 @@ class TrustRegion : public mfem::NewtonSolver { TrustRegionResults trResults(X.Size()); TrustRegionSettings settings; settings.maxCgIterations = static_cast(linear_options.max_iterations); - settings.cgTol = 0.2 * norm_goal; - double trSize = 10.0; + settings.cgTol = 0.5 * norm_goal; + double trSize = 0.1 * std::sqrt(X.Size()); size_t cumulativeCgIters = 0; int it = 0; @@ -499,6 +499,8 @@ class TrustRegion : public mfem::NewtonSolver { mfem::out << "Newton iteration " << std::setw(3) << it << " : ||r|| = " << std::setw(13) << norm; if (it > 0) { mfem::out << ", ||r||/||r_0|| = " << std::setw(13) << (initial_norm != 0.0 ? norm / initial_norm : norm); + } else { + mfem::out << ", norm goal = " << std::setw(13) << norm_goal << "\n"; } mfem::out << '\n'; } @@ -554,7 +556,7 @@ class TrustRegion : public mfem::NewtonSolver { trResults.cgIterationsCount = 1; trResults.interiorStatus = TrustRegionResults::Status::OnBoundary; } else { - settings.cgTol = std::max(0.2 * norm_goal, 1e-3 * norm); + settings.cgTol = std::max(0.5 * norm_goal, 5e-5 * norm); solve_trust_region_minimization(r, scratch, hess_vec_func, precond_func, settings, trSize, trResults); } cumulativeCgIters += trResults.cgIterationsCount; diff --git a/src/serac/physics/solid_mechanics.hpp b/src/serac/physics/solid_mechanics.hpp index 428386033..41dd4aaa7 100644 --- a/src/serac/physics/solid_mechanics.hpp +++ b/src/serac/physics/solid_mechanics.hpp @@ -128,9 +128,8 @@ class SolidMechanics, std::integer_se * @param parameter_names A vector of the names of the requested parameter fields * @param cycle The simulation cycle (i.e. timestep iteration) to intialize the physics module to * @param time The simulation time to initialize the physics module to - * @param checkpoint_to_disk A flag to save the transient states on disk instead of memory for the transient adjoint - * solves - * @param use_warm_start A flag to turn on or off the displacement warm start predictor which helps robustness for + * @param checkpoint_to_disk Flag to save the transient states on disk instead of memory for transient adjoint solver + * @param use_warm_start Flag to turn on or off the displacement warm start predictor which helps robustness for * large deformation problems * * @note On parallel file systems (e.g. lustre), significant slowdowns and occasional errors were observed when @@ -158,7 +157,7 @@ class SolidMechanics, std::integer_se * @param parameter_names A vector of the names of the requested parameter fields * @param cycle The simulation cycle (i.e. timestep iteration) to intialize the physics module to * @param time The simulation time to initialize the physics module to - * @param checkpoint_to_disk A flag to save the transient states on disk instead of memory for the transient adjoint + * @param checkpoint_to_disk Flag to save the transient states on disk instead of memory for transient adjoint solves * @param use_warm_start A flag to turn on or off the displacement warm start predictor which helps robustness for * large deformation problems * @@ -249,13 +248,12 @@ class SolidMechanics, std::integer_se if (amg_prec) { // ZRA - Iterative refinement tends to be more expensive than it is worth // We should add a flag allowing users to enable it - if constexpr (serac::ordering == mfem::Ordering::byVDIM) { - bool iterative_refinement = false; - amg_prec->SetElasticityOptions(&displacement_.space(), iterative_refinement); - } else { - // SetElasticityOptions only works with byVDIM ordering, instead fallback to SetSystemsOptions - amg_prec->SetSystemsOptions(displacement_.space().GetVDim(), serac::ordering == mfem::Ordering::byNODES); - } + + // bool iterative_refinement = false; + // amg_prec->SetElasticityOptions(&displacement_.space(), iterative_refinement); + + // SetElasticityOptions only works with byVDIM ordering, some evidence that it is not often optimal + amg_prec->SetSystemsOptions(displacement_.space().GetVDim(), serac::ordering == mfem::Ordering::byNODES); } int true_size = velocity_.space().TrueVSize(); @@ -421,23 +419,7 @@ class SolidMechanics, std::integer_se template qdata_type createQuadratureDataBuffer(T initial_state) { - constexpr auto Q = order + 1; - - std::array elems = geometry_counts(mesh_); - std::array qpts_per_elem{}; - - std::vector geometries; - if (dim == 2) { - geometries = {mfem::Geometry::TRIANGLE, mfem::Geometry::SQUARE}; - } else { - geometries = {mfem::Geometry::TETRAHEDRON, mfem::Geometry::CUBE}; - } - - for (auto geom : geometries) { - qpts_per_elem[size_t(geom)] = uint32_t(num_quadrature_points(geom, Q)); - } - - return std::make_shared>(elems, qpts_per_elem, initial_state); + return StateManager::newQuadratureDataBuffer(mesh_tag_, order, dim, initial_state); } /** diff --git a/src/serac/physics/state/state_manager.cpp b/src/serac/physics/state/state_manager.cpp index dcaec3ee4..8a9d09f32 100644 --- a/src/serac/physics/state/state_manager.cpp +++ b/src/serac/physics/state/state_manager.cpp @@ -138,7 +138,7 @@ void StateManager::storeState(FiniteElementState& state) { SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first"); auto mesh_tag = collectionID(&state.mesh()); - SLIC_ERROR_ROOT_IF(named_states_.find(state.name()) != named_states_.end(), + SLIC_ERROR_ROOT_IF(hasState(state.name()), axom::fmt::format("StateManager already contains a state named '{}'", state.name())); auto& datacoll = datacolls_.at(mesh_tag); const std::string name = state.name(); @@ -163,9 +163,8 @@ FiniteElementState StateManager::newState(const mfem::ParFiniteElementSpace& spa std::string mesh_tag = collectionID(space.GetParMesh()); SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first"); - SLIC_ERROR_ROOT_IF(datacolls_.find(mesh_tag) == datacolls_.end(), - axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); - SLIC_ERROR_ROOT_IF(named_states_.find(state_name) != named_states_.end(), + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); + SLIC_ERROR_ROOT_IF(hasState(state_name), axom::fmt::format("StateManager already contains a state named '{}'", state_name)); auto state = FiniteElementState(space, state_name); storeState(state); @@ -176,7 +175,7 @@ void StateManager::storeDual(FiniteElementDual& dual) { SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first"); auto mesh_tag = collectionID(&dual.mesh()); - SLIC_ERROR_ROOT_IF(named_duals_.find(dual.name()) != named_duals_.end(), + SLIC_ERROR_ROOT_IF(hasDual(dual.name()), axom::fmt::format("StateManager already contains a state named '{}'", dual.name())); auto& datacoll = datacolls_.at(mesh_tag); const std::string name = dual.name(); @@ -203,9 +202,8 @@ FiniteElementDual StateManager::newDual(const mfem::ParFiniteElementSpace& space std::string mesh_tag = collectionID(space.GetParMesh()); SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first"); - SLIC_ERROR_ROOT_IF(datacolls_.find(mesh_tag) == datacolls_.end(), - axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); - SLIC_ERROR_ROOT_IF(named_duals_.find(dual_name) != named_duals_.end(), + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); + SLIC_ERROR_ROOT_IF(hasDual(dual_name), axom::fmt::format("StateManager already contains a dual named '{}'", dual_name)); auto dual = FiniteElementDual(space, dual_name); storeDual(dual); @@ -215,8 +213,7 @@ FiniteElementDual StateManager::newDual(const mfem::ParFiniteElementSpace& space void StateManager::save(const double t, const int cycle, const std::string& mesh_tag) { SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first"); - SLIC_ERROR_ROOT_IF(datacolls_.find(mesh_tag) == datacolls_.end(), - axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); auto& datacoll = datacolls_.at(mesh_tag); std::string file_path = axom::utilities::filesystem::joinPath(datacoll.GetPrefixPath(), datacoll.GetCollectionName()); SLIC_INFO_ROOT( @@ -295,8 +292,7 @@ void StateManager::constructShapeFields(const std::string& mesh_tag) mfem::ParMesh& StateManager::mesh(const std::string& mesh_tag) { - SLIC_ERROR_ROOT_IF(datacolls_.find(mesh_tag) == datacolls_.end(), - axom::fmt::format("Mesh tag \"{}\" not found in the data store", mesh_tag)); + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag \"{}\" not found in the data store", mesh_tag)); auto mesh = datacolls_.at(mesh_tag).GetMesh(); SLIC_ERROR_ROOT_IF(!mesh, "The datacollection does not contain a mesh object"); return static_cast(*mesh); @@ -315,15 +311,13 @@ std::string StateManager::collectionID(const mfem::ParMesh* pmesh) int StateManager::cycle(std::string mesh_tag) { - SLIC_ERROR_ROOT_IF(datacolls_.find(mesh_tag) == datacolls_.end(), - axom::fmt::format("Mesh tag \"{}\" not found in the data store", mesh_tag)); + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag \"{}\" not found in the data store", mesh_tag)); return datacolls_.at(mesh_tag).GetCycle(); } double StateManager::time(std::string mesh_tag) { - SLIC_ERROR_ROOT_IF(datacolls_.find(mesh_tag) == datacolls_.end(), - axom::fmt::format("Mesh tag \"{}\" not found in the data store", mesh_tag)); + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag \"{}\" not found in the data store", mesh_tag)); return datacolls_.at(mesh_tag).GetTime(); } diff --git a/src/serac/physics/state/state_manager.hpp b/src/serac/physics/state/state_manager.hpp index ee4a2d172..acf21d0aa 100644 --- a/src/serac/physics/state/state_manager.hpp +++ b/src/serac/physics/state/state_manager.hpp @@ -48,7 +48,14 @@ class StateManager { static void initialize(axom::sidre::DataStore& ds, const std::string& output_directory); /** - * @brief Factory method for creating a new FEState object\ + * @brief Checks if StateManager has a state with the given name + * @param[in] name A string that uniquely identifies the state + * @return True if state exists with the given name + */ + static bool hasState(const std::string& name) { return named_states_.find(name) != named_states_.end(); } + + /** + * @brief Factory method for creating a new FEState object * * @tparam FunctionSpace The function space (e.g. H1<1>) to build the finite element state on * @param space The function space (e.g. H1<1>) to build the finite element state on @@ -62,9 +69,8 @@ class StateManager { static FiniteElementState newState(FunctionSpace space, const std::string& state_name, const std::string& mesh_tag) { SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first"); - SLIC_ERROR_ROOT_IF(datacolls_.find(mesh_tag) == datacolls_.end(), - axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); - SLIC_ERROR_ROOT_IF(named_states_.find(state_name) != named_states_.end(), + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); + SLIC_ERROR_ROOT_IF(hasState(state_name), axom::fmt::format("StateManager already contains a state named '{}'", state_name)); auto state = FiniteElementState(mesh(mesh_tag), space, state_name); @@ -89,6 +95,48 @@ class StateManager { */ static void storeState(FiniteElementState& state); + /** + * @brief Create a shared ptr to a quadrature data buffer for the given material type + * + * @tparam T the type to be created at each quadrature point + * @param mesh_tag The tag for the stored mesh used to construct the finite element state + * @param order The order of the discretization of the displacement and velocity fields + * @param dim The spatial dimension of the mesh + * @param initial_state the value to be broadcast to each quadrature point + * @return shared pointer to quadrature data buffer + */ + template + static std::shared_ptr> newQuadratureDataBuffer(const std::string& mesh_tag, int order, int dim, + T initial_state) + { + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); + + int Q = order + 1; + + std::array elems = geometry_counts(mesh(mesh_tag)); + std::array qpts_per_elem{}; + + std::vector geometries; + if (dim == 2) { + geometries = {mfem::Geometry::TRIANGLE, mfem::Geometry::SQUARE}; + } else { + geometries = {mfem::Geometry::TETRAHEDRON, mfem::Geometry::CUBE}; + } + + for (auto geom : geometries) { + qpts_per_elem[size_t(geom)] = uint32_t(num_quadrature_points(geom, Q)); + } + + return std::make_shared>(elems, qpts_per_elem, initial_state); + } + + /** + * @brief Checks if StateManager has a dual with the given name + * @param name A string that uniquely identifies the name + * @return True if dual exists with the given name + */ + static bool hasDual(const std::string& name) { return named_duals_.find(name) != named_duals_.end(); } + /** * @brief Factory method for creating a new FEDual object * @@ -104,9 +152,8 @@ class StateManager { static FiniteElementDual newDual(FunctionSpace space, const std::string& dual_name, const std::string& mesh_tag) { SLIC_ERROR_ROOT_IF(!ds_, "Serac's data store was not initialized - call StateManager::initialize first"); - SLIC_ERROR_ROOT_IF(datacolls_.find(mesh_tag) == datacolls_.end(), - axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); - SLIC_ERROR_ROOT_IF(named_states_.find(dual_name) != named_duals_.end(), + SLIC_ERROR_ROOT_IF(!hasMesh(mesh_tag), axom::fmt::format("Mesh tag '{}' not found in the data store", mesh_tag)); + SLIC_ERROR_ROOT_IF(hasDual(dual_name), axom::fmt::format("StateManager already contains a dual named '{}'", dual_name)); auto dual = FiniteElementDual(mesh(mesh_tag), space, dual_name); @@ -140,7 +187,7 @@ class StateManager { */ static void updateState(const FiniteElementState& state) { - SLIC_ERROR_ROOT_IF(named_states_.find(state.name()) == named_states_.end(), + SLIC_ERROR_ROOT_IF(!hasState(state.name()), axom::fmt::format("State manager does not contain state named '{}'", state.name())); state.fillGridFunction(*named_states_[state.name()]); @@ -156,7 +203,7 @@ class StateManager { */ static void updateDual(const FiniteElementDual& dual) { - SLIC_ERROR_ROOT_IF(named_duals_.find(dual.name()) == named_duals_.end(), + SLIC_ERROR_ROOT_IF(!hasDual(dual.name()), axom::fmt::format("State manager does not contain dual named '{}'", dual.name())); dual.space().GetRestrictionMatrix()->MultTranspose(dual, *named_duals_[dual.name()]); @@ -203,6 +250,13 @@ class StateManager { ds_ = nullptr; }; + /** + * @brief Checks if StateManager has a mesh with the given mesh_tag + * @param[in] mesh_tag A string that uniquely identifies the mesh + * @return True if mesh exists with the given mesh_tag + */ + static bool hasMesh(const std::string& mesh_tag) { return datacolls_.find(mesh_tag) != datacolls_.end(); } + /** * @brief Gives ownership of mesh to StateManager * @param[in] pmesh The mesh to register diff --git a/src/serac/physics/tests/solid.cpp b/src/serac/physics/tests/solid.cpp index 864928724..8c321a7f7 100644 --- a/src/serac/physics/tests/solid.cpp +++ b/src/serac/physics/tests/solid.cpp @@ -74,9 +74,9 @@ void functional_solid_test_static_J2() Material::State initial_state{}; - auto state = solid_solver.createQuadratureDataBuffer(initial_state); + auto qdata = solid_solver.createQuadratureDataBuffer(initial_state); - solid_solver.setMaterial(mat, state); + solid_solver.setMaterial(mat, qdata); // prescribe zero displacement at the supported end of the beam, std::set support = {1};