Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
33a0975
first commit on contact homotopy repo
thartland Sep 8, 2025
8bdc9da
more documentation on the added contact constraint functions
thartland Sep 8, 2025
51e083f
Merge remote-tracking branch 'origin/develop' into hartland1/feature/…
thartland Sep 8, 2025
d5af8dc
settingPressures and style
thartland Sep 9, 2025
8aa0917
safer grabbing of contact blocks, more error checking to make sure us…
thartland Sep 9, 2025
9213d2b
initializing the multipliers
thartland Sep 10, 2025
a2b0adf
initializing the multipliers
thartland Sep 10, 2025
cd9b2f8
Merge remote-tracking branch 'origin/develop' into hartland1/feature/…
thartland Sep 12, 2025
b20213e
simplifying things, obtainBlock static utility function for safe memo…
thartland Sep 12, 2025
e249df5
implementation of untested but compiling TiedContactProblem that inhe…
thartland Sep 16, 2025
b8bd03c
instantiating the TiedContactProblem
thartland Sep 16, 2025
74105b7
serac/smith convention of _ ending character for member variables and…
thartland Sep 17, 2025
417fcba
moving constraint_twist to a examples/contact/homotopy as it is conta…
thartland Sep 17, 2025
da16481
removing unnecessary [[maybe_unused]]
thartland Sep 17, 2025
0c93c33
update to constraint_twist example problem. debugging state
thartland Sep 19, 2025
01ca984
swapping order of calls
thartland Sep 19, 2025
759a22f
small updates
thartland Sep 22, 2025
7cfaace
temp fix for evaluate
ebchin Sep 23, 2025
f50bbdb
Merge pull request #1470 from LLNL/chin23/fix-evaluate
thartland Sep 24, 2025
c3e4b38
Merge branch 'develop' into hartland1/feature/contact-homotopy
chapman39 Sep 25, 2025
f67b7d0
merge develop
thartland Oct 10, 2025
870cdda
working TiedContact via EqualityConstrainedHomotopyProblem
thartland Oct 12, 2025
6cd27b6
clean up
thartland Oct 13, 2025
87ce874
delete free/raw pointer avoidance inertia relief example
thartland Oct 13, 2025
02e12b2
delete free/raw pointer avoidance in homotopy/contact example
thartland Oct 13, 2025
3d54eb3
minor removal of std::move
thartland Oct 13, 2025
1556b8a
small std::move change
thartland Oct 13, 2025
345cc7c
ContinuationSolvers with adjoint solve, cleaning up other else testin…
thartland Oct 13, 2025
b70cf4b
updating ContinuationSolver branch, adding in print_level for the in …
thartland Oct 15, 2025
78ed2ea
moving forward with a function argument in which we specify whether o…
thartland Oct 17, 2025
17e30e0
propagating ability to specify whether constraint functions are being…
thartland Oct 17, 2025
286383e
Merge remote-tracking branch 'origin/develop' into hartland1/feature/…
thartland Oct 17, 2025
3f98407
updating ContinuationSolver branch in order to cache the Q evaluation…
thartland Oct 20, 2025
c8223ff
using utility function that has function arguments ordered (row, colu…
thartland Oct 20, 2025
8923fb3
updating submodule
thartland Oct 20, 2025
66d333a
utility function rename
thartland Oct 20, 2025
604d8f7
more fully utilizing new_pt function evaluation caching
thartland Oct 22, 2025
9915788
updating inertia relief problem (minor), and updating ContinuationSol…
thartland Oct 29, 2025
c3a1f4c
Merge remote-tracking branch 'origin/develop' into hartland1/feature/…
thartland Oct 29, 2025
41682a2
removal of one usage of mfem::Mpi::WorldRank and comments
thartland Oct 29, 2025
5d0e414
Caching contact computations
thartland Oct 29, 2025
de4fbf7
Complete caching of function calls. Good for testing the infastructur…
thartland Oct 29, 2025
94ec08c
updating, fixed bug. more to do
thartland Nov 6, 2025
399c85d
full commit
thartland Nov 7, 2025
bb24929
updating constraint twist example, linearized contact and no contact …
thartland Nov 13, 2025
fd3479f
bug fix
thartland Nov 13, 2025
552d118
formatting
thartland Nov 13, 2025
c7ec343
getting rid of warning messages
thartland Nov 13, 2025
932cdb3
more descriptive naming, proper conventions for member variables, mor…
thartland Nov 14, 2025
793c068
more descriptive residual_state variable name instead of all_states
thartland Nov 14, 2025
54b90aa
formatting
thartland Nov 14, 2025
0475859
small cleanup
thartland Nov 14, 2025
8d8bd29
command line options, Frictionless --> TiedNormal contact in order th…
thartland Nov 17, 2025
807db0c
visualize option, fd check on gap constraint
thartland Nov 17, 2025
99f5ec9
finite difference check along each iterate direction
thartland Nov 18, 2025
f5439cb
saving displacement BC progress before working on this same item on a…
thartland Nov 19, 2025
44fbe4f
pushing up changes
Nov 19, 2025
80b7c92
update
Nov 19, 2025
42b1fd9
resolving conflict
Nov 19, 2025
f4b70f9
working nonlinear elasticity/contact problem/
Nov 19, 2025
9097758
initial cleanup following having working nonlinear contact solves via…
thartland Nov 19, 2025
45b19ef
infastructure in place for nonlinear contact with displacement BCs. T…
thartland Nov 19, 2025
aaed0c7
displacement boundary conditions
thartland Nov 20, 2025
6563fcb
updating ContinuationSolvers
thartland Nov 20, 2025
e582169
consistent usage of RegularizedNewtonMode for solving nonlinear equat…
thartland Nov 21, 2025
a891b8f
Merge branch 'develop' of github.com:LLNL/smith into hartland1/featur…
thartland Nov 23, 2025
4a5590f
serac --> smith name change on contact/homotopy example
thartland Nov 23, 2025
4dfd356
constraint_twist --> two_blocks rename, I believe this is a better de…
thartland Nov 23, 2025
7c2b7a9
removing unneeded options such as the no contact option
thartland Nov 23, 2025
18b94aa
style
thartland Nov 24, 2025
4fc3a09
style
thartland Nov 24, 2025
7d1b742
updating tribol version. I seem to have messed with the version used
thartland Nov 25, 2025
4fd8d57
updating two block example -- using fixed and displacement dofs now h…
thartland Nov 25, 2025
550f6ad
style
thartland Nov 26, 2025
fb923b6
updates to push more things to backend
thartland Nov 26, 2025
f04b320
updates, cleanup, consolidation, making homotopy examples more unifor…
thartland Nov 26, 2025
fb6b313
updating example problems, cleanup
Nov 26, 2025
d8cdb75
style
Nov 26, 2025
985b450
spacing
thartland Nov 30, 2025
c91cfbd
slightly more verbose param description
Nov 30, 2025
638f97e
Merge remote-tracking branch 'origin/develop' into hartland1/feature/…
thartland Dec 3, 2025
52bb2c1
updating readme
thartland Dec 3, 2025
4da74d1
bug fix inertia relief example
thartland Dec 3, 2025
d1d4a51
comment update
thartland Dec 5, 2025
522c25c
slight restructing, more comments
thartland Dec 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
357 changes: 346 additions & 11 deletions examples/contact/constraint_twist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,95 @@

#include "mfem.hpp"

// ContinuationSolver headers
#include "problems/Problems.hpp"
#include "solvers/HomotopySolver.hpp"
#include "utilities.hpp"

#include "serac/serac.hpp"


static constexpr int dim = 3;
static constexpr int disp_order = 1;

using VectorSpace = serac::H1<disp_order, dim>;

using DensitySpace = serac::L2<disp_order - 1>;

using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity;
using SolidWeakFormT = serac::SolidWeakForm<disp_order, dim, serac::Parameters<DensitySpace>>;

enum FIELD
{
DISP = SolidWeakFormT::DISPLACEMENT,
VELO = SolidWeakFormT::VELOCITY,
ACCEL = SolidWeakFormT::ACCELERATION,
DENSITY = SolidWeakFormT::NUM_STATES
};

/* NLMCP of the form
* 0 <= x \perp F(x, y) >= 0
* Q(x, y) = 0
* Here, F and x are both 0-dimensional
* and Q(x, y) = [ r(u) + (dc/du)^T l]
* [-c(u)]
* y = [ u ]
* [ l ]
* we use the approximate Jacobian
* dQ/dy \approx [ dr/du (dc/du)^T]
* [-dc/du 0 ]
* we note that the sign-convention with regard to "c" is important
* as the approximate Jacobian is positive semi-definite when dr/du is
* and thus the NLMC problem is guaranteed to be semi-monotone.
*/
template <typename SolidWeakFormType>
class TiedContactProblem : public GeneralNLMCProblem {
protected:
mfem::HypreParMatrix* dFdx;
mfem::HypreParMatrix* dFdy;
mfem::HypreParMatrix* dQdx;
mfem::HypreParMatrix* dQdy;
HYPRE_BigInt* uOffsets;
HYPRE_BigInt* cOffsets;
int dimu;
int dimc;
int dimcglb;
mfem::Array<int> y_partition;
std::vector<serac::FieldPtr> contact_states;
std::vector<serac::FieldPtr> all_states;
std::shared_ptr<SolidWeakFormType> weak_form;
std::unique_ptr<serac::FiniteElementState> shape_disp;
std::shared_ptr<serac::Mesh> mesh;
std::shared_ptr<serac::ContactConstraint> constraints;
double time = 0.0;
double dt = 0.0;
std::vector<double> jacobian_weights = {1.0, 0.0, 0.0, 0.0};

public:
TiedContactProblem(std::vector<serac::FieldPtr> contact_states_, std::vector<serac::FieldPtr> all_states_,
std::shared_ptr<serac::Mesh> mesh_, std::shared_ptr<SolidWeakFormType> weak_form_,
std::shared_ptr<serac::ContactConstraint> constraints_);
void F(const mfem::Vector& x, const mfem::Vector& y, mfem::Vector& feval, int& Feval_err) const;
void Q(const mfem::Vector& x, const mfem::Vector& y, mfem::Vector& qeval, int& Qeval_err) const;
mfem::HypreParMatrix* DxF(const mfem::Vector& x, const mfem::Vector& y);
mfem::HypreParMatrix* DyF(const mfem::Vector& x, const mfem::Vector& y);
mfem::HypreParMatrix* DxQ(const mfem::Vector& x, const mfem::Vector& y);
mfem::HypreParMatrix* DyQ(const mfem::Vector& x, const mfem::Vector& y);
virtual ~TiedContactProblem();
};

// this example is intended to eventually replace twist.cpp
int main(int argc, char* argv[])
{
// Initialize and automatically finalize MPI and other libraries
serac::ApplicationManager applicationManager(argc, argv);

// NOTE: p must be equal to 1 to work with Tribol's mortar method
constexpr int p = 1;
// NOTE: dim must be equal to 3
constexpr int dim = 3;
//// NOTE: p must be equal to 1 to work with Tribol's mortar method
//constexpr int p = 1;
//// NOTE: dim must be equal to 3
//constexpr int dim = 3;

using VectorSpace = serac::H1<p, dim>;
//using VectorSpace = serac::H1<p, dim>;

// Create DataStore
std::string name = "contact_twist_example";
Expand All @@ -51,28 +126,288 @@ int main(int argc, char* argv[])
auto contact_interaction_id = 0;
std::set<int> surface_1_boundary_attributes({4});
std::set<int> surface_2_boundary_attributes({5});
serac::ContactConstraint contact_constraint(contact_interaction_id, mesh->mfemParMesh(),
auto contact_constraint = std::make_shared<serac::ContactConstraint>(contact_interaction_id, mesh->mfemParMesh(),
surface_1_boundary_attributes, surface_2_boundary_attributes,
contact_options, contact_constraint_name);

serac::FiniteElementState shape = serac::StateManager::newState(VectorSpace{}, "shape", mesh->tag());
serac::FiniteElementState disp = serac::StateManager::newState(VectorSpace{}, "displacement", mesh->tag());
serac::FiniteElementState velo = serac::StateManager::newState(VectorSpace{}, "velocity", mesh->tag());
serac::FiniteElementState accel = serac::StateManager::newState(VectorSpace{}, "acceleration", mesh->tag());
serac::FiniteElementState density = serac::StateManager::newState(DensitySpace{}, "density", mesh->tag());

std::vector<serac::FiniteElementState> contact_states;
std::vector<serac::FiniteElementState> states;
std::vector<serac::FiniteElementState> params;
contact_states = {shape, disp};
states = {disp, velo, accel};
params = {density};


// initialize displacement
contact_states[serac::ContactFields::DISP].setFromFieldFunction([](serac::tensor<double, dim> x) {
auto u = 0.1 * x;
return u;
});

contact_states[serac::ContactFields::SHAPE] = 0.0;
states[FIELD::VELO] = 0.0;
states[FIELD::ACCEL] = 0.0;
params[0] = 1.0;

std::string physics_name = "solid";

// construct residual
auto solid_mechanics_weak_form =
std::make_shared<SolidWeakFormT>(physics_name, mesh, states[FIELD::DISP].space(), getSpaces(params));

SolidMaterial mat;
mat.K = 1.0;
mat.G = 0.5;
solid_mechanics_weak_form->setMaterial(serac::DependsOn<0>{}, mesh->entireBodyName(), mat);

// apply some traction boundary conditions
std::string surface_name = "side";
mesh->addDomainOfBoundaryElements(surface_name, serac::by_attr<dim>(1));
solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.0 * n; });

serac::tensor<double, dim> constant_force{};
for (int i = 0; i < dim; i++) {
constant_force[i] = 1.e0;
}

double time = 0.0, dt = 1.0;
int direction = 0;
auto input_states = getConstFieldPointers(contact_states);
auto gap = contact_constraint.evaluate(time, dt, input_states);
auto gap_Jacobian = contact_constraint.jacobian(time, dt, input_states, direction);
solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) {
return serac::tuple{constant_force, 0.0 * serac::get<serac::DERIVATIVE>(x)};
});


