11 #include "axom/slic.hpp"
16 #ifdef SMITH_USE_TRIBOL
17 #include "tribol/interface/tribol.hpp"
18 #include "tribol/interface/mfem_tribol.hpp"
23 #ifdef SMITH_USE_TRIBOL
27 reference_nodes_{static_cast<const mfem::ParGridFunction*>(mesh.GetNodes())},
28 current_coords_{*reference_nodes_},
29 have_lagrange_multipliers_{false},
30 num_pressure_dofs_{0},
31 offsets_up_to_date_{false}
35 ContactData::~ContactData() { tribol::finalize(); }
37 void ContactData::addContactInteraction(
int interaction_id,
const std::set<int>& bdry_attr_surf1,
38 const std::set<int>& bdry_attr_surf2, ContactOptions contact_opts)
40 interactions_.emplace_back(interaction_id, mesh_, bdry_attr_surf1, bdry_attr_surf2, current_coords_, contact_opts);
41 if (contact_opts.enforcement == ContactEnforcement::LagrangeMultiplier) {
42 have_lagrange_multipliers_ =
true;
43 num_pressure_dofs_ += interactions_.back().numPressureDofs();
44 offsets_up_to_date_ =
false;
48 void ContactData::reset()
50 for (
auto& interaction : interactions_) {
51 FiniteElementState zero = interaction.pressure();
53 interaction.setPressure(zero);
57 void ContactData::update(
int cycle,
double time,
double& dt)
64 tribol::updateMfemParallelDecomposition();
68 tribol::update(cycle, time, dt);
71 FiniteElementDual ContactData::forces()
const
73 FiniteElementDual f(*reference_nodes_->ParFESpace(),
"contact force");
74 for (
const auto& interaction : interactions_) {
75 f += interaction.forces();
80 mfem::HypreParVector ContactData::mergedPressures()
const
83 mfem::HypreParVector merged_p(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
84 global_pressure_dof_offsets_.GetData());
85 for (
size_t i{0}; i < interactions_.size(); ++i) {
86 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
87 mfem::Vector p_interaction;
88 p_interaction.MakeRef(
89 merged_p, pressure_dof_offsets_[
static_cast<int>(i)],
90 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
91 p_interaction.Set(1.0, interactions_[i].pressure());
97 mfem::HypreParVector ContactData::mergedGaps(
bool zero_inactive)
const
100 mfem::HypreParVector merged_g(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
101 global_pressure_dof_offsets_.GetData());
102 for (
size_t i{0}; i < interactions_.size(); ++i) {
103 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
104 auto g = interactions_[i].gaps();
106 for (
auto dof : interactions_[i].inactiveDofs()) {
110 mfem::Vector g_interaction(
111 merged_g, pressure_dof_offsets_[
static_cast<int>(i)],
112 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
113 g_interaction.Set(1.0, g);
119 std::unique_ptr<mfem::BlockOperator> ContactData::mergedJacobian()
const
126 auto block_J = std::make_unique<mfem::BlockOperator>(jacobian_offsets_);
127 block_J->owns_blocks =
true;
130 mfem::Array2D<const mfem::HypreParMatrix*> dgdu_blocks(
static_cast<int>(interactions_.size()), 1);
131 mfem::Array2D<const mfem::HypreParMatrix*> dfdp_blocks(1,
static_cast<int>(interactions_.size()));
132 for (
size_t i{0}; i < interactions_.size(); ++i) {
133 dgdu_blocks(
static_cast<int>(i), 0) =
nullptr;
134 dfdp_blocks(0,
static_cast<int>(i)) =
nullptr;
137 for (
size_t i{0}; i < interactions_.size(); ++i) {
139 auto interaction_J = interactions_[i].jacobian();
140 interaction_J->owns_blocks =
false;
142 if (!interaction_J->IsZeroBlock(0, 0)) {
143 SLIC_ERROR_ROOT_IF(!
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(0, 0)),
144 "Only HypreParMatrix constraint matrix blocks are currently supported.");
145 if (block_J->IsZeroBlock(0, 0)) {
146 block_J->SetBlock(0, 0, &interaction_J->GetBlock(0, 0));
148 block_J->SetBlock(0, 0,
149 mfem::Add(1.0,
static_cast<mfem::HypreParMatrix&
>(block_J->GetBlock(0, 0)), 1.0,
150 static_cast<mfem::HypreParMatrix&
>(interaction_J->GetBlock(0, 0))));
151 delete &interaction_J->GetBlock(0, 0);
156 if (!interaction_J->IsZeroBlock(1, 0) && !interaction_J->IsZeroBlock(0, 1)) {
157 auto dgdu =
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(1, 0));
158 auto dfdp =
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(0, 1));
159 SLIC_ERROR_ROOT_IF(!dgdu,
"Only HypreParMatrix constraint matrix blocks are currently supported.");
160 SLIC_ERROR_ROOT_IF(!dfdp,
"Only HypreParMatrix constraint matrix blocks are currently supported.");
162 auto inactive_dofs = interactions_[i].inactiveDofs();
163 dgdu->EliminateRows(inactive_dofs);
164 dfdp->EliminateCols(inactive_dofs);
165 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::Penalty) {
167 std::unique_ptr<mfem::HypreParMatrix> BTB(mfem::ParMult(dfdp, dgdu,
true));
168 delete &interaction_J->GetBlock(1, 0);
169 delete &interaction_J->GetBlock(0, 1);
170 if (block_J->IsZeroBlock(0, 0)) {
171 mfem::Vector penalty(reference_nodes_->ParFESpace()->GetTrueVSize());
172 penalty = interactions_[i].getContactOptions().penalty;
173 BTB->ScaleRows(penalty);
174 block_J->SetBlock(0, 0, BTB.release());
176 block_J->SetBlock(0, 0,
177 mfem::Add(1.0,
static_cast<mfem::HypreParMatrix&
>(block_J->GetBlock(0, 0)),
178 interactions_[i].getContactOptions().penalty, *BTB));
183 dgdu_blocks(
static_cast<int>(i), 0) = dgdu;
184 dfdp_blocks(0,
static_cast<int>(i)) = dfdp;
186 if (!interaction_J->IsZeroBlock(1, 1)) {
188 delete &interaction_J->GetBlock(1, 1);
192 if (haveLagrangeMultipliers()) {
194 block_J->SetBlock(1, 0, mfem::HypreParMatrixFromBlocks(dgdu_blocks));
196 block_J->SetBlock(0, 1, mfem::HypreParMatrixFromBlocks(dfdp_blocks));
198 for (
size_t i{0}; i < interactions_.size(); ++i) {
199 delete dgdu_blocks(
static_cast<int>(i), 0);
200 delete dfdp_blocks(0,
static_cast<int>(i));
203 mfem::Array<const mfem::Array<int>*> inactive_tdofs_vector(
static_cast<int>(interactions_.size()));
204 int inactive_tdofs_ct = 0;
205 for (
int i{0}; i < inactive_tdofs_vector.Size(); ++i) {
206 inactive_tdofs_vector[i] = &interactions_[
static_cast<size_t>(i)].inactiveDofs();
207 inactive_tdofs_ct += inactive_tdofs_vector[i]->Size();
209 mfem::Array<int> inactive_tdofs(inactive_tdofs_ct);
210 inactive_tdofs_ct = 0;
211 for (
int i{0}; i < inactive_tdofs_vector.Size(); ++i) {
212 if (inactive_tdofs_vector[i]) {
213 for (
int d{0}; d < inactive_tdofs_vector[i]->Size(); ++d) {
214 inactive_tdofs[d + inactive_tdofs_ct] = (*inactive_tdofs_vector[i])[d] + pressure_dof_offsets_[i];
216 inactive_tdofs_ct += inactive_tdofs_vector[i]->Size();
219 inactive_tdofs.GetMemory().SetHostPtrOwner(
false);
220 mfem::Array<int> rows(numPressureDofs() + 1);
222 inactive_tdofs_ct = 0;
223 for (
int i{0}; i < numPressureDofs(); ++i) {
224 if (inactive_tdofs_ct < inactive_tdofs.Size() && inactive_tdofs[inactive_tdofs_ct] == i) {
227 rows[i + 1] = inactive_tdofs_ct;
229 rows.GetMemory().SetHostPtrOwner(
false);
230 mfem::Vector ones(inactive_tdofs_ct);
232 ones.GetMemory().SetHostPtrOwner(
false);
233 mfem::SparseMatrix inactive_diag(rows.GetData(), inactive_tdofs.GetData(), ones.GetData(), numPressureDofs(),
234 numPressureDofs(),
false,
false,
true);
237 inactive_diag.SetDataOwner(
false);
239 new mfem::HypreParMatrix(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
240 global_pressure_dof_offsets_, &inactive_diag);
241 block_1_1->SetOwnerFlags(3, 3, 1);
242 block_J->SetBlock(1, 1, block_1_1);
248 void ContactData::residualFunction(
const mfem::Vector& u_shape,
const mfem::Vector& u, mfem::Vector& r)
250 const int disp_size = reference_nodes_->ParFESpace()->GetTrueVSize();
254 auto& u_const =
const_cast<mfem::Vector&
>(u);
255 const mfem::Vector u_blk(u_const, 0, disp_size);
256 const mfem::Vector p_blk(u_const, disp_size, numPressureDofs());
258 mfem::Vector r_blk(r, 0, disp_size);
259 mfem::Vector g_blk(r, disp_size, numPressureDofs());
261 setDisplacements(u_shape, u_blk);
264 for (
auto& interaction : interactions_) {
265 interaction.evalJacobian(
false);
267 update(cycle_, time_, dt_);
271 for (
auto& interaction : interactions_) {
272 interaction.evalJacobian(
true);
274 update(cycle_, time_, dt_);
279 g_blk.Set(1.0, mergedGaps(
true));
282 std::unique_ptr<mfem::BlockOperator> ContactData::jacobianFunction(mfem::HypreParMatrix* orig_J)
const
284 auto J_contact = mergedJacobian();
285 if (J_contact->IsZeroBlock(0, 0)) {
286 J_contact->SetBlock(0, 0, orig_J);
288 J_contact->SetBlock(0, 0,
289 mfem::Add(1.0, *orig_J, 1.0,
static_cast<mfem::HypreParMatrix&
>(J_contact->GetBlock(0, 0))));
295 void ContactData::setPressures(
const mfem::Vector& merged_pressures)
const
298 for (
size_t i{0}; i < interactions_.size(); ++i) {
299 FiniteElementState p_interaction(interactions_[i].pressureSpace());
300 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
302 auto& merged_pressures_const =
const_cast<mfem::Vector&
>(merged_pressures);
303 const mfem::Vector p_interaction_ref(
304 merged_pressures_const, pressure_dof_offsets_[
static_cast<int>(i)],
305 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
306 p_interaction.Set(1.0, p_interaction_ref);
309 p_interaction.Set(interactions_[i].getContactOptions().penalty, interactions_[i].gaps());
311 for (
auto dof : interactions_[i].inactiveDofs()) {
312 p_interaction[dof] = 0.0;
314 interactions_[i].setPressure(p_interaction);
318 void ContactData::setDisplacements(
const mfem::Vector& shape_u,
const mfem::Vector& u)
320 mfem::ParGridFunction prolonged_shape_disp{current_coords_};
321 reference_nodes_->ParFESpace()->GetProlongationMatrix()->Mult(u, current_coords_);
322 reference_nodes_->ParFESpace()->GetProlongationMatrix()->Mult(shape_u, prolonged_shape_disp);
324 current_coords_ += *reference_nodes_;
325 current_coords_ += prolonged_shape_disp;
328 void ContactData::updateDofOffsets()
const
330 if (offsets_up_to_date_) {
333 jacobian_offsets_ = mfem::Array<int>({0, reference_nodes_->ParFESpace()->GetTrueVSize(),
334 numPressureDofs() + reference_nodes_->ParFESpace()->GetTrueVSize()});
335 pressure_dof_offsets_.SetSize(
static_cast<int>(interactions_.size()) + 1);
336 pressure_dof_offsets_ = 0;
337 for (
size_t i{0}; i < interactions_.size(); ++i) {
338 pressure_dof_offsets_[
static_cast<int>(i + 1)] =
339 pressure_dof_offsets_[
static_cast<int>(i)] + interactions_[i].numPressureDofs();
341 global_pressure_dof_offsets_.SetSize(mesh_.GetNRanks() + 1);
342 global_pressure_dof_offsets_ = 0;
343 global_pressure_dof_offsets_[mesh_.GetMyRank() + 1] = numPressureDofs();
344 MPI_Allreduce(MPI_IN_PLACE, global_pressure_dof_offsets_.GetData(), global_pressure_dof_offsets_.Size(), MPI_INT,
345 MPI_SUM, mesh_.GetComm());
346 for (
int i{1}; i < mesh_.GetNRanks(); ++i) {
347 global_pressure_dof_offsets_[i + 1] += global_pressure_dof_offsets_[i];
349 if (HYPRE_AssumedPartitionCheck()) {
350 auto total_dofs = global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1];
352 if (mesh_.GetNRanks() < 2) {
353 global_pressure_dof_offsets_.SetSize(3);
355 global_pressure_dof_offsets_[0] = global_pressure_dof_offsets_[mesh_.GetMyRank()];
356 global_pressure_dof_offsets_[1] = global_pressure_dof_offsets_[mesh_.GetMyRank() + 1];
357 global_pressure_dof_offsets_[2] = total_dofs;
359 if (mesh_.GetNRanks() > 2) {
360 global_pressure_dof_offsets_.SetSize(3);
363 offsets_up_to_date_ =
true;
368 ContactData::ContactData([[maybe_unused]]
const mfem::ParMesh& mesh)
369 : have_lagrange_multipliers_{false}, num_pressure_dofs_{0}
376 [[maybe_unused]]
const std::set<int>& bdry_attr_surf1,
377 [[maybe_unused]]
const std::set<int>& bdry_attr_surf2,
380 SLIC_WARNING_ROOT(
"Smith built without Tribol support. No contact interaction will be added.");
383 void ContactData::update([[maybe_unused]]
int cycle, [[maybe_unused]]
double time, [[maybe_unused]]
double& dt) {}
396 return mfem::HypreParVector();
401 jacobian_offsets_ = mfem::Array<int>(
402 {0, reference_nodes_->ParFESpace()->GetTrueVSize(), reference_nodes_->ParFESpace()->GetTrueVSize()});
403 return std::make_unique<mfem::BlockOperator>(jacobian_offsets_);
407 [[maybe_unused]] mfem::Vector& r)
414 if (J_contact->IsZeroBlock(0, 0)) {
415 J_contact->SetBlock(0, 0, orig_J);
417 J_contact->SetBlock(0, 0,
418 mfem::Add(1.0, *orig_J, 1.0,
static_cast<mfem::HypreParMatrix&
>(J_contact->GetBlock(0, 0))));
427 [[maybe_unused]]
const mfem::Vector& true_displacement)
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.