13 #include "axom/slic.hpp"
18 #ifdef SMITH_USE_TRIBOL
19 #include "tribol/interface/tribol.hpp"
20 #include "tribol/interface/mfem_tribol.hpp"
21 #include "tribol/mesh/CouplingScheme.hpp"
26 #ifdef SMITH_USE_TRIBOL
30 reference_nodes_{static_cast<const mfem::ParGridFunction*>(mesh.GetNodes())},
31 current_coords_{*reference_nodes_},
32 have_lagrange_multipliers_{false},
33 num_pressure_dofs_{0},
34 offsets_up_to_date_{false}
38 ContactData::~ContactData() { tribol::finalize(); }
40 void ContactData::addContactInteraction(
int interaction_id,
const std::set<int>& bdry_attr_surf1,
41 const std::set<int>& bdry_attr_surf2, ContactOptions contact_opts)
44 auto* cs = tribol::CouplingSchemeManager::getInstance().findData(
static_cast<tribol::IndexT
>(interaction_id));
45 SLIC_ERROR_ROOT_IF(cs !=
nullptr,
46 std::format(
"Contact interaction id {} is already registered with Tribol.", interaction_id));
48 interactions_.emplace_back(interaction_id, mesh_, bdry_attr_surf1, bdry_attr_surf2, current_coords_, contact_opts);
49 if (contact_opts.enforcement == ContactEnforcement::LagrangeMultiplier) {
50 have_lagrange_multipliers_ =
true;
51 num_pressure_dofs_ += interactions_.back().numPressureDofs();
52 offsets_up_to_date_ =
false;
55 mfem::Array<int> contact_bdry_attribs;
56 contact_bdry_attribs.SetSize(mesh_.bdr_attributes.Max());
57 contact_bdry_attribs = 0;
60 for (
const auto& bdry_attr : bdry_attr_surf1) {
61 contact_bdry_attribs[bdry_attr - 1] = 1;
63 for (
const auto& bdry_attr : bdry_attr_surf2) {
64 contact_bdry_attribs[bdry_attr - 1] = 1;
67 mfem::Array<int> contact_interaction_dofs;
68 reference_nodes_->ParFESpace()->GetEssentialTrueDofs(contact_bdry_attribs, contact_interaction_dofs);
70 contact_dofs_.Append(contact_interaction_dofs);
73 contact_dofs_.Unique();
76 void ContactData::reset()
78 for (
auto& interaction : interactions_) {
79 FiniteElementState zero = interaction.pressure();
81 interaction.setPressure(zero);
85 void ContactData::updateGeometry(
int cycle,
double time,
double& dt,
86 std::optional<std::reference_wrapper<const mfem::Vector>> u_shape,
87 std::optional<std::reference_wrapper<const mfem::Vector>> u,
bool eval_jacobian)
94 setDisplacements(u_shape->get(), u->get());
97 for (
auto& interaction : interactions_) {
98 interaction.evalJacobian(eval_jacobian);
103 tribol::updateMfemParallelDecomposition();
106 tribol::update(cycle, time, dt);
109 void ContactData::updateForcesAndJacobian(
int cycle,
double time,
double& dt,
110 std::optional<std::reference_wrapper<const mfem::Vector>> u_shape,
111 std::optional<std::reference_wrapper<const mfem::Vector>> u,
112 std::optional<std::reference_wrapper<const mfem::Vector>> p)
115 updateGeometry(cycle, time, dt, u_shape, u,
false);
122 SLIC_ERROR_ROOT_IF(haveLagrangeMultipliers() && !p,
123 "updateForcesAndJacobian() requires pressure values for Lagrange multiplier contact.");
125 mfem::Vector zero_pressures;
127 zero_pressures.SetSize(numPressureDofs());
128 zero_pressures = 0.0;
130 const mfem::Vector& pressures = p ? p->get() : zero_pressures;
133 setPressures(pressures);
135 for (
auto& interaction : interactions_) {
136 interaction.evalJacobian(
true);
140 tribol::updateMfemParallelDecomposition();
141 tribol::update(cycle, time, dt);
144 FiniteElementDual ContactData::forces()
const
146 FiniteElementDual f(*reference_nodes_->ParFESpace(),
"contact force");
147 for (
const auto& interaction : interactions_) {
148 f += interaction.forces();
153 mfem::HypreParVector ContactData::mergedPressures()
const
156 mfem::HypreParVector merged_p(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
157 global_pressure_dof_offsets_.GetData());
158 for (
size_t i{0}; i < interactions_.size(); ++i) {
159 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
160 mfem::Vector p_interaction;
161 p_interaction.MakeRef(
162 merged_p, pressure_dof_offsets_[
static_cast<int>(i)],
163 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
164 p_interaction.Set(1.0, interactions_[i].pressure());
170 mfem::HypreParVector ContactData::mergedGaps(
bool zero_inactive)
const
173 mfem::HypreParVector merged_g(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
174 global_pressure_dof_offsets_.GetData());
175 for (
size_t i{0}; i < interactions_.size(); ++i) {
176 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
177 auto g = interactions_[i].gaps();
179 for (
auto dof : interactions_[i].inactiveDofs()) {
183 mfem::Vector g_interaction(
184 merged_g, pressure_dof_offsets_[
static_cast<int>(i)],
185 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
186 g_interaction.Set(1.0, g);
192 std::unique_ptr<mfem::BlockOperator> ContactData::mergedJacobian()
const
199 auto block_J = std::make_unique<mfem::BlockOperator>(jacobian_offsets_);
200 block_J->owns_blocks =
true;
203 mfem::Array2D<const mfem::HypreParMatrix*> dgdu_blocks(
static_cast<int>(interactions_.size()), 1);
204 mfem::Array2D<const mfem::HypreParMatrix*> dfdp_blocks(1,
static_cast<int>(interactions_.size()));
205 for (
size_t i{0}; i < interactions_.size(); ++i) {
206 dgdu_blocks(
static_cast<int>(i), 0) =
nullptr;
207 dfdp_blocks(0,
static_cast<int>(i)) =
nullptr;
210 for (
size_t i{0}; i < interactions_.size(); ++i) {
212 auto interaction_J = interactions_[i].jacobianContribution();
213 interaction_J->owns_blocks =
false;
216 if (!interaction_J->IsZeroBlock(0, 0)) {
217 SLIC_ERROR_ROOT_IF(!
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(0, 0)),
218 "Only HypreParMatrix constraint matrix blocks are currently supported.");
219 if (block_J->IsZeroBlock(0, 0)) {
220 block_J->SetBlock(0, 0, &interaction_J->GetBlock(0, 0));
222 block_J->SetBlock(0, 0,
223 mfem::Add(1.0,
static_cast<mfem::HypreParMatrix&
>(block_J->GetBlock(0, 0)), 1.0,
224 static_cast<mfem::HypreParMatrix&
>(interaction_J->GetBlock(0, 0))));
225 delete &interaction_J->GetBlock(0, 0);
230 if (!interaction_J->IsZeroBlock(1, 0) && !interaction_J->IsZeroBlock(0, 1)) {
231 auto dgdu =
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(1, 0));
232 auto dfdp =
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(0, 1));
233 SLIC_ERROR_ROOT_IF(!dgdu,
"Only HypreParMatrix constraint matrix blocks are currently supported.");
234 SLIC_ERROR_ROOT_IF(!dfdp,
"Only HypreParMatrix constraint matrix blocks are currently supported.");
236 dgdu_blocks(
static_cast<int>(i), 0) = dgdu;
237 dfdp_blocks(0,
static_cast<int>(i)) = dfdp;
240 if (haveLagrangeMultipliers()) {
242 block_J->SetBlock(1, 0, mfem::HypreParMatrixFromBlocks(dgdu_blocks));
244 block_J->SetBlock(0, 1, mfem::HypreParMatrixFromBlocks(dfdp_blocks));
246 for (
size_t i{0}; i < interactions_.size(); ++i) {
247 delete dgdu_blocks(
static_cast<int>(i), 0);
248 delete dfdp_blocks(0,
static_cast<int>(i));
251 mfem::Array<const mfem::Array<int>*> inactive_tdofs_vector(
static_cast<int>(interactions_.size()));
252 int inactive_tdofs_ct = 0;
253 for (
int i{0}; i < inactive_tdofs_vector.Size(); ++i) {
254 inactive_tdofs_vector[i] = &interactions_[
static_cast<size_t>(i)].inactiveDofs();
255 inactive_tdofs_ct += inactive_tdofs_vector[i]->Size();
257 mfem::Array<int> inactive_tdofs(inactive_tdofs_ct);
258 inactive_tdofs_ct = 0;
259 for (
int i{0}; i < inactive_tdofs_vector.Size(); ++i) {
260 if (inactive_tdofs_vector[i]) {
261 for (
int d{0}; d < inactive_tdofs_vector[i]->Size(); ++d) {
262 inactive_tdofs[d + inactive_tdofs_ct] = (*inactive_tdofs_vector[i])[d] + pressure_dof_offsets_[i];
264 inactive_tdofs_ct += inactive_tdofs_vector[i]->Size();
267 inactive_tdofs.GetMemory().SetHostPtrOwner(
false);
268 mfem::Array<int> rows(numPressureDofs() + 1);
270 inactive_tdofs_ct = 0;
271 for (
int i{0}; i < numPressureDofs(); ++i) {
272 if (inactive_tdofs_ct < inactive_tdofs.Size() && inactive_tdofs[inactive_tdofs_ct] == i) {
275 rows[i + 1] = inactive_tdofs_ct;
277 rows.GetMemory().SetHostPtrOwner(
false);
278 mfem::Vector ones(inactive_tdofs_ct);
280 ones.GetMemory().SetHostPtrOwner(
false);
281 mfem::SparseMatrix inactive_diag(rows.GetData(), inactive_tdofs.GetData(), ones.GetData(), numPressureDofs(),
282 numPressureDofs(),
false,
false,
true);
285 inactive_diag.SetDataOwner(
false);
287 new mfem::HypreParMatrix(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
288 global_pressure_dof_offsets_, &inactive_diag);
289 block_1_1->SetOwnerFlags(3, 3, 1);
290 block_J->SetBlock(1, 1, block_1_1);
296 void ContactData::residualFunction(
const mfem::Vector& u_shape,
const mfem::Vector& u, mfem::Vector& r)
298 const int disp_size = reference_nodes_->ParFESpace()->GetTrueVSize();
302 auto& u_const =
const_cast<mfem::Vector&
>(u);
303 const mfem::Vector u_blk(u_const, 0, disp_size);
304 const mfem::Vector p_blk(u_const, disp_size, numPressureDofs());
306 mfem::Vector r_blk(r, 0, disp_size);
307 mfem::Vector g_blk(r, disp_size, numPressureDofs());
309 updateForcesAndJacobian(cycle_, time_, dt_, u_shape, u_blk, p_blk);
314 g_blk.Set(1.0, mergedGaps(
true));
317 std::unique_ptr<mfem::BlockOperator> ContactData::jacobianFunction(std::unique_ptr<mfem::HypreParMatrix> orig_J)
const
319 auto J_contact = mergedJacobian();
320 if (J_contact->IsZeroBlock(0, 0)) {
321 J_contact->SetBlock(0, 0, orig_J.release());
323 J_contact->SetBlock(0, 0,
324 mfem::Add(1.0, *orig_J, 1.0,
static_cast<mfem::HypreParMatrix&
>(J_contact->GetBlock(0, 0))));
330 void ContactData::setPressures(
const mfem::Vector& merged_pressures)
const
333 for (
size_t i{0}; i < interactions_.size(); ++i) {
334 FiniteElementState p_interaction(interactions_[i].pressureSpace());
335 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
337 auto& merged_pressures_const =
const_cast<mfem::Vector&
>(merged_pressures);
338 const mfem::Vector p_interaction_ref(
339 merged_pressures_const, pressure_dof_offsets_[
static_cast<int>(i)],
340 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
341 p_interaction.Set(1.0, p_interaction_ref);
344 p_interaction.Set(interactions_[i].getContactOptions().penalty, interactions_[i].gaps());
346 for (
auto dof : interactions_[i].inactiveDofs()) {
347 p_interaction[dof] = 0.0;
349 interactions_[i].setPressure(p_interaction);
353 void ContactData::setDisplacements(
const mfem::Vector& shape_u,
const mfem::Vector& u)
355 mfem::ParGridFunction prolonged_shape_disp{current_coords_};
356 reference_nodes_->ParFESpace()->GetProlongationMatrix()->Mult(u, current_coords_);
357 reference_nodes_->ParFESpace()->GetProlongationMatrix()->Mult(shape_u, prolonged_shape_disp);
359 current_coords_ += *reference_nodes_;
360 current_coords_ += prolonged_shape_disp;
363 void ContactData::updateDofOffsets()
const
365 if (offsets_up_to_date_) {
368 jacobian_offsets_ = mfem::Array<int>({0, reference_nodes_->ParFESpace()->GetTrueVSize(),
369 numPressureDofs() + reference_nodes_->ParFESpace()->GetTrueVSize()});
370 pressure_dof_offsets_.SetSize(
static_cast<int>(interactions_.size()) + 1);
371 pressure_dof_offsets_ = 0;
372 for (
size_t i{0}; i < interactions_.size(); ++i) {
373 pressure_dof_offsets_[
static_cast<int>(i + 1)] =
374 pressure_dof_offsets_[
static_cast<int>(i)] + interactions_[i].numPressureDofs();
376 global_pressure_dof_offsets_.SetSize(mesh_.GetNRanks() + 1);
377 global_pressure_dof_offsets_ = 0;
378 global_pressure_dof_offsets_[mesh_.GetMyRank() + 1] = numPressureDofs();
379 MPI_Allreduce(MPI_IN_PLACE, global_pressure_dof_offsets_.GetData(), global_pressure_dof_offsets_.Size(), MPI_INT,
380 MPI_SUM, mesh_.GetComm());
381 for (
int i{1}; i < mesh_.GetNRanks(); ++i) {
382 global_pressure_dof_offsets_[i + 1] += global_pressure_dof_offsets_[i];
384 if (HYPRE_AssumedPartitionCheck()) {
385 auto total_dofs = global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1];
387 if (mesh_.GetNRanks() < 2) {
388 global_pressure_dof_offsets_.SetSize(3);
390 global_pressure_dof_offsets_[0] = global_pressure_dof_offsets_[mesh_.GetMyRank()];
391 global_pressure_dof_offsets_[1] = global_pressure_dof_offsets_[mesh_.GetMyRank() + 1];
392 global_pressure_dof_offsets_[2] = total_dofs;
394 if (mesh_.GetNRanks() > 2) {
395 global_pressure_dof_offsets_.SetSize(3);
398 offsets_up_to_date_ =
true;
401 std::unique_ptr<mfem::HypreParMatrix> ContactData::contactSubspaceTransferOperator()
403 const MPI_Comm comm = reference_nodes_->ParFESpace()->GetComm();
404 HYPRE_BigInt* col_offsets = reference_nodes_->ParFESpace()->GetTrueDofOffsets();
405 HYPRE_BigInt ncols_glb = reference_nodes_->ParFESpace()->GlobalTrueVSize();
409 int nrows_loc = contact_dofs_.Size();
414 MPI_Allreduce(&nrows_loc, &nrows_glb, 1, MPI_INT, MPI_SUM, comm);
417 MPI_Scan(&nrows_loc, &row_offset, 1, MPI_INT, MPI_SUM, comm);
418 row_offset -= nrows_loc;
419 HYPRE_BigInt row_offsets[2];
420 row_offsets[0] = row_offset;
421 row_offsets[1] = row_offset + nrows_loc;
427 mfem::SparseMatrix Rsparse(nrows_loc, ncols_glb);
428 mfem::Array<int> col(1);
430 mfem::Vector entry(1);
432 HYPRE_BigInt col_offset = col_offsets[0];
433 for (
int k = 0; k < nrows_loc; k++) {
436 col[0] = col_offset + contact_dofs_[k];
437 Rsparse.SetRow(k, col, entry);
443 int* I = Rsparse.GetI();
444 HYPRE_BigInt* J = Rsparse.GetJ();
445 double* data = Rsparse.GetData();
447 std::unique_ptr<mfem::HypreParMatrix> restriction_operator = std::make_unique<mfem::HypreParMatrix>(
448 comm, nrows_loc, nrows_glb, ncols_glb, I, J, data, row_offsets, col_offsets);
452 std::unique_ptr<mfem::HypreParMatrix> transfer_operator;
453 transfer_operator.reset(restriction_operator->Transpose());
454 return transfer_operator;
459 ContactData::ContactData([[maybe_unused]]
const mfem::ParMesh& mesh)
460 : have_lagrange_multipliers_{false}, num_pressure_dofs_{0}
467 [[maybe_unused]]
const std::set<int>& bdry_attr_surf1,
468 [[maybe_unused]]
const std::set<int>& bdry_attr_surf2,
471 SLIC_WARNING_ROOT(
"Smith built without Tribol support. No contact interaction will be added.");
475 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> u_shape,
476 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> u,
477 [[maybe_unused]]
bool eval_jacobian)
482 [[maybe_unused]]
int cycle, [[maybe_unused]]
double time, [[maybe_unused]]
double& dt,
483 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> u_shape,
484 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> u,
485 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> p)
500 return mfem::HypreParVector();
505 jacobian_offsets_ = mfem::Array<int>(
506 {0, reference_nodes_->ParFESpace()->GetTrueVSize(), reference_nodes_->ParFESpace()->GetTrueVSize()});
507 return std::make_unique<mfem::BlockOperator>(jacobian_offsets_);
511 [[maybe_unused]] mfem::Vector& r)
518 if (J_contact->IsZeroBlock(0, 0)) {
519 J_contact->SetBlock(0, 0, orig_J.release());
521 J_contact->SetBlock(0, 0,
522 mfem::Add(1.0, *orig_J, 1.0,
static_cast<mfem::HypreParMatrix&
>(J_contact->GetBlock(0, 0))));
531 [[maybe_unused]]
const mfem::Vector& true_displacement)
537 std::unique_ptr<mfem::HypreParMatrix> transfer_operator =
nullptr;
538 return transfer_operator;
Class for encapsulating the dual vector space of a finite element space (i.e. the space of linear for...
This file contains the declaration of structure that manages the MFEM objects that make up the state ...
Accelerator functionality.