auto all_states = serac::getConstFieldPointers(states, params);
auto non_const_states = serac::getFieldPointers(states, params);
auto contact_state_ptrs = serac::getFieldPointers(contact_states);
TiedContactProblem<SolidWeakFormT> problem(contact_state_ptrs, non_const_states, mesh, solid_mechanics_weak_form, contact_constraint);


//double time = 0.0, dt = 1.0;
//int direction = serac::ContactFields::DISP;
//auto input_states = getConstFieldPointers(contact_states);
//auto gap = contact_constraint.evaluate(time, dt, input_states);
//auto gap_Jacobian = contact_constraint.jacobian(time, dt, input_states, direction);
//auto gap_Jacobian_tilde = contact_constraint.jacobian_tilde(time, dt, input_states, direction);

//int nPressureDofs = contact_constraint.numPressureDofs();
//mfem::Vector multipliers(nPressureDofs);
//multipliers = 0.0;
//auto residual = contact_constraint.residual_contribution(time, dt, input_states, multipliers, direction);



// auto residual_Jacobian = contact_constraint.residual_contribution_jacobian(time, dt,
// input_states, multipliers,
// direction);

return 0;
}

template <typename SolidWeakFormType>
TiedContactProblem<SolidWeakFormType>::TiedContactProblem(std::vector<serac::FiniteElementState*> contact_states_,
std::vector<serac::FiniteElementState*> all_states_,
std::shared_ptr<serac::Mesh> mesh_,
std::shared_ptr<SolidWeakFormType> weak_form_,
std::shared_ptr<serac::ContactConstraint> constraints_)
: GeneralNLMCProblem(),
dFdx(nullptr),
dFdy(nullptr),
dQdx(nullptr),
dQdy(nullptr),
uOffsets(nullptr),
cOffsets(nullptr)
{
weak_form = weak_form_;
mesh = mesh_;
shape_disp = std::make_unique<serac::FiniteElementState>(mesh->newShapeDisplacement());

constraints = constraints_;

all_states.resize(all_states_.size());
std::copy(all_states_.begin(), all_states_.end(), all_states.begin());

contact_states.resize(contact_states_.size());
std::copy(contact_states_.begin(), contact_states_.end(), contact_states.begin());

int numConstraints = constraints->numPressureDofs();

uOffsets = new HYPRE_BigInt[2];
cOffsets = new HYPRE_BigInt[2];
for (int i = 0; i < 2; i++) {
uOffsets[i] = all_states[FIELD::DISP]->space().GetTrueDofOffsets()[i];
}

int cumulativeConstraints = 0;
MPI_Scan(&numConstraints, &cumulativeConstraints, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
cOffsets[0] = cumulativeConstraints - numConstraints;
cOffsets[1] = cumulativeConstraints;

dimu = uOffsets[1] - uOffsets[0];
dimc = cOffsets[1] - cOffsets[0];
y_partition.SetSize(3);
y_partition[0] = 0;
y_partition[1] = dimu;
y_partition[2] = dimc;
y_partition.PartialSum();

{
HYPRE_BigInt dofOffsets[2];
HYPRE_BigInt complementarityOffsets[2];
for (int i = 0; i < 2; i++) {
dofOffsets[i] = uOffsets[i] + cOffsets[i];
}
complementarityOffsets[0] = 0;
complementarityOffsets[1] = 0;
Init(complementarityOffsets, dofOffsets);
}

// dF / dx 0 x 0 matrix
{
int nentries = 0;
auto temp = new mfem::SparseMatrix(dimx, dimxglb, nentries);
dFdx = GenerateHypreParMatrixFromSparseMatrix(dofOffsetsx, dofOffsetsx, temp);
delete temp;
}

// dF / dy 0 x dimy matrix
{
int nentries = 0;
auto temp = new mfem::SparseMatrix(dimx, dimyglb, nentries);
dFdy = GenerateHypreParMatrixFromSparseMatrix(dofOffsetsy, dofOffsetsx, temp);
delete temp;
}

// dQ / dx dimy x 0 matrix
{
int nentries = 0;
auto temp = new mfem::SparseMatrix(dimy, dimxglb, nentries);
dQdx = GenerateHypreParMatrixFromSparseMatrix(dofOffsetsx, dofOffsetsy, temp);
delete temp;
}
}

template <typename SolidWeakFormType>
void TiedContactProblem<SolidWeakFormType>::F(const mfem::Vector& x, const mfem::Vector& y, mfem::Vector& feval, int& Feval_err) const
{
MFEM_VERIFY(x.Size() == dimx && y.Size() == dimy && feval.Size() == dimx,
"TiedContactProblem::F -- Inconsistent dimensions");
feval = 0.0;
Feval_err = 0;
}

// Q = [ r + J^T l]
// [ -c ]
// dQ / dy = [ K J^T]
// [-J 0 ]
template <typename SolidWeakFormType>
void TiedContactProblem<SolidWeakFormType>::Q(const mfem::Vector& x, const mfem::Vector& y, mfem::Vector& qeval, int& Qeval_err) const
{
MFEM_VERIFY(x.Size() == dimx && y.Size() == dimy && qeval.Size() == dimy,
"TiedContactProblem::Q -- Inconsistent dimensions");
qeval = 0.0;
mfem::BlockVector yblock(y_partition);
yblock.Set(1.0, y);
mfem::BlockVector qblock(y_partition);
qblock = 0.0;

contact_states[serac::ContactFields::DISP]->Set(1.0, yblock.GetBlock(0));
serac::FiniteElementDual res_vector(all_states[FIELD::DISP]->space(), "tempresidual");
res_vector = weak_form->residual(time, dt, shape_disp.get(), serac::getConstFieldPointers(all_states));

// TODO: is this the right field pointer to pass?
auto res_contribution = constraints->residual_contribution(time, dt, serac::getConstFieldPointers(contact_states), yblock.GetBlock(1), serac::ContactFields::DISP);
auto gap = constraints->evaluate(time, dt, serac::getConstFieldPointers(contact_states));

qblock.GetBlock(0).Set(1.0, res_vector);
qblock.GetBlock(0).Add(1.0, res_contribution);
qblock.GetBlock(1).Set(-1.0, gap);

qeval.Set(1.0, qblock);

Qeval_err = 0;
int Qeval_err_loc = 0;
for (int i = 0; i < qeval.Size(); i++) {
if (std::isnan(qeval(i))) {
Qeval_err_loc = 1;
break;
}
}
MPI_Allreduce(&Qeval_err_loc, &Qeval_err, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
if (Qeval_err > 0) {
Qeval_err = 1;
}
if (Qeval_err > 0 && mfem::Mpi::WorldRank() == 0) {
std::cout << "at least one nan entry\n";
}
}

template <typename SolidWeakFormType>
mfem::HypreParMatrix* TiedContactProblem<SolidWeakFormType>::DxF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dFdx; }

template <typename SolidWeakFormType>
mfem::HypreParMatrix* TiedContactProblem<SolidWeakFormType>::DyF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dFdy; }

