18 #include <string_view>
19 #include <system_error>
20 #include <unordered_map>
28 template <
int order,
int dim,
typename parameters = Parameters<>,
29 typename parameter_indices = std::make_
integer_sequence<
int, parameters::n>>
41 template <
int order,
int dim,
typename... parameter_space,
int... parameter_indices>
43 std::integer_sequence<int, parameter_indices...>>
44 :
public SolidMechanics<order, dim, Parameters<parameter_space...>,
45 std::integer_sequence<int, parameter_indices...>> {
67 std::shared_ptr<smith::Mesh> smith_mesh, std::vector<std::string> parameter_names = {},
68 int cycle = 0,
double time = 0.0,
bool checkpoint_to_disk =
false,
bool use_warm_start =
true)
69 :
SolidMechanicsContact(std::make_unique<EquationSolver>(nonlinear_opts, lin_opts, smith_mesh->getComm()),
70 timestepping_opts, physics_name, smith_mesh, parameter_names, cycle, time,
71 checkpoint_to_disk, use_warm_start)
91 std::shared_ptr<smith::Mesh> smith_mesh, std::vector<std::string> parameter_names = {},
92 int cycle = 0,
double time = 0.0,
bool checkpoint_to_disk =
false,
bool use_warm_start =
true)
93 : SolidMechanicsBase(std::move(solver), timestepping_opts, physics_name, smith_mesh, parameter_names, cycle, time,
94 checkpoint_to_disk, use_warm_start),
96 forces_(
StateManager::newDual(displacement_.space(), detail::addPrefix(physics_name,
"contact_forces")))
99 duals_.push_back(&forces_);
112 std::shared_ptr<smith::Mesh> smith_mesh,
int cycle = 0,
double time = 0.0)
115 forces_(
StateManager::newDual(displacement_.
space(), detail::addPrefix(physics_name,
"contact_forces")))
118 duals_.push_back(&forces_);
124 SolidMechanicsBase::resetStates(cycle, time);
126 for (
auto& [_, force] : contact_interaction_forces_) {
131 for (
auto& [_, seed] : contact_interaction_force_adjoint_bcs_) {
138 mfem::Vector p(contact_.numPressureDofs());
146 auto residual_fn = [
this](
const mfem::Vector& u, mfem::Vector& r) {
147 const mfem::Vector u_blk(
const_cast<mfem::Vector&
>(u), 0, displacement_.Size());
149 *parameters_[parameter_indices].state...);
154 mfem::Vector r_blk(r, 0, displacement_.Size());
158 r_blk.SetSubVector(bcs_.allEssentialTrueDofs(), 0.0);
162 if (contact_.haveLagrangeMultipliers()) {
165 J_offsets_ = mfem::Array<int>({0, displacement_.Size(), displacement_.Size() + contact_.numPressureDofs()});
166 return std::make_unique<mfem_ext::StdFunctionOperator>(
167 displacement_.space().TrueVSize() + contact_.numPressureDofs(), residual_fn,
169 [
this](
const mfem::Vector& u) -> mfem::Operator& {
170 const mfem::Vector u_blk(const_cast<mfem::Vector&>(u), 0, displacement_.Size());
171 auto [r, drdu] = (*residual_)(time_, BasePhysics::shapeDisplacement(), differentiate_wrt(u_blk),
172 acceleration_, *parameters_[parameter_indices].state...);
175 J_constraint_ = contact_.jacobianFunction(assemble(drdu));
178 J_constraint_->owns_blocks = false;
179 J_ = std::unique_ptr<mfem::HypreParMatrix>(
180 static_cast<mfem::HypreParMatrix*>(&J_constraint_->GetBlock(0, 0)));
181 J_12_ = std::unique_ptr<mfem::HypreParMatrix>(
182 static_cast<mfem::HypreParMatrix*>(&J_constraint_->GetBlock(0, 1)));
183 J_21_ = std::unique_ptr<mfem::HypreParMatrix>(
184 static_cast<mfem::HypreParMatrix*>(&J_constraint_->GetBlock(1, 0)));
185 J_22_ = std::unique_ptr<mfem::HypreParMatrix>(
186 static_cast<mfem::HypreParMatrix*>(&J_constraint_->GetBlock(1, 1)));
189 J_e_ = bcs_.eliminateAllEssentialDofsFromMatrix(*J_);
190 J_e_21_ = std::unique_ptr<mfem::HypreParMatrix>(J_21_->EliminateCols(bcs_.allEssentialTrueDofs()));
191 J_12_->EliminateRows(bcs_.allEssentialTrueDofs());
194 J_constraint_e_ = std::make_unique<mfem::BlockOperator>(J_offsets_);
195 J_constraint_e_->SetBlock(0, 0, J_e_.get());
196 J_constraint_e_->SetBlock(1, 0, J_e_21_.get());
198 J_operator_ = J_constraint_.get();
199 return *J_constraint_;
204 return std::make_unique<mfem_ext::StdFunctionOperator>(
205 displacement_.space().TrueVSize(), residual_fn, [
this](
const mfem::Vector& u) -> mfem::Operator& {
206 auto [r, drdu] = (*residual_)(time_, BasePhysics::shapeDisplacement(), differentiate_wrt(u), acceleration_,
207 *parameters_[parameter_indices].state...);
210 auto block_J = contact_.jacobianFunction(assemble(drdu));
211 block_J->owns_blocks = false;
212 J_ = std::unique_ptr<mfem::HypreParMatrix>(static_cast<mfem::HypreParMatrix*>(&block_J->GetBlock(0, 0)));
214 J_e_ = bcs_.eliminateAllEssentialDofsFromMatrix(*J_);
216 J_operator_ = J_.get();
231 const std::set<int>& bdry_attr_surf2,
ContactOptions contact_opts)
233 SLIC_ERROR_ROOT_IF(!is_quasistatic_,
"Contact can only be applied to quasistatic problems.");
234 SLIC_ERROR_ROOT_IF(order > 1,
"Contact can only be applied to linear (order = 1) meshes.");
239 contact_interaction_forces_.find(interaction_id) != contact_interaction_forces_.end(),
240 std::format(
"Contact interaction id {} already exists for physics module '{}'.", interaction_id, name_));
243 contact_.addContactInteraction(interaction_id, bdry_attr_surf1, bdry_attr_surf2, contact_opts);
245 const auto interaction_force_name = detail::addPrefix(name_, std::format(
"contact_force_{}", interaction_id));
246 auto interaction_force_dual =
247 std::make_unique<FiniteElementDual>(
StateManager::newDual(displacement_.space(), interaction_force_name));
248 *interaction_force_dual = 0.0;
249 duals_.push_back(interaction_force_dual.get());
250 contact_interaction_forces_.emplace(interaction_id, std::move(interaction_force_dual));
252 const auto force_adjoint_bcs_name =
253 detail::addPrefix(name_, std::format(
"contact_force_adjoint_bcs_{}", interaction_id));
254 auto force_adjoint_bcs = std::make_unique<FiniteElementState>(displacement_.space(), force_adjoint_bcs_name);
255 *force_adjoint_bcs = 0.0;
256 this->dual_adjoints_.push_back(force_adjoint_bcs.get());
257 contact_interaction_force_adjoint_bcs_.emplace(interaction_id, std::move(force_adjoint_bcs));
263 auto dual_names = SolidMechanicsBase::dualNames();
264 dual_names.push_back(
"contact_forces");
265 #ifdef SMITH_USE_TRIBOL
266 for (
const auto& interaction : contact_.getContactInteractions()) {
267 dual_names.push_back(std::format(
"contact_force_{}", interaction.getInteractionId()));
276 if (dual_name ==
"contact_forces" || dual_name == detail::addPrefix(this->name_,
"contact_forces")) {
280 const auto interaction_id = parseContactInteractionForceId(dual_name);
281 if (interaction_id.has_value()) {
282 auto it = contact_interaction_forces_.find(*interaction_id);
283 if (it != contact_interaction_forces_.end()) {
294 const auto interaction_id = parseContactInteractionForceId(dual_name);
295 if (interaction_id.has_value()) {
296 auto it = contact_interaction_force_adjoint_bcs_.find(*interaction_id);
297 if (it != contact_interaction_force_adjoint_bcs_.end()) {
302 return SolidMechanicsBase::dualAdjoint(dual_name);
312 if (!contact_dof_prolongation_) {
313 contact_dof_prolongation_ = contact_.contactSubspaceTransferOperator();
325 mfem::Vector p = pressure();
328 SolidMechanicsBase::completeSetup();
336 mfem::HypreParVector
pressure()
const {
return contact_.mergedPressures(); }
338 #ifdef SMITH_USE_TRIBOL
345 const ContactInteraction& contactInteraction(
int interaction_id)
const
347 for (
const auto& interaction : contact_.getContactInteractions()) {
348 if (interaction.getInteractionId() == interaction_id) {
352 SLIC_ERROR_ROOT(std::format(
"No contact interaction found with interaction_id={}", interaction_id));
353 return contact_.getContactInteractions().front();
358 void setDualAdjointBcs(std::unordered_map<std::string, const smith::FiniteElementState&> bcs)
override
360 SLIC_ERROR_ROOT_IF(bcs.size() == 0,
"Adjoint load container size must be greater than 0 in SolidMechanicsContact.");
362 auto reaction_adjoint_load = bcs.find(
"reactions");
363 if (reaction_adjoint_load != bcs.end()) {
364 SolidMechanicsBase::setDualAdjointBcs({{
"reactions", reaction_adjoint_load->second}});
367 for (
const auto& [name, bc] : bcs) {
368 if (name ==
"reactions") {
372 const auto interaction_id = parseContactInteractionForceId(name);
373 SLIC_ERROR_ROOT_IF(!interaction_id.has_value(),
374 std::format(
"Unknown dual adjoint BC '{}' for SolidMechanicsContact.", name));
376 auto it = contact_interaction_force_adjoint_bcs_.find(*interaction_id);
377 SLIC_ERROR_ROOT_IF(it == contact_interaction_force_adjoint_bcs_.end(),
378 std::format(
"No contact force adjoint BC registered for interaction_id={}", *interaction_id));
388 constexpr std::string_view prefix =
"contact_force_";
391 const auto idx = dual_name.rfind(prefix);
392 if (idx == std::string_view::npos) {
397 const std::string_view id_str = dual_name.substr(idx + prefix.size());
398 int interaction_id = -1;
399 const auto* begin = id_str.data();
400 const auto* end = id_str.data() + id_str.size();
401 auto [ptr, ec] = std::from_chars(begin, end, interaction_id);
402 if (ec != std::errc{} || ptr != end) {
405 return interaction_id;
414 warmStartDisplacementContact(dt);
421 mfem::Vector augmented_solution(displacement_.Size() + contact_.numPressureDofs());
422 augmented_solution.SetVector(displacement_, 0);
423 augmented_solution.SetVector(contact_.mergedPressures(), displacement_.Size());
426 nonlin_solver_->solve(augmented_solution);
427 displacement_.Set(1.0, mfem::Vector(augmented_solution, 0, displacement_.Size()));
428 forces_.SetVector(contact_.forces(), 0);
430 #ifdef SMITH_USE_TRIBOL
431 for (
const auto& interaction : contact_.getContactInteractions()) {
432 auto it = contact_interaction_forces_.find(interaction.getInteractionId());
433 if (it != contact_interaction_forces_.end()) {
434 it->second->SetVector(interaction.forces(), 0);
478 for (
auto& bc : bcs_.essentials()) {
480 bc.setDofs(du_, time_ + dt);
483 auto& constrained_dofs = bcs_.allEssentialTrueDofs();
484 for (
int i = 0; i < constrained_dofs.Size(); i++) {
485 int j = constrained_dofs[i];
486 du_[j] -= displacement_(j);
489 auto amgf_prec =
dynamic_cast<mfem::AMGFSolver*
>(&nonlin_solver_->preconditioner());
492 computeContactSubspaceTransferOperator();
494 amgf_prec->SetFilteredSubspaceTransferOperator(*(contact_dof_prolongation_.get()));
497 int filter_solver_print_level = 0;
499 std::make_unique<StrumpackSolver>(filter_solver_print_level, contact_dof_prolongation_->GetComm());
500 amgf_prec->SetFilteredSubspaceSolver(*filter_solver_.get());
502 auto& lin_solver = nonlin_solver_->linearSolver();
503 auto iterative_solver =
dynamic_cast<mfem::IterativeSolver*
>(&lin_solver);
504 SLIC_WARNING_ROOT_IF(!iterative_solver,
505 "AMGFContact should only be used as a preconditioner for an iterative solver");
508 if (use_warm_start_) {
510 mfem::Vector augmented_residual(displacement_.Size() + contact_.numPressureDofs());
511 augmented_residual = 0.0;
513 *parameters_[parameter_indices].state...);
515 mfem::Vector augmented_solution(displacement_.space().TrueVSize() + contact_.numPressureDofs());
516 augmented_solution = 0.0;
517 mfem::Vector du(augmented_solution, 0, displacement_.space().TrueVSize());
519 mfem::Vector p_blk(augmented_solution, displacement_.Size(), contact_.numPressureDofs());
525 mfem::Vector r_blk(augmented_residual, 0, displacement_.space().TrueVSize());
527 r_blk += contact_.forces();
529 mfem::Vector g_blk(augmented_residual, displacement_.Size(), contact_.numPressureDofs());
530 g_blk.Set(1.0, contact_.mergedGaps(
true));
532 r_blk.SetSubVector(bcs_.allEssentialTrueDofs(), 0.0);
536 acceleration_, *parameters_[parameter_indices].previous_state...);
538 if (contact_.haveLagrangeMultipliers()) {
539 J_offsets_ = mfem::Array<int>({0, displacement_.Size(), displacement_.Size() + contact_.numPressureDofs()});
540 J_constraint_ = contact_.jacobianFunction(assemble(drdu));
543 J_constraint_->owns_blocks =
false;
544 J_ = std::unique_ptr<mfem::HypreParMatrix>(
static_cast<mfem::HypreParMatrix*
>(&J_constraint_->GetBlock(0, 0)));
546 std::unique_ptr<mfem::HypreParMatrix>(
static_cast<mfem::HypreParMatrix*
>(&J_constraint_->GetBlock(0, 1)));
548 std::unique_ptr<mfem::HypreParMatrix>(
static_cast<mfem::HypreParMatrix*
>(&J_constraint_->GetBlock(1, 0)));
550 std::unique_ptr<mfem::HypreParMatrix>(
static_cast<mfem::HypreParMatrix*
>(&J_constraint_->GetBlock(1, 1)));
552 J_e_ = bcs_.eliminateAllEssentialDofsFromMatrix(*J_);
553 J_e_21_ = std::unique_ptr<mfem::HypreParMatrix>(J_21_->EliminateCols(bcs_.allEssentialTrueDofs()));
554 J_12_->EliminateRows(bcs_.allEssentialTrueDofs());
556 J_operator_ = J_constraint_.get();
559 auto block_J = contact_.jacobianFunction(assemble(drdu));
560 block_J->owns_blocks =
false;
561 J_ = std::unique_ptr<mfem::HypreParMatrix>(
static_cast<mfem::HypreParMatrix*
>(&block_J->GetBlock(0, 0)));
563 J_e_ = bcs_.eliminateAllEssentialDofsFromMatrix(*J_);
565 J_operator_ = J_.get();
568 augmented_residual *= -1.0;
571 mfem::EliminateBC(*J_, *J_e_, constrained_dofs, du, r_blk);
572 for (
int i = 0; i < constrained_dofs.Size(); i++) {
573 int j = constrained_dofs[i];
577 auto& lin_solver = nonlin_solver_->linearSolver();
578 lin_solver.SetOperator(*J_operator_);
579 lin_solver.Mult(augmented_residual, augmented_solution);
584 displacement_ += du_;
590 SLIC_ERROR_ROOT_IF(contact_.haveLagrangeMultipliers(),
591 "Lagrange multiplier contact does not currently support sensitivities/adjoints.");
594 acceleration_, *parameters_[parameter_indices].state...);
596 auto block_J = contact_.jacobianFunction(assemble(drdu));
597 block_J->owns_blocks =
false;
598 auto jacobian = std::unique_ptr<mfem::HypreParMatrix>(
static_cast<mfem::HypreParMatrix*
>(&block_J->GetBlock(0, 0)));
599 auto J_T = std::unique_ptr<mfem::HypreParMatrix>(jacobian->Transpose());
606 #ifdef SMITH_USE_TRIBOL
607 if (!contact_interaction_force_adjoint_bcs_.empty()) {
608 FiniteElementDual contact_force_load(displacement_.space(),
"contact_force_dual_adjoint_load");
609 contact_force_load = 0.0;
611 for (
const auto& [interaction_id, force_seed] : contact_interaction_force_adjoint_bcs_) {
617 if (force_seed->Norml2() == 0.0) {
621 const auto interaction_J = contactInteraction(interaction_id).jacobianContribution();
622 auto* J00 =
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(0, 0));
623 SLIC_ERROR_ROOT_IF(!J00,
"Expected HypreParMatrix (0,0) block for contact interaction Jacobian.");
625 FiniteElementDual tmp(displacement_.space(),
"contact_force_dual_adjoint_load_tmp");
627 J00->MultTranspose(*force_seed, tmp);
628 contact_force_load.Add(1.0, tmp);
631 displacement_adjoint_load_.Add(-1.0, contact_force_load);
635 auto J_e_T = bcs_.eliminateAllEssentialDofsFromMatrix(*J_T);
636 auto& constrained_dofs = bcs_.allEssentialTrueDofs();
638 mfem::EliminateBC(*J_T, *J_e_T, constrained_dofs, reactions_adjoint_bcs_, displacement_adjoint_load_);
639 for (
int i = 0; i < constrained_dofs.Size(); i++) {
640 int j = constrained_dofs[i];
641 displacement_adjoint_load_[j] = reactions_adjoint_bcs_[j];
644 auto& lin_solver = nonlin_solver_->linearSolver();
645 lin_solver.SetOperator(*J_T);
646 lin_solver.Mult(displacement_adjoint_load_, adjoint_displacement_);
654 displacement_, acceleration_, *parameters_[parameter_indices].state...));
656 auto block_J = contact_.jacobianFunction(assemble(drdshape));
657 block_J->owns_blocks =
false;
659 std::unique_ptr<mfem::HypreParMatrix>(
static_cast<mfem::HypreParMatrix*
>(&block_J->GetBlock(0, 0)));
661 drdshape_mat->MultTranspose(adjoint_displacement_, shape_displacement_dual_);
675 using SolidMechanicsBase::acceleration_;
676 using SolidMechanicsBase::adjoint_displacement_;
677 using SolidMechanicsBase::d_residual_d_;
678 using SolidMechanicsBase::DERIVATIVE;
679 using SolidMechanicsBase::displacement_;
680 using SolidMechanicsBase::displacement_adjoint_load_;
681 using SolidMechanicsBase::du_;
682 using SolidMechanicsBase::J_;
683 using SolidMechanicsBase::J_e_;
684 using SolidMechanicsBase::nonlin_solver_;
685 using SolidMechanicsBase::reactions_adjoint_bcs_;
686 using SolidMechanicsBase::residual_;
687 using SolidMechanicsBase::residual_with_bcs_;
688 using SolidMechanicsBase::shape_displacement_dual_;
689 using SolidMechanicsBase::time_end_step_;
690 using SolidMechanicsBase::use_warm_start_;
691 using SolidMechanicsBase::warmStartDisplacement;
697 std::unique_ptr<mfem::HypreParMatrix>
J_21_;
700 std::unique_ptr<mfem::HypreParMatrix>
J_12_;
703 std::unique_ptr<mfem::HypreParMatrix>
J_22_;
712 std::unique_ptr<mfem::HypreParMatrix>
J_e_21_;
This is the abstract base class for a generic forward solver.
std::string name_
Name of the physics module.
std::shared_ptr< smith::Mesh > mesh_
The primary mesh.
std::vector< const smith::FiniteElementState * > states_
List of finite element primal states associated with this physics module.
int cycle_
Current cycle (forward pass time iteration count)
std::vector< const smith::FiniteElementDual * > duals_
List of finite element duals associated with this physics module.
std::vector< ParameterInfo > parameters_
A vector of the parameters associated with this physics module.
virtual const FiniteElementState & shapeDisplacement() const
Accessor for getting the shape displacement field from the physics modules.
double time_
Current time for the forward pass.
BoundaryConditionManager bcs_
Boundary condition manager instance.
bool is_quasistatic_
Whether the simulation is time-independent.
const FiniteElementDual & shapeDisplacementSensitivity() const
Internally used accessor for getting the shape displacement sensitivity from the physics modules.
const mfem::ParMesh & mfemParMesh() const
Returns a reference to the mfem ParMesh object.
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.
The nonlinear solid solver class.
Manages the lifetimes of FEState objects such that restarts are abstracted from physics modules.
static FiniteElementDual newDual(FunctionSpace space, const std::string &dual_name, const std::string &mesh_tag)
Factory method for creating a new FEDual object.
Accelerator functionality.
dual(double, T) -> dual< T >
class template argument deduction guide for type dual.
auto differentiate_wrt(const mfem::Vector &v)
this function is intended to only be used in combination with smith::Functional::operator(),...
mfem::ParFiniteElementSpace & space(FieldState field)
Get the space from the primal field of a field states.
#define SMITH_MARK_FUNCTION
Tools for tagging a set of components of a vector field for boundary condition enforcement.
Parameters for an iterative linear solution scheme.
Nonlinear solution scheme parameters.
a struct that is used in the physics modules to clarify which template arguments are user-controlled ...
A timestep and boundary condition enforcement method for a dynamic solver.