9 #include "axom/slic.hpp"
11 #ifdef SERAC_USE_TRIBOL
12 #include "tribol/interface/tribol.hpp"
13 #include "tribol/interface/mfem_tribol.hpp"
18 #ifdef SERAC_USE_TRIBOL
22 reference_nodes_{dynamic_cast<const mfem::ParGridFunction*>(mesh.GetNodes())},
23 current_coords_{*reference_nodes_},
24 have_lagrange_multipliers_{false},
25 num_pressure_dofs_{0},
26 offsets_up_to_date_{false}
28 tribol::initialize(mesh_.SpaceDimension(), mesh_.GetComm());
31 ContactData::~ContactData() { tribol::finalize(); }
33 void ContactData::addContactInteraction(
int interaction_id,
const std::set<int>& bdry_attr_surf1,
34 const std::set<int>& bdry_attr_surf2, ContactOptions contact_opts)
36 interactions_.emplace_back(interaction_id, mesh_, bdry_attr_surf1, bdry_attr_surf2, current_coords_, contact_opts);
37 if (contact_opts.enforcement == ContactEnforcement::LagrangeMultiplier) {
38 have_lagrange_multipliers_ =
true;
39 num_pressure_dofs_ += interactions_.back().numPressureDofs();
40 offsets_up_to_date_ =
false;
44 void ContactData::update(
int cycle,
double time,
double& dt)
48 tribol::updateMfemParallelDecomposition();
52 tribol::update(cycle, time, dt);
55 FiniteElementDual ContactData::forces()
const
57 FiniteElementDual f(*reference_nodes_->ParFESpace(),
"contact force");
58 for (
const auto& interaction : interactions_) {
59 f += interaction.forces();
64 mfem::HypreParVector ContactData::mergedPressures()
const
67 mfem::HypreParVector merged_p(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
68 global_pressure_dof_offsets_.GetData());
69 for (
size_t i{0}; i < interactions_.size(); ++i) {
70 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
71 mfem::Vector p_interaction;
72 p_interaction.MakeRef(
73 merged_p, pressure_dof_offsets_[
static_cast<int>(i)],
74 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
75 p_interaction.Set(1.0, interactions_[i].pressure());
81 mfem::HypreParVector ContactData::mergedGaps(
bool zero_inactive)
const
84 mfem::HypreParVector merged_g(mesh_.GetComm(), global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1],
85 global_pressure_dof_offsets_.GetData());
86 for (
size_t i{0}; i < interactions_.size(); ++i) {
87 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
88 auto g = interactions_[i].gaps();
90 for (
auto dof : interactions_[i].inactiveDofs()) {
94 mfem::Vector g_interaction(
95 merged_g, pressure_dof_offsets_[
static_cast<int>(i)],
96 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
97 g_interaction.Set(1.0, g);
103 std::unique_ptr<mfem::BlockOperator> ContactData::mergedJacobian()
const
110 auto block_J = std::make_unique<mfem::BlockOperator>(jacobian_offsets_);
111 block_J->owns_blocks =
true;
114 mfem::Array2D<const mfem::HypreParMatrix*> constraint_matrices(
static_cast<int>(interactions_.size()), 1);
116 for (
size_t i{0}; i < interactions_.size(); ++i) {
118 auto interaction_J = interactions_[i].jacobian();
119 interaction_J->owns_blocks =
false;
121 if (!interaction_J->IsZeroBlock(0, 0)) {
122 SLIC_ERROR_ROOT_IF(!
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(0, 0)),
123 "Only HypreParMatrix constraint matrix blocks are currently supported.");
124 if (block_J->IsZeroBlock(0, 0)) {
125 block_J->SetBlock(0, 0, &interaction_J->GetBlock(0, 0));
127 if (block_J->IsZeroBlock(0, 0)) {
128 block_J->SetBlock(0, 0, &interaction_J->GetBlock(0, 0));
130 block_J->SetBlock(0, 0,
131 mfem::Add(1.0,
static_cast<mfem::HypreParMatrix&
>(block_J->GetBlock(0, 0)), 1.0,
132 static_cast<mfem::HypreParMatrix&
>(interaction_J->GetBlock(0, 0))));
134 delete &interaction_J->GetBlock(0, 0);
139 if (!interaction_J->IsZeroBlock(1, 0)) {
140 auto B =
dynamic_cast<mfem::HypreParMatrix*
>(&interaction_J->GetBlock(1, 0));
141 SLIC_ERROR_ROOT_IF(!B,
"Only HypreParMatrix constraint matrix blocks are currently supported.");
143 B->EliminateRows(interactions_[i].inactiveDofs());
144 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::Penalty) {
146 std::unique_ptr<mfem::HypreParMatrix> BTB(
147 mfem::ParMult(std::unique_ptr<mfem::HypreParMatrix>(B->Transpose()).get(), B,
true));
148 delete &interaction_J->GetBlock(1, 0);
149 if (block_J->IsZeroBlock(0, 0)) {
150 mfem::Vector penalty(reference_nodes_->ParFESpace()->GetTrueVSize());
151 penalty = interactions_[i].getContactOptions().penalty;
152 BTB->ScaleRows(penalty);
153 block_J->SetBlock(0, 0, BTB.release());
155 block_J->SetBlock(0, 0,
156 mfem::Add(1.0,
static_cast<mfem::HypreParMatrix&
>(block_J->GetBlock(0, 0)),
157 interactions_[i].getContactOptions().penalty, *BTB));
159 constraint_matrices(
static_cast<int>(i), 0) =
nullptr;
163 constraint_matrices(
static_cast<int>(i), 0) =
static_cast<mfem::HypreParMatrix*
>(B);
165 if (interaction_J->IsZeroBlock(0, 1)) {
166 SLIC_ERROR_ROOT(
"Only symmetric constraint matrices are currently supported.");
168 delete &interaction_J->GetBlock(0, 1);
169 if (!interaction_J->IsZeroBlock(1, 1)) {
171 delete &interaction_J->GetBlock(1, 1);
175 if (haveLagrangeMultipliers()) {
177 block_J->SetBlock(1, 0, mfem::HypreParMatrixFromBlocks(constraint_matrices));
179 block_J->SetBlock(0, 1,
static_cast<mfem::HypreParMatrix&
>(block_J->GetBlock(1, 0)).Transpose());
181 mfem::Array<const mfem::Array<int>*> inactive_tdofs_vector(
static_cast<int>(interactions_.size()));
182 int inactive_tdofs_ct = 0;
183 for (
int i{0}; i < inactive_tdofs_vector.Size(); ++i) {
184 inactive_tdofs_vector[i] = &interactions_[
static_cast<size_t>(i)].inactiveDofs();
185 inactive_tdofs_ct += inactive_tdofs_vector[i]->Size();
187 mfem::Array<int> inactive_tdofs(inactive_tdofs_ct);
188 inactive_tdofs_ct = 0;
189 for (
int i{0}; i < inactive_tdofs_vector.Size(); ++i) {
190 if (inactive_tdofs_vector[i]) {
191 for (
int d{0}; d < inactive_tdofs_vector[i]->Size(); ++d) {
192 inactive_tdofs[d + inactive_tdofs_ct] = (*inactive_tdofs_vector[i])[d] + pressure_dof_offsets_[i];
194 inactive_tdofs_ct += inactive_tdofs_vector[i]->Size();
197 inactive_tdofs.GetMemory().SetHostPtrOwner(
false);
198 mfem::Array<int> rows(numPressureDofs() + 1);
200 inactive_tdofs_ct = 0;
201 for (
int i{0}; i < numPressureDofs(); ++i) {
202 if (inactive_tdofs_ct < inactive_tdofs.Size() && inactive_tdofs[inactive_tdofs_ct] == i) {
205 rows[i + 1] = inactive_tdofs_ct;
207 rows.GetMemory().SetHostPtrOwner(
false);
208 mfem::Vector ones(inactive_tdofs_ct);
210 ones.GetMemory().SetHostPtrOwner(
false);
211 mfem::SparseMatrix inactive_diag(rows.GetData(), inactive_tdofs.GetData(), ones.GetData(), numPressureDofs(),
212 numPressureDofs(),
false,
false,
true);
215 inactive_diag.SetDataOwner(
false);
216 auto& block_1_0 =
static_cast<mfem::HypreParMatrix&
>(block_J->GetBlock(1, 0));
217 auto block_1_1 =
new mfem::HypreParMatrix(block_1_0.GetComm(), block_1_0.GetGlobalNumRows(),
218 block_1_0.GetRowStarts(), &inactive_diag);
219 block_1_1->SetOwnerFlags(3, 3, 1);
220 block_J->SetBlock(1, 1, block_1_1);
226 void ContactData::residualFunction(
const mfem::Vector& u, mfem::Vector& r)
228 const int disp_size = reference_nodes_->ParFESpace()->GetTrueVSize();
232 auto& u_const =
const_cast<mfem::Vector&
>(u);
233 const mfem::Vector u_blk(u_const, 0, disp_size);
234 const mfem::Vector p_blk(u_const, disp_size, numPressureDofs());
236 mfem::Vector r_blk(r, 0, disp_size);
237 mfem::Vector g_blk(r, disp_size, numPressureDofs());
240 setDisplacements(u_blk);
251 g_blk.Set(1.0, mergedGaps(
true));
254 std::unique_ptr<mfem::BlockOperator> ContactData::jacobianFunction(
const mfem::Vector& u,
255 mfem::HypreParMatrix* orig_J)
const
259 auto& u_const =
const_cast<mfem::Vector&
>(u);
260 const mfem::Vector u_blk(u_const, 0, reference_nodes_->ParFESpace()->GetTrueVSize());
262 auto J_contact = mergedJacobian();
263 if (J_contact->IsZeroBlock(0, 0)) {
264 J_contact->SetBlock(0, 0, orig_J);
266 J_contact->SetBlock(0, 0,
267 mfem::Add(1.0, *orig_J, 1.0,
static_cast<mfem::HypreParMatrix&
>(J_contact->GetBlock(0, 0))));
273 void ContactData::setPressures(
const mfem::Vector& merged_pressures)
const
276 for (
size_t i{0}; i < interactions_.size(); ++i) {
277 FiniteElementState p_interaction(interactions_[i].pressureSpace());
278 if (interactions_[i].getContactOptions().enforcement == ContactEnforcement::LagrangeMultiplier) {
280 auto& merged_pressures_const =
const_cast<mfem::Vector&
>(merged_pressures);
281 const mfem::Vector p_interaction_ref(
282 merged_pressures_const, pressure_dof_offsets_[
static_cast<int>(i)],
283 pressure_dof_offsets_[
static_cast<int>(i) + 1] - pressure_dof_offsets_[
static_cast<int>(i)]);
284 p_interaction.Set(1.0, p_interaction_ref);
287 p_interaction.Set(interactions_[i].getContactOptions().penalty, interactions_[i].gaps());
289 for (
auto dof : interactions_[i].inactiveDofs()) {
290 p_interaction[dof] = 0.0;
292 interactions_[i].setPressure(p_interaction);
296 void ContactData::setDisplacements(
const mfem::Vector& u)
298 reference_nodes_->ParFESpace()->GetProlongationMatrix()->Mult(u, current_coords_);
299 current_coords_ += *reference_nodes_;
302 void ContactData::updateDofOffsets()
const
304 if (offsets_up_to_date_) {
307 jacobian_offsets_ = mfem::Array<int>({0, reference_nodes_->ParFESpace()->GetTrueVSize(),
308 numPressureDofs() + reference_nodes_->ParFESpace()->GetTrueVSize()});
309 pressure_dof_offsets_.SetSize(
static_cast<int>(interactions_.size()) + 1);
310 pressure_dof_offsets_ = 0;
311 for (
size_t i{0}; i < interactions_.size(); ++i) {
312 pressure_dof_offsets_[
static_cast<int>(i + 1)] =
313 pressure_dof_offsets_[
static_cast<int>(i)] + interactions_[i].numPressureDofs();
315 global_pressure_dof_offsets_.SetSize(mesh_.GetNRanks() + 1);
316 global_pressure_dof_offsets_ = 0;
317 global_pressure_dof_offsets_[mesh_.GetMyRank() + 1] = numPressureDofs();
318 MPI_Allreduce(MPI_IN_PLACE, global_pressure_dof_offsets_.GetData(), global_pressure_dof_offsets_.Size(), MPI_INT,
319 MPI_SUM, mesh_.GetComm());
320 for (
int i{1}; i < mesh_.GetNRanks(); ++i) {
321 global_pressure_dof_offsets_[i + 1] += global_pressure_dof_offsets_[i];
323 if (HYPRE_AssumedPartitionCheck()) {
324 auto total_dofs = global_pressure_dof_offsets_[global_pressure_dof_offsets_.Size() - 1];
326 if (mesh_.GetNRanks() < 2) {
327 global_pressure_dof_offsets_.SetSize(3);
329 global_pressure_dof_offsets_[0] = global_pressure_dof_offsets_[mesh_.GetMyRank()];
330 global_pressure_dof_offsets_[1] = global_pressure_dof_offsets_[mesh_.GetMyRank() + 1];
331 global_pressure_dof_offsets_[2] = total_dofs;
333 if (mesh_.GetNRanks() > 2) {
334 global_pressure_dof_offsets_.SetSize(3);
337 offsets_up_to_date_ =
true;
342 ContactData::ContactData([[maybe_unused]]
const mfem::ParMesh& mesh)
343 : have_lagrange_multipliers_{false}, num_pressure_dofs_{0}
350 [[maybe_unused]]
const std::set<int>& bdry_attr_surf1,
351 [[maybe_unused]]
const std::set<int>& bdry_attr_surf2,
354 SLIC_WARNING_ROOT(
"Serac built without Tribol support. No contact interaction will be added.");
357 void ContactData::update([[maybe_unused]]
int cycle, [[maybe_unused]]
double time, [[maybe_unused]]
double& dt) {}
370 return mfem::HypreParVector();
375 jacobian_offsets_ = mfem::Array<int>(
376 {0, reference_nodes_->ParFESpace()->GetTrueVSize(), reference_nodes_->ParFESpace()->GetTrueVSize()});
377 return std::make_unique<mfem::BlockOperator>(jacobian_offsets_);
383 mfem::HypreParMatrix* orig_J)
const
387 auto& u_const =
const_cast<mfem::Vector&
>(u);
388 const mfem::Vector u_blk(u_const, 0, reference_nodes_->ParFESpace()->GetTrueVSize());
391 if (J_contact->IsZeroBlock(0, 0)) {
392 J_contact->SetBlock(0, 0, orig_J);
394 J_contact->SetBlock(0, 0,
395 mfem::Add(1.0, *orig_J, 1.0,
static_cast<mfem::HypreParMatrix&
>(J_contact->GetBlock(0, 0))));
Class for encapsulating the dual vector space of a finite element space (i.e. the space of linear for...
Accelerator functionality.