template <typename SolidWeakFormType>
mfem::HypreParMatrix* TiedContactProblem<SolidWeakFormType>::DxQ(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dQdx; }

template <typename SolidWeakFormType>
mfem::HypreParMatrix* TiedContactProblem<SolidWeakFormType>::DyQ(const mfem::Vector& /*x*/, const mfem::Vector& y)
{
MFEM_VERIFY(y.Size() == dimy, "TiedContactProblem::DyQ -- Inconsistent dimensions");
// dQdy = [dr/du dc/du^T]
// [dc/du 0 ]
// note we are neglecting Hessian constraint terms
mfem::BlockVector yblock(y_partition);
yblock.Set(1.0, y);
mfem::BlockVector qblock(y_partition);
qblock = 0.0;
contact_states[serac::ContactFields::DISP]->Set(1.0, yblock.GetBlock(0));
if (dQdy) {
delete dQdy;
}
{
mfem::HypreParMatrix* drdu = nullptr;
auto drdu_unique =
weak_form->jacobian(time, dt, shape_disp.get(), getConstFieldPointers(all_states), jacobian_weights);
MFEM_VERIFY(drdu_unique->Height() == dimu, "size error");

drdu = drdu_unique.release();

auto negdcdu_unique = constraints->jacobian(time, dt, serac::getConstFieldPointers(contact_states), serac::ContactFields::DISP);
auto negdcdu = negdcdu_unique.release();
mfem::Vector scale(dimc);
scale = -1.0;
negdcdu->ScaleRows(scale);

auto dcdutilde_unique = constraints->jacobian_tilde(time, dt, serac::getConstFieldPointers(contact_states), serac::ContactFields::DISP);
auto dcdutildeT = dcdutilde_unique->Transpose();

mfem::Array2D<const mfem::HypreParMatrix*> BlockMat(2, 2);
BlockMat(0, 0) = drdu;
BlockMat(0, 1) = dcdutildeT;
BlockMat(1, 0) = negdcdu;
BlockMat(1, 1) = nullptr;
dQdy = HypreParMatrixFromBlocks(BlockMat);
delete drdu;
delete dcdutildeT;
delete negdcdu;
}
return dQdy;
}

template <typename SolidWeakFormType>
TiedContactProblem<SolidWeakFormType>::~TiedContactProblem()
{
delete[] uOffsets;
delete[] cOffsets;
delete dFdx;
delete dFdy;
delete dQdx;
delete dQdy;
}

Loading
Loading