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::updateGaps(
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::update(
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)
116 updateGaps(cycle, time, dt, u_shape, u,
false);
127 setPressures(p->get());
129 for (
auto& interaction : interactions_) {
130 interaction.evalJacobian(
true);
134 tribol::updateMfemParallelDecomposition();
135 tribol::update(cycle, time, dt);
139 FiniteElementDual ContactData::forces()
const
141 FiniteElementDual f(*reference_nodes_->ParFESpace(),
"contact force");
142 for (
const auto& interaction : interactions_) {
143 f += interaction.forces();
148 mfem::HypreParVector ContactData::mergedPressures()
const
151 mfem::HypreParVector merged_p(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
152 global_pressure_dof_offsets_.GetData());
153 for (
size_t i{0}; i < interactions_.size(); ++i) {
154 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
155 mfem::Vector p_interaction;
156 p_interaction.MakeRef(
157 merged_p, pressure_dof_offsets_[
static_cast<int>(i)],
158 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
159 p_interaction.Set(1.0, interactions_[i].pressure());
165 mfem::HypreParVector ContactData::mergedGaps(
bool zero_inactive)
const
168 mfem::HypreParVector merged_g(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
169 global_pressure_dof_offsets_.GetData());
170 for (
size_t i{0}; i < interactions_.size(); ++i) {
171 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
172 auto g = interactions_[i].gaps();
174 for (
auto dof : interactions_[i].inactiveDofs()) {
178 mfem::Vector g_interaction(
179 merged_g, pressure_dof_offsets_[
static_cast<int>(i)],
180 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
181 g_interaction.Set(1.0, g);
187 std::unique_ptr<mfem::BlockOperator> ContactData::mergedJacobian()
const
194 auto block_J = std::make_unique<mfem::BlockOperator>(jacobian_offsets_);
195 block_J->owns_blocks =
true;
198 mfem::Array2D<const mfem::HypreParMatrix*> dgdu_blocks(
static_cast<int>(interactions_.size()), 1);
199 mfem::Array2D<const mfem::HypreParMatrix*> dfdp_blocks(1,
static_cast<int>(interactions_.size()));
200 for (
size_t i{0}; i < interactions_.size(); ++i) {
201 dgdu_blocks(
static_cast<int>(i), 0) =
nullptr;
202 dfdp_blocks(0,
static_cast<int>(i)) =
nullptr;
205 for (
size_t i{0}; i < interactions_.size(); ++i) {
207 auto interaction_J = interactions_[i].jacobianContribution();
208 interaction_J->owns_blocks =
false;
211 if (!interaction_J->IsZeroBlock(0, 0)) {
212 SLIC_ERROR_ROOT_IF(!
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(0, 0)),
213 "Only HypreParMatrix constraint matrix blocks are currently supported.");
214 if (block_J->IsZeroBlock(0, 0)) {
215 block_J->SetBlock(0, 0, &interaction_J->GetBlock(0, 0));
217 block_J->SetBlock(0, 0,
218 mfem::Add(1.0,
static_cast<mfem::HypreParMatrix&
>(block_J->GetBlock(0, 0)), 1.0,
219 static_cast<mfem::HypreParMatrix&
>(interaction_J->GetBlock(0, 0))));
220 delete &interaction_J->GetBlock(0, 0);
225 if (!interaction_J->IsZeroBlock(1, 0) && !interaction_J->IsZeroBlock(0, 1)) {
226 auto dgdu =
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(1, 0));
227 auto dfdp =
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(0, 1));
228 SLIC_ERROR_ROOT_IF(!dgdu,
"Only HypreParMatrix constraint matrix blocks are currently supported.");
229 SLIC_ERROR_ROOT_IF(!dfdp,
"Only HypreParMatrix constraint matrix blocks are currently supported.");
231 dgdu_blocks(
static_cast<int>(i), 0) = dgdu;
232 dfdp_blocks(0,
static_cast<int>(i)) = dfdp;
235 if (haveLagrangeMultipliers()) {
237 block_J->SetBlock(1, 0, mfem::HypreParMatrixFromBlocks(dgdu_blocks));
239 block_J->SetBlock(0, 1, mfem::HypreParMatrixFromBlocks(dfdp_blocks));
241 for (
size_t i{0}; i < interactions_.size(); ++i) {
242 delete dgdu_blocks(
static_cast<int>(i), 0);
243 delete dfdp_blocks(0,
static_cast<int>(i));
246 mfem::Array<const mfem::Array<int>*> inactive_tdofs_vector(
static_cast<int>(interactions_.size()));
247 int inactive_tdofs_ct = 0;
248 for (
int i{0}; i < inactive_tdofs_vector.Size(); ++i) {
249 inactive_tdofs_vector[i] = &interactions_[
static_cast<size_t>(i)].inactiveDofs();
250 inactive_tdofs_ct += inactive_tdofs_vector[i]->Size();
252 mfem::Array<int> inactive_tdofs(inactive_tdofs_ct);
253 inactive_tdofs_ct = 0;
254 for (
int i{0}; i < inactive_tdofs_vector.Size(); ++i) {
255 if (inactive_tdofs_vector[i]) {
256 for (
int d{0}; d < inactive_tdofs_vector[i]->Size(); ++d) {
257 inactive_tdofs[d + inactive_tdofs_ct] = (*inactive_tdofs_vector[i])[d] + pressure_dof_offsets_[i];
259 inactive_tdofs_ct += inactive_tdofs_vector[i]->Size();
262 mfem::Array<int> rows(numPressureDofs() + 1);
264 inactive_tdofs_ct = 0;
265 for (
int i{0}; i < numPressureDofs(); ++i) {
266 if (inactive_tdofs_ct < inactive_tdofs.Size() && inactive_tdofs[inactive_tdofs_ct] == i) {
269 rows[i + 1] = inactive_tdofs_ct;
271 mfem::Vector ones(inactive_tdofs_ct);
273 mfem::SparseMatrix inactive_diag(rows.GetData(), inactive_tdofs.GetData(), ones.GetData(), numPressureDofs(),
274 numPressureDofs(),
false,
false,
true);
275 rows.GetMemory().ClearOwnerFlags();
276 inactive_tdofs.GetMemory().ClearOwnerFlags();
277 ones.GetMemory().ClearOwnerFlags();
278 inactive_diag.GetMemoryI().ClearOwnerFlags();
279 inactive_diag.GetMemoryJ().ClearOwnerFlags();
280 inactive_diag.GetMemoryData().ClearOwnerFlags();
282 new mfem::HypreParMatrix(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
283 global_pressure_dof_offsets_, &inactive_diag);
284 constexpr
int mfem_owned_host_flag = 3;
285 block_1_1->SetOwnerFlags(mfem_owned_host_flag, block_1_1->OwnsOffd(), block_1_1->OwnsColMap());
286 block_J->SetBlock(1, 1, block_1_1);
292 void ContactData::residualFunction(
const mfem::Vector& u_shape,
const mfem::Vector& u, mfem::Vector& r)
294 const int disp_size = reference_nodes_->ParFESpace()->GetTrueVSize();
298 auto& u_const =
const_cast<mfem::Vector&
>(u);
299 const mfem::Vector u_blk(u_const, 0, disp_size);
300 const mfem::Vector p_blk(u_const, disp_size, numPressureDofs());
302 mfem::Vector r_blk(r, 0, disp_size);
303 mfem::Vector g_blk(r, disp_size, numPressureDofs());
305 update(cycle_, time_, dt_, u_shape, u_blk, p_blk);
310 g_blk.Set(1.0, mergedGaps(
true));
313 std::unique_ptr<mfem::BlockOperator> ContactData::jacobianFunction(std::unique_ptr<mfem::HypreParMatrix> orig_J)
const
315 auto J_contact = mergedJacobian();
316 if (J_contact->IsZeroBlock(0, 0)) {
317 J_contact->SetBlock(0, 0, orig_J.release());
319 J_contact->SetBlock(0, 0,
320 mfem::Add(1.0, *orig_J, 1.0,
static_cast<mfem::HypreParMatrix&
>(J_contact->GetBlock(0, 0))));
326 void ContactData::setPressures(
const mfem::Vector& merged_pressures)
const
329 for (
size_t i{0}; i < interactions_.size(); ++i) {
330 FiniteElementState p_interaction(interactions_[i].pressureSpace());
331 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
333 auto& merged_pressures_const =
const_cast<mfem::Vector&
>(merged_pressures);
334 const mfem::Vector p_interaction_ref(
335 merged_pressures_const, pressure_dof_offsets_[
static_cast<int>(i)],
336 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
337 p_interaction.Set(1.0, p_interaction_ref);
340 p_interaction.Set(interactions_[i].getContactOptions().penalty, interactions_[i].gaps());
342 for (
auto dof : interactions_[i].inactiveDofs()) {
343 p_interaction[dof] = 0.0;
345 interactions_[i].setPressure(p_interaction);
349 void ContactData::setDisplacements(
const mfem::Vector& shape_u,
const mfem::Vector& u)
351 mfem::ParGridFunction prolonged_shape_disp{current_coords_};
352 reference_nodes_->ParFESpace()->GetProlongationMatrix()->Mult(u, current_coords_);
353 reference_nodes_->ParFESpace()->GetProlongationMatrix()->Mult(shape_u, prolonged_shape_disp);
355 current_coords_ += *reference_nodes_;
356 current_coords_ += prolonged_shape_disp;
359 void ContactData::updateDofOffsets()
const
361 if (offsets_up_to_date_) {
364 jacobian_offsets_ = mfem::Array<int>({0, reference_nodes_->ParFESpace()->GetTrueVSize(),
365 numPressureDofs() + reference_nodes_->ParFESpace()->GetTrueVSize()});
366 pressure_dof_offsets_.SetSize(
static_cast<int>(interactions_.size()) + 1);
367 pressure_dof_offsets_ = 0;
368 for (
size_t i{0}; i < interactions_.size(); ++i) {
369 pressure_dof_offsets_[
static_cast<int>(i + 1)] =
370 pressure_dof_offsets_[
static_cast<int>(i)] + interactions_[i].numPressureDofs();
372 global_pressure_dof_offsets_.SetSize(mesh_.GetNRanks() + 1);
373 global_pressure_dof_offsets_ = 0;
374 global_pressure_dof_offsets_[mesh_.GetMyRank() + 1] = numPressureDofs();
375 MPI_Allreduce(MPI_IN_PLACE, global_pressure_dof_offsets_.GetData(), global_pressure_dof_offsets_.Size(), MPI_INT,
376 MPI_SUM, mesh_.GetComm());
377 for (
int i{1}; i < mesh_.GetNRanks(); ++i) {
378 global_pressure_dof_offsets_[i + 1] += global_pressure_dof_offsets_[i];
380 if (HYPRE_AssumedPartitionCheck()) {
381 auto total_dofs = global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1];
383 if (mesh_.GetNRanks() < 2) {
384 global_pressure_dof_offsets_.SetSize(3);
386 global_pressure_dof_offsets_[0] = global_pressure_dof_offsets_[mesh_.GetMyRank()];
387 global_pressure_dof_offsets_[1] = global_pressure_dof_offsets_[mesh_.GetMyRank() + 1];
388 global_pressure_dof_offsets_[2] = total_dofs;
390 if (mesh_.GetNRanks() > 2) {
391 global_pressure_dof_offsets_.SetSize(3);
394 offsets_up_to_date_ =
true;
397 std::unique_ptr<mfem::HypreParMatrix> ContactData::contactSubspaceTransferOperator()
399 const MPI_Comm comm = reference_nodes_->ParFESpace()->GetComm();
400 HYPRE_BigInt* col_offsets = reference_nodes_->ParFESpace()->GetTrueDofOffsets();
401 HYPRE_BigInt ncols_glb = reference_nodes_->ParFESpace()->GlobalTrueVSize();
405 int nrows_loc = contact_dofs_.Size();
410 MPI_Allreduce(&nrows_loc, &nrows_glb, 1, MPI_INT, MPI_SUM, comm);
413 MPI_Scan(&nrows_loc, &row_offset, 1, MPI_INT, MPI_SUM, comm);
414 row_offset -= nrows_loc;
415 HYPRE_BigInt row_offsets[2];
416 row_offsets[0] = row_offset;
417 row_offsets[1] = row_offset + nrows_loc;
423 mfem::SparseMatrix Rsparse(nrows_loc, ncols_glb);
424 mfem::Array<int> col(1);
426 mfem::Vector entry(1);
428 HYPRE_BigInt col_offset = col_offsets[0];
429 for (
int k = 0; k < nrows_loc; k++) {
432 col[0] = col_offset + contact_dofs_[k];
433 Rsparse.SetRow(k, col, entry);
439 int* I = Rsparse.GetI();
440 HYPRE_BigInt* J = Rsparse.GetJ();
441 double* data = Rsparse.GetData();
443 std::unique_ptr<mfem::HypreParMatrix> restriction_operator = std::make_unique<mfem::HypreParMatrix>(
444 comm, nrows_loc, nrows_glb, ncols_glb, I, J, data, row_offsets, col_offsets);
448 std::unique_ptr<mfem::HypreParMatrix> transfer_operator;
449 transfer_operator.reset(restriction_operator->Transpose());
450 return transfer_operator;
455 ContactData::ContactData([[maybe_unused]]
const mfem::ParMesh& mesh)
456 : have_lagrange_multipliers_{false}, num_pressure_dofs_{0}
463 [[maybe_unused]]
const std::set<int>& bdry_attr_surf1,
464 [[maybe_unused]]
const std::set<int>& bdry_attr_surf2,
467 SLIC_WARNING_ROOT(
"Smith built without Tribol support. No contact interaction will be added.");
471 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> u_shape,
472 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> u,
473 [[maybe_unused]]
bool eval_jacobian)
477 void ContactData::update([[maybe_unused]]
int cycle, [[maybe_unused]]
double time, [[maybe_unused]]
double& dt,
478 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> u_shape,
479 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> u,
480 [[maybe_unused]] std::optional<std::reference_wrapper<const mfem::Vector>> p)
495 return mfem::HypreParVector();
500 jacobian_offsets_ = mfem::Array<int>(
501 {0, reference_nodes_->ParFESpace()->GetTrueVSize(), reference_nodes_->ParFESpace()->GetTrueVSize()});
502 return std::make_unique<mfem::BlockOperator>(jacobian_offsets_);
506 [[maybe_unused]] mfem::Vector& r)
513 if (J_contact->IsZeroBlock(0, 0)) {
514 J_contact->SetBlock(0, 0, orig_J.release());
516 J_contact->SetBlock(0, 0,
517 mfem::Add(1.0, *orig_J, 1.0,
static_cast<mfem::HypreParMatrix&
>(J_contact->GetBlock(0, 0))));
526 [[maybe_unused]]
const mfem::Vector& true_displacement)
532 std::unique_ptr<mfem::HypreParMatrix> transfer_operator =
nullptr;
533 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.