From 33a097549c60bbc79a834468d8dafec273b92afa Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 8 Sep 2025 13:17:54 -0700 Subject: [PATCH 01/82] first commit on contact homotopy repo --- src/serac/physics/contact_constraint.hpp | 59 +++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 9e11ed851..6d9c9da83 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -96,7 +96,7 @@ class ContactConstraint : public Constraint { contact_.update(cycle, time, dt); return contact_.mergedGaps(false); }; - + /** @brief Interface for computing contact gap constraint Jacobian from a vector of serac::FiniteElementState* * * @param time time @@ -124,6 +124,63 @@ class ContactConstraint : public Constraint { std::unique_ptr dgdu_unique(dgdu); return dgdu_unique; }; + + /** @brief Interface for computing...*/ + mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, + const mfem::Vector& multipliers, int direction) const + { + contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); + tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); + + int cycle = 0; + contact_.update(cycle, time, dt); + return contact_.forces(); + }; + + std::unique_ptr residual_contribution_jacobian( + [[maybe_unused]] double time, [[maybe_unused]] double dt, + [[maybe_unused]] const std::vector& fields, [[maybe_unused]] const mfem::Vector& multipliers, + [[maybe_unused]] int direction) const + { + contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); + tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); + + // TODO: how to specify the right cycle? + int cycle = 0; + contact_.update(cycle, time, dt); + auto J_contact = contact_.mergedJacobian(); + J_contact->owns_blocks = false; + delete &J_contact->GetBlock(0, 1); + delete &J_contact->GetBlock(1, 0); + delete &J_contact->GetBlock(1, 1); + + auto dgdu = dynamic_cast(&J_contact->GetBlock(0, 0)); + std::unique_ptr dgdu_unique(dgdu); + return dgdu_unique; + }; + + + std::unique_ptr jacobian_tilde(double time, double dt, + const std::vector& fields, + int direction) const + { + contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); + tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); + + // TODO: how to specify the right cycle? + int cycle = 0; + contact_.update(cycle, time, dt); + auto J_contact = contact_.mergedJacobian(); + J_contact->owns_blocks = false; + delete &J_contact->GetBlock(0, 0); + delete &J_contact->GetBlock(1, 0); + delete &J_contact->GetBlock(1, 1); + + auto dgdu = dynamic_cast(&J_contact->GetBlock(0, 1)); + std::unique_ptr dgdu_unique(dgdu); + return dgdu_unique; + }; + protected: /** From 8bdc9da39740e15f1249051bb4b3f0f3b466c8ed Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 8 Sep 2025 16:40:35 -0700 Subject: [PATCH 02/82] more documentation on the added contact constraint functions --- src/serac/physics/contact_constraint.hpp | 48 ++++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 6d9c9da83..680923c56 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -85,8 +85,8 @@ class ContactConstraint : public Constraint { * @param fields vector of serac::FiniteElementState* * @return mfem::Vector which is the constraint evaluation */ - mfem::Vector evaluate([[maybe_unused]] double time, [[maybe_unused]] double dt, - [[maybe_unused]] const std::vector& fields) const + mfem::Vector evaluate(double time, double dt, + const std::vector& fields) const { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); @@ -105,10 +105,11 @@ class ContactConstraint : public Constraint { * @param direction index for which field to take the gradient with respect to * @return std::unique_ptr */ - std::unique_ptr jacobian([[maybe_unused]] double time, [[maybe_unused]] double dt, - [[maybe_unused]] const std::vector& fields, + std::unique_ptr jacobian(double time, double dt, + const std::vector& fields, [[maybe_unused]] int direction) const { + // TODO: should direction be optional? should we check that the user has been an acceptable value? contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); @@ -125,9 +126,18 @@ class ContactConstraint : public Constraint { return dgdu_unique; }; - /** @brief Interface for computing...*/ + /** @brief Interface for computing residual contribution Jacobian_tilde^T multiplier from a vector of + * serac::FiniteElementState* + * + * @param time time + * @param dt time step + * @param fields vector of serac::FiniteElementState* + * @param multipliers mfem::Vector of Lagrange multipliers + * @param direction index for which field to take the gradient with respect to + * @return std::Vector + */ mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, - const mfem::Vector& multipliers, int direction) const + [[maybe_unused]] const mfem::Vector& multipliers, [[maybe_unused]] int direction) const { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); @@ -137,15 +147,24 @@ class ContactConstraint : public Constraint { return contact_.forces(); }; + /** @brief Interface for computing Jacobians of the residual contribution from a vector of + * serac::FiniteElementState* + * + * @param time time + * @param dt time step + * @param fields vector of serac::FiniteElementState* + * @param multipliers mfem::Vector of Lagrange multipliers + * @param direction index for which field to take the gradient with respect to + * @return std::unique_ptr + */ std::unique_ptr residual_contribution_jacobian( - [[maybe_unused]] double time, [[maybe_unused]] double dt, - [[maybe_unused]] const std::vector& fields, [[maybe_unused]] const mfem::Vector& multipliers, + double time, double dt, + const std::vector& fields, [[maybe_unused]] const mfem::Vector& multipliers, [[maybe_unused]] int direction) const { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); - // TODO: how to specify the right cycle? int cycle = 0; contact_.update(cycle, time, dt); auto J_contact = contact_.mergedJacobian(); @@ -160,14 +179,21 @@ class ContactConstraint : public Constraint { }; + /** @brief Interface for computing contact constraint Jacobian_tilde from a vector of serac::FiniteElementState* + * + * @param time time + * @param dt time step + * @param fields vector of serac::FiniteElementState* + * @param direction index for which field to take the gradient with respect to + * @return std::unique_ptr + */ std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, - int direction) const + [[maybe_unused]] int direction) const { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); - // TODO: how to specify the right cycle? int cycle = 0; contact_.update(cycle, time, dt); auto J_contact = contact_.mergedJacobian(); From d5af8dc970b9784ebc8e811eb4fc8320db4e8772 Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 9 Sep 2025 09:23:54 -0700 Subject: [PATCH 03/82] settingPressures and style --- src/serac/physics/contact_constraint.hpp | 33 +++++++++++------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 680923c56..e0ef831fd 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -85,8 +85,7 @@ class ContactConstraint : public Constraint { * @param fields vector of serac::FiniteElementState* * @return mfem::Vector which is the constraint evaluation */ - mfem::Vector evaluate(double time, double dt, - const std::vector& fields) const + mfem::Vector evaluate(double time, double dt, const std::vector& fields) const { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); @@ -96,7 +95,7 @@ class ContactConstraint : public Constraint { contact_.update(cycle, time, dt); return contact_.mergedGaps(false); }; - + /** @brief Interface for computing contact gap constraint Jacobian from a vector of serac::FiniteElementState* * * @param time time @@ -105,8 +104,7 @@ class ContactConstraint : public Constraint { * @param direction index for which field to take the gradient with respect to * @return std::unique_ptr */ - std::unique_ptr jacobian(double time, double dt, - const std::vector& fields, + std::unique_ptr jacobian(double time, double dt, const std::vector& fields, [[maybe_unused]] int direction) const { // TODO: should direction be optional? should we check that the user has been an acceptable value? @@ -125,7 +123,7 @@ class ContactConstraint : public Constraint { std::unique_ptr dgdu_unique(dgdu); return dgdu_unique; }; - + /** @brief Interface for computing residual contribution Jacobian_tilde^T multiplier from a vector of * serac::FiniteElementState* * @@ -137,16 +135,17 @@ class ContactConstraint : public Constraint { * @return std::Vector */ mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, - [[maybe_unused]] const mfem::Vector& multipliers, [[maybe_unused]] int direction) const + const mfem::Vector& multipliers, [[maybe_unused]] int direction) const { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); + contact_.setPressures(multipliers); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); int cycle = 0; contact_.update(cycle, time, dt); return contact_.forces(); }; - + /** @brief Interface for computing Jacobians of the residual contribution from a vector of * serac::FiniteElementState* * @@ -157,12 +156,13 @@ class ContactConstraint : public Constraint { * @param direction index for which field to take the gradient with respect to * @return std::unique_ptr */ - std::unique_ptr residual_contribution_jacobian( - double time, double dt, - const std::vector& fields, [[maybe_unused]] const mfem::Vector& multipliers, - [[maybe_unused]] int direction) const + std::unique_ptr residual_contribution_jacobian(double time, double dt, + const std::vector& fields, + const mfem::Vector& multipliers, + [[maybe_unused]] int direction) const { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); + contact_.setPressures(multipliers); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); int cycle = 0; @@ -177,8 +177,7 @@ class ContactConstraint : public Constraint { std::unique_ptr dgdu_unique(dgdu); return dgdu_unique; }; - - + /** @brief Interface for computing contact constraint Jacobian_tilde from a vector of serac::FiniteElementState* * * @param time time @@ -187,9 +186,8 @@ class ContactConstraint : public Constraint { * @param direction index for which field to take the gradient with respect to * @return std::unique_ptr */ - std::unique_ptr jacobian_tilde(double time, double dt, - const std::vector& fields, - [[maybe_unused]] int direction) const + std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, + [[maybe_unused]] int direction) const { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); @@ -207,7 +205,6 @@ class ContactConstraint : public Constraint { return dgdu_unique; }; - protected: /** * @brief ContactData which has various contact calls From 8aa0917d840507759fca1cf908bb96babee40e6b Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 9 Sep 2025 14:28:01 -0700 Subject: [PATCH 04/82] safer grabbing of contact blocks, more error checking to make sure users dont request a Jacobian of the gap with respect to the shape displacement field, etc --- examples/contact/constraint_twist.cpp | 11 +++- src/serac/physics/contact_constraint.hpp | 68 +++++++++++++++++++----- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/examples/contact/constraint_twist.cpp b/examples/contact/constraint_twist.cpp index d8e58b3ac..c7863c736 100644 --- a/examples/contact/constraint_twist.cpp +++ b/examples/contact/constraint_twist.cpp @@ -69,10 +69,19 @@ int main(int argc, char* argv[]) contact_states[serac::ContactFields::SHAPE] = 0.0; double time = 0.0, dt = 1.0; - int direction = 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); + 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; } diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index e0ef831fd..819b43efc 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -115,11 +115,22 @@ class ContactConstraint : public Constraint { contact_.update(cycle, time, dt); auto J_contact = contact_.mergedJacobian(); J_contact->owns_blocks = false; - delete &J_contact->GetBlock(0, 0); - delete &J_contact->GetBlock(0, 1); - delete &J_contact->GetBlock(1, 1); - auto dgdu = dynamic_cast(&J_contact->GetBlock(1, 0)); + int iblock = 1; + int jblock = 0; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + if (i == iblock && j == jblock) { + continue; + } + if (!J_contact->IsZeroBlock(i, j)) { + delete &J_contact->GetBlock(i, j); + } + } + } + + SLIC_ERROR_IF(J_contact->IsZeroBlock(iblock, jblock), "attempting to extract a null block"); + auto dgdu = dynamic_cast(&J_contact->GetBlock(iblock, jblock)); std::unique_ptr dgdu_unique(dgdu); return dgdu_unique; }; @@ -137,6 +148,7 @@ class ContactConstraint : public Constraint { mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, [[maybe_unused]] int direction) const { + SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); contact_.setPressures(multipliers); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); @@ -161,6 +173,7 @@ class ContactConstraint : public Constraint { const mfem::Vector& multipliers, [[maybe_unused]] int direction) const { + SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); contact_.setPressures(multipliers); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); @@ -169,13 +182,24 @@ class ContactConstraint : public Constraint { contact_.update(cycle, time, dt); auto J_contact = contact_.mergedJacobian(); J_contact->owns_blocks = false; - delete &J_contact->GetBlock(0, 1); - delete &J_contact->GetBlock(1, 0); - delete &J_contact->GetBlock(1, 1); - auto dgdu = dynamic_cast(&J_contact->GetBlock(0, 0)); - std::unique_ptr dgdu_unique(dgdu); - return dgdu_unique; + int iblock = 0; + int jblock = 0; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + if (i == iblock && j == jblock) { + continue; + } + if (!J_contact->IsZeroBlock(i, j)) { + delete &J_contact->GetBlock(i, j); + } + } + } + + SLIC_ERROR_IF(J_contact->IsZeroBlock(iblock, jblock), "attempting to extract a null block"); + auto Hessian = dynamic_cast(&J_contact->GetBlock(iblock, jblock)); // constraint Hessian + std::unique_ptr Hessian_unique(Hessian); + return Hessian_unique; }; /** @brief Interface for computing contact constraint Jacobian_tilde from a vector of serac::FiniteElementState* @@ -189,6 +213,7 @@ class ContactConstraint : public Constraint { std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, [[maybe_unused]] int direction) const { + SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); @@ -196,15 +221,30 @@ class ContactConstraint : public Constraint { contact_.update(cycle, time, dt); auto J_contact = contact_.mergedJacobian(); J_contact->owns_blocks = false; - delete &J_contact->GetBlock(0, 0); - delete &J_contact->GetBlock(1, 0); - delete &J_contact->GetBlock(1, 1); - auto dgdu = dynamic_cast(&J_contact->GetBlock(0, 1)); + int iblock = 0; + int jblock = 1; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + if (i == iblock && j == jblock) { + continue; + } + if (!J_contact->IsZeroBlock(i, j)) { + delete &J_contact->GetBlock(i, j); + } + } + } + + SLIC_ERROR_IF(J_contact->IsZeroBlock(iblock, jblock), "attempting to extract a null block"); + auto dgduT = dynamic_cast(&J_contact->GetBlock(iblock, jblock)); + auto dgdu = dgduT->Transpose(); + delete dgduT; std::unique_ptr dgdu_unique(dgdu); return dgdu_unique; }; + int numPressureDofs() const { return contact_.numPressureDofs(); } + protected: /** * @brief ContactData which has various contact calls From 9213d2bb200f85026c026eff66ff75c7a84bcee9 Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 9 Sep 2025 17:02:07 -0700 Subject: [PATCH 05/82] initializing the multipliers --- examples/contact/constraint_twist.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/contact/constraint_twist.cpp b/examples/contact/constraint_twist.cpp index c7863c736..6c6012297 100644 --- a/examples/contact/constraint_twist.cpp +++ b/examples/contact/constraint_twist.cpp @@ -76,12 +76,12 @@ int main(int argc, char* argv[]) auto gap_Jacobian_tilde = contact_constraint.jacobian_tilde(time, dt, input_states, direction); int nPressureDofs = contact_constraint.numPressureDofs(); - mfem::Vector multipliers(nPressureDofs); + 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, + //auto residual_Jacobian = contact_constraint.residual_contribution_jacobian(time, dt, // input_states, multipliers, // direction); - // + return 0; } From a2b0adf6f7ea216cc2a47c2b7950c3939dcb5c96 Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 9 Sep 2025 17:03:09 -0700 Subject: [PATCH 06/82] initializing the multipliers --- examples/contact/constraint_twist.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/contact/constraint_twist.cpp b/examples/contact/constraint_twist.cpp index 6c6012297..a5795bf4d 100644 --- a/examples/contact/constraint_twist.cpp +++ b/examples/contact/constraint_twist.cpp @@ -76,12 +76,12 @@ int main(int argc, char* argv[]) 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; + 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); - + // auto residual_Jacobian = contact_constraint.residual_contribution_jacobian(time, dt, + // input_states, multipliers, + // direction); return 0; } From b20213ee26caa97290bfbd8e337894eb5beb97eb Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 12 Sep 2025 09:51:00 -0700 Subject: [PATCH 07/82] simplifying things, obtainBlock static utility function for safe memory management of contact blocks --- src/serac/physics/contact_constraint.hpp | 96 +++++++++++------------- 1 file changed, 42 insertions(+), 54 deletions(-) diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 819b43efc..11ec13caa 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -42,6 +42,39 @@ enum ContactFields DISP, }; +/** @brief Interface for extracting the iblock, jblock block from a std::unique_ptr + * said block is returned as a std::unique_ptr if possible + * All other blocks are deleted + * + * @param block_operator the block operator + * @param iblock row block index + * @param jblock column block index + * @return std::unique_ptr The requested block of block_operator + */ +static std::unique_ptr safelyObtainBlock(mfem::BlockOperator* block_operator, int iblock, + int jblock) +{ + SLIC_ERROR_IF(iblock < 0 || jblock < 0, "block indicies must be non-negative"); + SLIC_ERROR_IF(iblock > block_operator->NumRowBlocks() || jblock > block_operator->NumColBlocks(), + "one or more block indicies are too large and the requested block does not exist"); + SLIC_ERROR_IF(block_operator->IsZeroBlock(iblock, jblock), "attempting to extract a null block"); + block_operator->owns_blocks = false; + for (int i = 0; i < block_operator->NumRowBlocks(); i++) { + for (int j = 0; j < block_operator->NumColBlocks(); j++) { + if (i == iblock && j == jblock) { + continue; + } + if (!block_operator->IsZeroBlock(i, j)) { + delete &block_operator->GetBlock(i, j); + } + } + } + auto Ablk = dynamic_cast(&block_operator->GetBlock(iblock, jblock)); + SLIC_ERROR_IF(!Ablk, "failed cast to mfem::HypreParMatrix"); + std::unique_ptr Ablk_unique(Ablk); + return Ablk_unique; +}; + class FiniteElementState; /** @@ -102,37 +135,23 @@ class ContactConstraint : public Constraint { * @param dt time step * @param fields vector of serac::FiniteElementState* * @param direction index for which field to take the gradient with respect to - * @return std::unique_ptr + * @return std::unique_ptr The true Jacobian */ std::unique_ptr jacobian(double time, double dt, const std::vector& fields, [[maybe_unused]] int direction) const { - // TODO: should direction be optional? should we check that the user has been an acceptable value? + SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); int cycle = 0; contact_.update(cycle, time, dt); auto J_contact = contact_.mergedJacobian(); - J_contact->owns_blocks = false; int iblock = 1; int jblock = 0; - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - if (i == iblock && j == jblock) { - continue; - } - if (!J_contact->IsZeroBlock(i, j)) { - delete &J_contact->GetBlock(i, j); - } - } - } - - SLIC_ERROR_IF(J_contact->IsZeroBlock(iblock, jblock), "attempting to extract a null block"); - auto dgdu = dynamic_cast(&J_contact->GetBlock(iblock, jblock)); - std::unique_ptr dgdu_unique(dgdu); - return dgdu_unique; + auto dgdu = safelyObtainBlock(J_contact.get(), iblock, jblock); + return dgdu; }; /** @brief Interface for computing residual contribution Jacobian_tilde^T multiplier from a vector of @@ -181,25 +200,10 @@ class ContactConstraint : public Constraint { int cycle = 0; contact_.update(cycle, time, dt); auto J_contact = contact_.mergedJacobian(); - J_contact->owns_blocks = false; - int iblock = 0; int jblock = 0; - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - if (i == iblock && j == jblock) { - continue; - } - if (!J_contact->IsZeroBlock(i, j)) { - delete &J_contact->GetBlock(i, j); - } - } - } - - SLIC_ERROR_IF(J_contact->IsZeroBlock(iblock, jblock), "attempting to extract a null block"); - auto Hessian = dynamic_cast(&J_contact->GetBlock(iblock, jblock)); // constraint Hessian - std::unique_ptr Hessian_unique(Hessian); - return Hessian_unique; + auto Hessian = safelyObtainBlock(J_contact.get(), iblock, jblock); + return Hessian; }; /** @brief Interface for computing contact constraint Jacobian_tilde from a vector of serac::FiniteElementState* @@ -220,27 +224,11 @@ class ContactConstraint : public Constraint { int cycle = 0; contact_.update(cycle, time, dt); auto J_contact = contact_.mergedJacobian(); - J_contact->owns_blocks = false; - int iblock = 0; int jblock = 1; - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - if (i == iblock && j == jblock) { - continue; - } - if (!J_contact->IsZeroBlock(i, j)) { - delete &J_contact->GetBlock(i, j); - } - } - } - - SLIC_ERROR_IF(J_contact->IsZeroBlock(iblock, jblock), "attempting to extract a null block"); - auto dgduT = dynamic_cast(&J_contact->GetBlock(iblock, jblock)); - auto dgdu = dgduT->Transpose(); - delete dgduT; - std::unique_ptr dgdu_unique(dgdu); - return dgdu_unique; + auto dgduT = safelyObtainBlock(J_contact.get(), iblock, jblock); + std::unique_ptr dgdu(dgduT->Transpose()); + return dgdu; }; int numPressureDofs() const { return contact_.numPressureDofs(); } From e249df502cb5178e97ea8052cc1877321f543c06 Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 16 Sep 2025 13:24:03 -0700 Subject: [PATCH 08/82] implementation of untested but compiling TiedContactProblem that inherits from the NLMCProblem class in order to be used with the HomotopySolver --- examples/contact/constraint_twist.cpp | 282 ++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) diff --git a/examples/contact/constraint_twist.cpp b/examples/contact/constraint_twist.cpp index a5795bf4d..8903bf524 100644 --- a/examples/contact/constraint_twist.cpp +++ b/examples/contact/constraint_twist.cpp @@ -13,8 +13,84 @@ #include "mfem.hpp" +// ContinuationSolver headers +#include "problems/Problems.hpp" +#include "solvers/HomotopySolver.hpp" +#include "utilities.hpp" + #include "serac/serac.hpp" + +auto element_shape = mfem::Element::QUADRILATERAL; +static constexpr int dim = 3; +static constexpr int disp_order = 1; + +using VectorSpace = serac::H1; + +using DensitySpace = serac::L2; + +using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; +using SolidWeakFormT = serac::SolidWeakForm>; + +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 +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 y_partition; + std::vector contact_states; + std::vector all_states; + std::shared_ptr weak_form; + std::unique_ptr shape_disp; + std::shared_ptr mesh; + std::shared_ptr constraints; + double time = 0.0; + double dt = 0.0; + std::vector jacobian_weights = {1.0, 0.0, 0.0, 0.0}; + + public: + TiedContactProblem(std::vector contact_states_, std::vector all_states_, + std::shared_ptr mesh_, std::shared_ptr weak_form_, + std::shared_ptr 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[]) { @@ -85,3 +161,209 @@ int main(int argc, char* argv[]) return 0; } + +template +TiedContactProblem::TiedContactProblem(std::vector contact_states_, + std::vector all_states_, + std::shared_ptr mesh_, + std::shared_ptr weak_form_, + std::shared_ptr 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(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; + + int myid = mfem::Mpi::WorldRank(); + + 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 +void TiedContactProblem::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 +void TiedContactProblem::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 +mfem::HypreParMatrix* TiedContactProblem::DxF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dFdx; } + +template +mfem::HypreParMatrix* TiedContactProblem::DyF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dFdy; } + +template +mfem::HypreParMatrix* TiedContactProblem::DxQ(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dQdx; } + +template +mfem::HypreParMatrix* TiedContactProblem::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 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 +TiedContactProblem::~TiedContactProblem() +{ + delete[] uOffsets; + delete[] cOffsets; + delete dFdx; + delete dFdy; + delete dQdx; + delete dQdy; +} + From b8bd03cd3430f20135212849b81c41ad517c3b29 Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 16 Sep 2025 14:58:35 -0700 Subject: [PATCH 09/82] instantiating the TiedContactProblem --- examples/contact/constraint_twist.cpp | 84 ++++++++++++++++++++------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/examples/contact/constraint_twist.cpp b/examples/contact/constraint_twist.cpp index 8903bf524..db162d39f 100644 --- a/examples/contact/constraint_twist.cpp +++ b/examples/contact/constraint_twist.cpp @@ -21,7 +21,6 @@ #include "serac/serac.hpp" -auto element_shape = mfem::Element::QUADRILATERAL; static constexpr int dim = 3; static constexpr int disp_order = 1; @@ -97,12 +96,12 @@ 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; + //using VectorSpace = serac::H1; // Create DataStore std::string name = "contact_twist_example"; @@ -127,15 +126,24 @@ int main(int argc, char* argv[]) auto contact_interaction_id = 0; std::set surface_1_boundary_attributes({4}); std::set surface_2_boundary_attributes({5}); - serac::ContactConstraint contact_constraint(contact_interaction_id, mesh->mfemParMesh(), + auto contact_constraint = std::make_shared(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 contact_states; + std::vector states; + std::vector params; contact_states = {shape, disp}; + states = {disp, velo, accel}; + params = {density}; + + // initialize displacement contact_states[serac::ContactFields::DISP].setFromFieldFunction([](serac::tensor x) { auto u = 0.1 * x; @@ -143,18 +151,56 @@ int main(int argc, char* argv[]) }); 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(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(1)); + solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.0 * n; }); + + serac::tensor constant_force{}; + for (int i = 0; i < dim; i++) { + constant_force[i] = 1.e0; + } - 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); + solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { + return serac::tuple{constant_force, 0.0 * serac::get(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 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); @@ -201,8 +247,6 @@ TiedContactProblem::TiedContactProblem(std::vector Date: Tue, 16 Sep 2025 18:21:46 -0700 Subject: [PATCH 10/82] serac/smith convention of _ ending character for member variables and formatting --- examples/contact/constraint_twist.cpp | 240 +++++++++++++------------- 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/examples/contact/constraint_twist.cpp b/examples/contact/constraint_twist.cpp index db162d39f..cc74d7a09 100644 --- a/examples/contact/constraint_twist.cpp +++ b/examples/contact/constraint_twist.cpp @@ -20,7 +20,6 @@ #include "serac/serac.hpp" - static constexpr int dim = 3; static constexpr int disp_order = 1; @@ -57,30 +56,29 @@ enum FIELD template 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 y_partition; - std::vector contact_states; - std::vector all_states; - std::shared_ptr weak_form; - std::unique_ptr shape_disp; - std::shared_ptr mesh; - std::shared_ptr constraints; - double time = 0.0; - double dt = 0.0; - std::vector jacobian_weights = {1.0, 0.0, 0.0, 0.0}; + mfem::HypreParMatrix* dFdx_ = nullptr; + mfem::HypreParMatrix* dFdy_ = nullptr; + mfem::HypreParMatrix* dQdx_ = nullptr; + mfem::HypreParMatrix* dQdy_ = nullptr; + HYPRE_BigInt* uOffsets_ = nullptr; + HYPRE_BigInt* cOffsets_ = nullptr; + int dimu_; + int dimc_; + mfem::Array y_partition_; + std::vector contact_states_; + std::vector all_states_; + std::shared_ptr weak_form_; + std::unique_ptr shape_disp_; + std::shared_ptr mesh_; + std::shared_ptr constraints_; + double time_ = 0.0; + double dt_ = 0.0; + std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; public: - TiedContactProblem(std::vector contact_states_, std::vector all_states_, - std::shared_ptr mesh_, std::shared_ptr weak_form_, - std::shared_ptr constraints_); + TiedContactProblem(std::vector contact_states, std::vector all_states, + std::shared_ptr mesh, std::shared_ptr weak_form, + std::shared_ptr 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); @@ -97,11 +95,11 @@ int main(int argc, char* argv[]) serac::ApplicationManager applicationManager(argc, argv); //// NOTE: p must be equal to 1 to work with Tribol's mortar method - //constexpr int p = 1; + // constexpr int p = 1; //// NOTE: dim must be equal to 3 - //constexpr int dim = 3; + // constexpr int dim = 3; - //using VectorSpace = serac::H1; + // using VectorSpace = serac::H1; // Create DataStore std::string name = "contact_twist_example"; @@ -126,9 +124,9 @@ int main(int argc, char* argv[]) auto contact_interaction_id = 0; std::set surface_1_boundary_attributes({4}); std::set surface_2_boundary_attributes({5}); - auto contact_constraint = std::make_shared(contact_interaction_id, mesh->mfemParMesh(), - surface_1_boundary_attributes, surface_2_boundary_attributes, - contact_options, contact_constraint_name); + auto contact_constraint = std::make_shared( + 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()); @@ -142,8 +140,7 @@ int main(int argc, char* argv[]) contact_states = {shape, disp}; states = {disp, velo, accel}; params = {density}; - - + // initialize displacement contact_states[serac::ContactFields::DISP].setFromFieldFunction([](serac::tensor x) { auto u = 0.1 * x; @@ -154,7 +151,7 @@ int main(int argc, char* argv[]) states[FIELD::VELO] = 0.0; states[FIELD::ACCEL] = 0.0; params[0] = 1.0; - + std::string physics_name = "solid"; // construct residual @@ -180,27 +177,24 @@ int main(int argc, char* argv[]) return serac::tuple{constant_force, 0.0 * serac::get(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 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); - - - + TiedContactProblem 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); @@ -209,57 +203,51 @@ int main(int argc, char* argv[]) } template -TiedContactProblem::TiedContactProblem(std::vector contact_states_, - std::vector all_states_, - std::shared_ptr mesh_, - std::shared_ptr weak_form_, - std::shared_ptr constraints_) - : GeneralNLMCProblem(), - dFdx(nullptr), - dFdy(nullptr), - dQdx(nullptr), - dQdy(nullptr), - uOffsets(nullptr), - cOffsets(nullptr) +TiedContactProblem::TiedContactProblem(std::vector contact_states, + std::vector all_states, + std::shared_ptr mesh, + std::shared_ptr weak_form, + std::shared_ptr constraints) + : GeneralNLMCProblem() { - weak_form = weak_form_; - mesh = mesh_; - shape_disp = std::make_unique(mesh->newShapeDisplacement()); + weak_form_ = weak_form; + mesh_ = mesh; + shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); - constraints = constraints_; + constraints_ = constraints; - all_states.resize(all_states_.size()); - std::copy(all_states_.begin(), all_states_.end(), all_states.begin()); + 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()); + contact_states_.resize(contact_states.size()); + std::copy(contact_states.begin(), contact_states.end(), contact_states_.begin()); - int numConstraints = constraints->numPressureDofs(); + int numConstraints = constraints_->numPressureDofs(); - uOffsets = new HYPRE_BigInt[2]; - cOffsets = new HYPRE_BigInt[2]; + 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]; + 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(); + 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]; + dofOffsets[i] = uOffsets_[i] + cOffsets_[i]; } complementarityOffsets[0] = 0; complementarityOffsets[1] = 0; @@ -270,7 +258,7 @@ TiedContactProblem::TiedContactProblem(std::vector::TiedContactProblem(std::vector::TiedContactProblem(std::vector -void TiedContactProblem::F(const mfem::Vector& x, const mfem::Vector& y, mfem::Vector& feval, int& Feval_err) const +void TiedContactProblem::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"); @@ -305,28 +294,29 @@ void TiedContactProblem::F(const mfem::Vector& x, const mfem: // dQ / dy = [ K J^T] // [-J 0 ] template -void TiedContactProblem::Q(const mfem::Vector& x, const mfem::Vector& y, mfem::Vector& qeval, int& Qeval_err) const +void TiedContactProblem::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); + mfem::BlockVector yblock(y_partition_); yblock.Set(1.0, y); - mfem::BlockVector qblock(y_partition); + 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)); + contact_states_[serac::ContactFields::DISP]->Set(1.0, yblock.GetBlock(0)); + auto 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)); + 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; @@ -347,13 +337,22 @@ void TiedContactProblem::Q(const mfem::Vector& x, const mfem: } template -mfem::HypreParMatrix* TiedContactProblem::DxF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dFdx; } +mfem::HypreParMatrix* TiedContactProblem::DxF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) +{ + return dFdx_; +} template -mfem::HypreParMatrix* TiedContactProblem::DyF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dFdy; } +mfem::HypreParMatrix* TiedContactProblem::DyF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) +{ + return dFdy_; +} template -mfem::HypreParMatrix* TiedContactProblem::DxQ(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) { return dQdx; } +mfem::HypreParMatrix* TiedContactProblem::DxQ(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) +{ + return dQdx_; +} template mfem::HypreParMatrix* TiedContactProblem::DyQ(const mfem::Vector& /*x*/, const mfem::Vector& y) @@ -362,29 +361,31 @@ mfem::HypreParMatrix* TiedContactProblem::DyQ(const mfem::Vec // dQdy = [dr/du dc/du^T] // [dc/du 0 ] // note we are neglecting Hessian constraint terms - mfem::BlockVector yblock(y_partition); + mfem::BlockVector yblock(y_partition_); yblock.Set(1.0, y); - mfem::BlockVector qblock(y_partition); + mfem::BlockVector qblock(y_partition_); qblock = 0.0; - contact_states[serac::ContactFields::DISP]->Set(1.0, yblock.GetBlock(0)); - if (dQdy) { - delete dQdy; + 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"); + 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_unique = + constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP); auto negdcdu = negdcdu_unique.release(); - mfem::Vector scale(dimc); + 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 dcdutilde_unique = constraints_->jacobian_tilde(time_, dt_, serac::getConstFieldPointers(contact_states_), + serac::ContactFields::DISP); auto dcdutildeT = dcdutilde_unique->Transpose(); mfem::Array2D BlockMat(2, 2); @@ -392,22 +393,21 @@ mfem::HypreParMatrix* TiedContactProblem::DyQ(const mfem::Vec BlockMat(0, 1) = dcdutildeT; BlockMat(1, 0) = negdcdu; BlockMat(1, 1) = nullptr; - dQdy = HypreParMatrixFromBlocks(BlockMat); + dQdy_ = HypreParMatrixFromBlocks(BlockMat); delete drdu; delete dcdutildeT; delete negdcdu; } - return dQdy; + return dQdy_; } template TiedContactProblem::~TiedContactProblem() { - delete[] uOffsets; - delete[] cOffsets; - delete dFdx; - delete dFdy; - delete dQdx; - delete dQdy; + delete[] uOffsets_; + delete[] cOffsets_; + delete dFdx_; + delete dFdy_; + delete dQdx_; + delete dQdy_; } - From 417fcba741eddf4b188becc5618979fa752b3011 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 17 Sep 2025 12:46:45 -0700 Subject: [PATCH 11/82] moving constraint_twist to a examples/contact/homotopy as it is contact problem but I do not want to have to modify examples/contact/CMakeLists.txt so that they only run when CONTINUATION_SOLVERS is enabled. The example worked on here constraint_twist.cpp can only be run when CONTINUATION_SOLVERS is enabled so there is some natural separation at this point --- examples/CMakeLists.txt | 3 +++ examples/contact/CMakeLists.txt | 1 - examples/contact/homotopy/CMakeLists.txt | 22 +++++++++++++++++++ .../{ => homotopy}/constraint_twist.cpp | 0 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 examples/contact/homotopy/CMakeLists.txt rename examples/contact/{ => homotopy}/constraint_twist.cpp (100%) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 66d9b25fc..c7f430650 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -26,6 +26,9 @@ add_subdirectory(conduction) # Add the contact examples add_subdirectory(contact) +# Add the contact-homotopy examples +add_subdirectory(contact/homotopy) + # Add the buckling examples add_subdirectory(buckling) diff --git a/examples/contact/CMakeLists.txt b/examples/contact/CMakeLists.txt index ba873379c..79acabe48 100644 --- a/examples/contact/CMakeLists.txt +++ b/examples/contact/CMakeLists.txt @@ -10,7 +10,6 @@ if(TRIBOL_FOUND AND STRUMPACK_DIR) ironing.cpp sphere.cpp twist.cpp - constraint_twist.cpp ) foreach(filename ${CONTACT_EXAMPLES_SOURCES}) diff --git a/examples/contact/homotopy/CMakeLists.txt b/examples/contact/homotopy/CMakeLists.txt new file mode 100644 index 000000000..49a98e17c --- /dev/null +++ b/examples/contact/homotopy/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) Lawrence Livermore National Security, LLC and +# other Serac Project Developers. See the top-level LICENSE file for +# details. +# +# SPDX-License-Identifier: (BSD-3-Clause) + +if (TRIBOL_FOUND AND STRUMPACK_DIR AND SERAC_ENABLE_CONTINUATION) + blt_add_executable( NAME constraint_twist_example + SOURCES constraint_twist.cpp + OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} + DEPENDS_ON serac_physics serac_mesh_utils continuation_solver + ) + serac_add_example_test(NAME constraint_twist + COMMAND constraint_twist_example + NUM_MPI_TASKS 4) + install( + FILES + constraint_twist.cpp + DESTINATION + examples/serac/constraint_twist + ) +endif() diff --git a/examples/contact/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp similarity index 100% rename from examples/contact/constraint_twist.cpp rename to examples/contact/homotopy/constraint_twist.cpp From da16481b460c525435d0e272b15dc2522e147360 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 17 Sep 2025 12:58:44 -0700 Subject: [PATCH 12/82] removing unnecessary [[maybe_unused]] --- src/serac/physics/contact_constraint.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 11ec13caa..ed8d472e1 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -138,7 +138,7 @@ class ContactConstraint : public Constraint { * @return std::unique_ptr The true Jacobian */ std::unique_ptr jacobian(double time, double dt, const std::vector& fields, - [[maybe_unused]] int direction) const + int direction) const { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); @@ -165,7 +165,7 @@ class ContactConstraint : public Constraint { * @return std::Vector */ mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, - const mfem::Vector& multipliers, [[maybe_unused]] int direction) const + const mfem::Vector& multipliers, int direction) const { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); @@ -190,7 +190,7 @@ class ContactConstraint : public Constraint { std::unique_ptr residual_contribution_jacobian(double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, - [[maybe_unused]] int direction) const + int direction) const { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); @@ -215,7 +215,7 @@ class ContactConstraint : public Constraint { * @return std::unique_ptr */ std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, - [[maybe_unused]] int direction) const + int direction) const { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); From 0c93c33ac32605d908a4c726dac9de8e59f605f6 Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 19 Sep 2025 13:24:32 -0700 Subject: [PATCH 13/82] update to constraint_twist example problem. debugging state --- .../contact/homotopy/constraint_twist.cpp | 99 +++++++++++++++---- 1 file changed, 81 insertions(+), 18 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index cc74d7a09..ac1bece24 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -182,22 +182,80 @@ int main(int argc, char* argv[]) auto contact_state_ptrs = serac::getFieldPointers(contact_states); TiedContactProblem 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); + + //int dimx = problem.GetDimx(); + //int dimy = problem.GetDimy(); + + //double nonlinear_absolute_tol = 1.e-4; + //int nonlinear_max_iterations = 30; + //mfem::Vector x0(dimx); + //x0 = 0.0; + //mfem::Vector y0(dimy); + //y0 = 0.0; + //mfem::Vector xf(dimx); + //xf = 0.0; + //mfem::Vector yf(dimy); + //yf = 0.0; + + //mfem::Vector q0(dimy); + //mfem::Vector f0(dimx); + //int qeval_err, feval_err; + //problem.Q(x0, y0, q0, qeval_err); + //if (qeval_err) + //{ + // std::cout << "qeval_err\n"; + // exit(1); + //} + //problem.F(x0, y0, f0, feval_err); + //if (feval_err) + //{ + // std::cout << "feval_err\n"; + // exit(1); + //} + //problem.Q(x0, y0, q0, qeval_err); + //if (qeval_err) + //{ + // std::cout << "qeval_err\n"; + // exit(1); + //} + //problem.F(x0, y0, f0, feval_err); + //if (feval_err) + //{ + // std::cout << "feval_err\n"; + // exit(1); + //} + + //HomotopySolver solver(&problem); + //solver.SetTol(nonlinear_absolute_tol); + //solver.SetMaxIter(nonlinear_max_iterations); + + //solver.Mult(x0, y0, xf, yf); + //bool converged = solver.GetConverged(); + //int myid = mfem::Mpi::WorldRank(); + //if (myid == 0) { + // if (converged) { + // std::cout << "converged!\n"; + // } else { + // std::cout << "homotopy solver did not converge\n"; + // } + //} + + + double time = 0.0, dt = 1.0; + int direction = serac::ContactFields::DISP; + auto input_states = getConstFieldPointers(contact_states); + 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 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); + + + //auto residual_Jacobian = contact_constraint->residual_contribution_jacobian(time, dt, + // input_states, multipliers, + // direction); return 0; } @@ -306,12 +364,17 @@ void TiedContactProblem::Q(const mfem::Vector& x, const mfem: qblock = 0.0; contact_states_[serac::ContactFields::DISP]->Set(1.0, yblock.GetBlock(0)); + std::cout << "updated contact state\n"; auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); + std::cout << "residual computed\n"; + + // TODO: why does gap need to be computed prior to residaul contributio + auto gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_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_)); + std::cout << "residual contribution computed\n"; + std::cout << "gap computed\n"; qblock.GetBlock(0).Set(1.0, res_vector); qblock.GetBlock(0).Add(1.0, res_contribution); From 01ca98466812dbeed0263912f09b01ce5e7af963 Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 19 Sep 2025 14:16:05 -0700 Subject: [PATCH 14/82] swapping order of calls --- src/serac/physics/contact_constraint.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index ed8d472e1..e2a03fc31 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -194,8 +194,8 @@ class ContactConstraint : public Constraint { { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); - contact_.setPressures(multipliers); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); + contact_.setPressures(multipliers); int cycle = 0; contact_.update(cycle, time, dt); From 759a22fa980c9d6420d03ddcb5873d73a5ce7633 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 22 Sep 2025 16:17:33 -0700 Subject: [PATCH 15/82] small updates --- .../contact/homotopy/constraint_twist.cpp | 175 +++++++++--------- src/serac/physics/contact_constraint.hpp | 26 ++- 2 files changed, 109 insertions(+), 92 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index ac1bece24..de664fe17 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -20,6 +20,8 @@ #include "serac/serac.hpp" +#include + static constexpr int dim = 3; static constexpr int disp_order = 1; @@ -91,16 +93,11 @@ class TiedContactProblem : public GeneralNLMCProblem { // this example is intended to eventually replace twist.cpp int main(int argc, char* argv[]) { + + feenableexcept(FE_INVALID | FE_OVERFLOW); // 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; - - // using VectorSpace = serac::H1; - // Create DataStore std::string name = "contact_twist_example"; axom::sidre::DataStore datastore; @@ -143,7 +140,7 @@ int main(int argc, char* argv[]) // initialize displacement contact_states[serac::ContactFields::DISP].setFromFieldFunction([](serac::tensor x) { - auto u = 0.1 * x; + auto u = 0.0 * x;//0.1 * x; return u; }); @@ -182,81 +179,85 @@ int main(int argc, char* argv[]) auto contact_state_ptrs = serac::getFieldPointers(contact_states); TiedContactProblem problem(contact_state_ptrs, non_const_states, mesh, solid_mechanics_weak_form, contact_constraint); - - //int dimx = problem.GetDimx(); - //int dimy = problem.GetDimy(); - - //double nonlinear_absolute_tol = 1.e-4; - //int nonlinear_max_iterations = 30; - //mfem::Vector x0(dimx); - //x0 = 0.0; - //mfem::Vector y0(dimy); - //y0 = 0.0; - //mfem::Vector xf(dimx); - //xf = 0.0; - //mfem::Vector yf(dimy); - //yf = 0.0; - - //mfem::Vector q0(dimy); - //mfem::Vector f0(dimx); - //int qeval_err, feval_err; - //problem.Q(x0, y0, q0, qeval_err); - //if (qeval_err) - //{ - // std::cout << "qeval_err\n"; - // exit(1); - //} - //problem.F(x0, y0, f0, feval_err); - //if (feval_err) - //{ - // std::cout << "feval_err\n"; - // exit(1); - //} - //problem.Q(x0, y0, q0, qeval_err); - //if (qeval_err) - //{ - // std::cout << "qeval_err\n"; - // exit(1); - //} - //problem.F(x0, y0, f0, feval_err); - //if (feval_err) - //{ - // std::cout << "feval_err\n"; - // exit(1); - //} - - //HomotopySolver solver(&problem); - //solver.SetTol(nonlinear_absolute_tol); - //solver.SetMaxIter(nonlinear_max_iterations); - - //solver.Mult(x0, y0, xf, yf); - //bool converged = solver.GetConverged(); - //int myid = mfem::Mpi::WorldRank(); - //if (myid == 0) { - // if (converged) { - // std::cout << "converged!\n"; - // } else { - // std::cout << "homotopy solver did not converge\n"; - // } - //} - - - double time = 0.0, dt = 1.0; - int direction = serac::ContactFields::DISP; - auto input_states = getConstFieldPointers(contact_states); - 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 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); - - - //auto residual_Jacobian = contact_constraint->residual_contribution_jacobian(time, dt, - // input_states, multipliers, - // direction); - + if (false) + { + int dimx = problem.GetDimx(); + int dimy = problem.GetDimy(); + + double nonlinear_absolute_tol = 1.e-4; + int nonlinear_max_iterations = 30; + mfem::Vector x0(dimx); + x0 = 0.0; + mfem::Vector y0(dimy); + y0 = 0.0; + mfem::Vector xf(dimx); + xf = 0.0; + mfem::Vector yf(dimy); + yf = 0.0; + + // x, y + // y = [u, multipliers], x = {} + // HomotopySolver: + // F_i(x, y) * x_i = 0, F_i, x_i >= 0 + // Q(x, y) = 0 + mfem::Vector q0(dimy); q0 = 0.0; + mfem::Vector f0(dimx); f0 = 0.0; + int qeval_err, feval_err; + qeval_err = 0; + feval_err = 0; + problem.Q(x0, y0, q0, qeval_err); + if (qeval_err) + { + std::cout << "qeval_err\n"; + } + problem.F(x0, y0, f0, feval_err); + if (feval_err) + { + std::cout << "feval_err\n"; + } + + //HomotopySolver solver(&problem); + //solver.SetTol(nonlinear_absolute_tol); + //solver.SetMaxIter(nonlinear_max_iterations); + + //solver.Mult(x0, y0, xf, yf); + //bool converged = solver.GetConverged(); + //int myid = mfem::Mpi::WorldRank(); + //if (myid == 0) { + // if (converged) { + // std::cout << "converged!\n"; + // } else { + // std::cout << "homotopy solver did not converge\n"; + // } + //} + } + else + { + 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); + for (int i = 0; i < gap.Size(); i++) + { + if (std::isnan(gap(i))) + { + std::cout << "nan entry at " << i << std::endl; + } + } + 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); + double gnorm = mfem::GlobalLpNorm(2, gap.Norml2(), MPI_COMM_WORLD); + std::cout << "||g||_2 = " << gnorm << std::endl; + std::cout << "||dg / du||_F = " << gap_Jacobian->FNorm() << std::endl; + //std::cout << "||tilde(dg / du)||_F = " << gap_Jacobian_tilde->FNorm() << std::endl; + //auto residual_Jacobian = contact_constraint->residual_contribution_jacobian(time, dt, + // input_states, multipliers, + // direction); + } return 0; } @@ -364,18 +365,12 @@ void TiedContactProblem::Q(const mfem::Vector& x, const mfem: qblock = 0.0; contact_states_[serac::ContactFields::DISP]->Set(1.0, yblock.GetBlock(0)); - std::cout << "updated contact state\n"; auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); - std::cout << "residual computed\n"; - // TODO: why does gap need to be computed prior to residaul contributio - auto gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_)); auto res_contribution = constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), yblock.GetBlock(1), serac::ContactFields::DISP); - std::cout << "residual contribution computed\n"; - std::cout << "gap computed\n"; - + 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); diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index e2a03fc31..420bd9a5e 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -169,11 +169,22 @@ class ContactConstraint : public Constraint { { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); - contact_.setPressures(multipliers); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); int cycle = 0; + // we need to call update first to update gaps + for (auto& interaction : contact_.getContactInteractions()) { + interaction.evalJacobian(false); + } + contact_.update(cycle, time, dt); + // with updated gaps, we can update pressure for contact interactions with penalty enforcement + contact_.setPressures(multipliers); + // call update again with the right pressures + for (auto& interaction : contact_.getContactInteractions()) { + interaction.evalJacobian(true); + } contact_.update(cycle, time, dt); + return contact_.forces(); }; @@ -195,10 +206,21 @@ class ContactConstraint : public Constraint { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); - contact_.setPressures(multipliers); int cycle = 0; + // we need to call update first to update gaps + for (auto& interaction : contact_.getContactInteractions()) { + interaction.evalJacobian(false); + } + contact_.update(cycle, time, dt); + // with updated gaps, we can update pressure for contact interactions with penalty enforcement + contact_.setPressures(multipliers); + // call update again with the right pressures + for (auto& interaction : contact_.getContactInteractions()) { + interaction.evalJacobian(true); + } contact_.update(cycle, time, dt); + auto J_contact = contact_.mergedJacobian(); int iblock = 0; int jblock = 0; From 7cfaacea1ff7032bda96404e90b3763aab6c410f Mon Sep 17 00:00:00 2001 From: "E. B. Chin" Date: Tue, 23 Sep 2025 16:07:33 -0700 Subject: [PATCH 16/82] temp fix for evaluate --- src/serac/physics/contact_constraint.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 420bd9a5e..4b3c4c899 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -126,7 +126,11 @@ class ContactConstraint : public Constraint { // note: Tribol does not use cycle. int cycle = 0; contact_.update(cycle, time, dt); - return contact_.mergedGaps(false); + auto merged_gaps = contact_.mergedGaps(false); + merged_gaps.SetOwnership(false); + mfem::Vector merged_gaps_vector = std::move(merged_gaps); + merged_gaps_vector.MakeDataOwner(); + return merged_gaps_vector; }; /** @brief Interface for computing contact gap constraint Jacobian from a vector of serac::FiniteElementState* From 870cddab95eb6649c54e8fa09bcf920f28235d3d Mon Sep 17 00:00:00 2001 From: thartland Date: Sun, 12 Oct 2025 15:22:07 -0700 Subject: [PATCH 17/82] working TiedContact via EqualityConstrainedHomotopyProblem --- .../contact/homotopy/constraint_twist.cpp | 381 ++++++------------ 1 file changed, 126 insertions(+), 255 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index de664fe17..9ac5c9e46 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -20,8 +20,6 @@ #include "serac/serac.hpp" -#include - static constexpr int dim = 3; static constexpr int disp_order = 1; @@ -56,14 +54,10 @@ enum FIELD * and thus the NLMC problem is guaranteed to be semi-monotone. */ template -class TiedContactProblem : public GeneralNLMCProblem { +class TiedContactProblem : public EqualityConstrainedHomotopyProblem { protected: - mfem::HypreParMatrix* dFdx_ = nullptr; - mfem::HypreParMatrix* dFdy_ = nullptr; - mfem::HypreParMatrix* dQdx_ = nullptr; - mfem::HypreParMatrix* dQdy_ = nullptr; - HYPRE_BigInt* uOffsets_ = nullptr; - HYPRE_BigInt* cOffsets_ = nullptr; + mfem::HypreParMatrix* drdu_ = nullptr; + mfem::HypreParMatrix* dcdu_ = nullptr; int dimu_; int dimc_; mfem::Array y_partition_; @@ -81,20 +75,17 @@ class TiedContactProblem : public GeneralNLMCProblem { TiedContactProblem(std::vector contact_states, std::vector all_states, std::shared_ptr mesh, std::shared_ptr weak_form, std::shared_ptr 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); + mfem::Vector residual(const mfem::Vector& u) const; + mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u); + mfem::Vector constraint(const mfem::Vector& u) const; + mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u); + mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l) const; virtual ~TiedContactProblem(); }; // this example is intended to eventually replace twist.cpp int main(int argc, char* argv[]) { - - feenableexcept(FE_INVALID | FE_OVERFLOW); // Initialize and automatically finalize MPI and other libraries serac::ApplicationManager applicationManager(argc, argv); @@ -140,7 +131,7 @@ int main(int argc, char* argv[]) // initialize displacement contact_states[serac::ContactFields::DISP].setFromFieldFunction([](serac::tensor x) { - auto u = 0.0 * x;//0.1 * x; + auto u = 0.0 * x; // 0.1 * x; return u; }); @@ -179,84 +170,79 @@ int main(int argc, char* argv[]) auto contact_state_ptrs = serac::getFieldPointers(contact_states); TiedContactProblem problem(contact_state_ptrs, non_const_states, mesh, solid_mechanics_weak_form, contact_constraint); - if (false) - { - int dimx = problem.GetDimx(); - int dimy = problem.GetDimy(); - - double nonlinear_absolute_tol = 1.e-4; - int nonlinear_max_iterations = 30; - mfem::Vector x0(dimx); - x0 = 0.0; - mfem::Vector y0(dimy); - y0 = 0.0; - mfem::Vector xf(dimx); - xf = 0.0; - mfem::Vector yf(dimy); - yf = 0.0; - - // x, y - // y = [u, multipliers], x = {} - // HomotopySolver: - // F_i(x, y) * x_i = 0, F_i, x_i >= 0 - // Q(x, y) = 0 - mfem::Vector q0(dimy); q0 = 0.0; - mfem::Vector f0(dimx); f0 = 0.0; - int qeval_err, feval_err; - qeval_err = 0; - feval_err = 0; - problem.Q(x0, y0, q0, qeval_err); - if (qeval_err) - { - std::cout << "qeval_err\n"; - } - problem.F(x0, y0, f0, feval_err); - if (feval_err) - { - std::cout << "feval_err\n"; - } - - //HomotopySolver solver(&problem); - //solver.SetTol(nonlinear_absolute_tol); - //solver.SetMaxIter(nonlinear_max_iterations); - - //solver.Mult(x0, y0, xf, yf); - //bool converged = solver.GetConverged(); - //int myid = mfem::Mpi::WorldRank(); - //if (myid == 0) { - // if (converged) { - // std::cout << "converged!\n"; - // } else { - // std::cout << "homotopy solver did not converge\n"; - // } - //} - } - else - { - 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); - for (int i = 0; i < gap.Size(); i++) - { - if (std::isnan(gap(i))) - { - std::cout << "nan entry at " << i << std::endl; - } - } - 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); - double gnorm = mfem::GlobalLpNorm(2, gap.Norml2(), MPI_COMM_WORLD); - std::cout << "||g||_2 = " << gnorm << std::endl; - std::cout << "||dg / du||_F = " << gap_Jacobian->FNorm() << std::endl; - //std::cout << "||tilde(dg / du)||_F = " << gap_Jacobian_tilde->FNorm() << std::endl; - //auto residual_Jacobian = contact_constraint->residual_contribution_jacobian(time, dt, - // input_states, multipliers, - // direction); + if (false) { + int dimx = problem.GetDimx(); + int dimy = problem.GetDimy(); + + double nonlinear_absolute_tol = 1.e-4; + int nonlinear_max_iterations = 30; + mfem::Vector x0(dimx); + x0 = 0.0; + mfem::Vector y0(dimy); + y0 = 0.0; + mfem::Vector xf(dimx); + xf = 0.0; + mfem::Vector yf(dimy); + yf = 0.0; + + // x, y + // y = [u, multipliers], x = {} + // HomotopySolver: + // F_i(x, y) * x_i = 0, F_i, x_i >= 0 + // Q(x, y) = 0 + mfem::Vector q0(dimy); + q0 = 0.0; + mfem::Vector f0(dimx); + f0 = 0.0; + int qeval_err, feval_err; + qeval_err = 0; + feval_err = 0; + problem.Q(x0, y0, q0, qeval_err); + if (qeval_err) { + std::cout << "qeval_err\n"; + } + problem.F(x0, y0, f0, feval_err); + if (feval_err) { + std::cout << "feval_err\n"; + } + + // HomotopySolver solver(&problem); + // solver.SetTol(nonlinear_absolute_tol); + // solver.SetMaxIter(nonlinear_max_iterations); + + // solver.Mult(x0, y0, xf, yf); + // bool converged = solver.GetConverged(); + // int myid = mfem::Mpi::WorldRank(); + // if (myid == 0) { + // if (converged) { + // std::cout << "converged!\n"; + // } else { + // std::cout << "homotopy solver did not converge\n"; + // } + // } + } else { + double time = 0.0, dt = 1.0; + int direction = serac::ContactFields::DISP; + auto input_states = serac::getConstFieldPointers(contact_states); + auto gap = contact_constraint->evaluate(time, dt, input_states); + for (int i = 0; i < gap.Size(); i++) { + if (std::isnan(gap(i))) { + std::cout << "nan entry at " << i << std::endl; + } + } + 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); + double gnorm = mfem::GlobalLpNorm(2, gap.Norml2(), MPI_COMM_WORLD); + std::cout << "||g||_2 = " << gnorm << std::endl; + std::cout << "||dg / du||_F = " << gap_Jacobian->FNorm() << std::endl; + // std::cout << "||tilde(dg / du)||_F = " << gap_Jacobian_tilde->FNorm() << std::endl; + // auto residual_Jacobian = contact_constraint->residual_contribution_jacobian(time, dt, + // input_states, multipliers, + // direction); } return 0; } @@ -267,7 +253,7 @@ TiedContactProblem::TiedContactProblem(std::vector mesh, std::shared_ptr weak_form, std::shared_ptr constraints) - : GeneralNLMCProblem() + : EqualityConstrainedHomotopyProblem() { weak_form_ = weak_form; mesh_ = mesh; @@ -281,191 +267,76 @@ TiedContactProblem::TiedContactProblem(std::vectornumPressureDofs(); - - 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; - } + HYPRE_BigInt* uOffsets = all_states[FIELD::DISP]->space().GetTrueDofOffsets(); - // dF / dy 0 x dimy matrix - { - int nentries = 0; - auto temp = new mfem::SparseMatrix(dimx, dimyglb, nentries); - dFdy_ = GenerateHypreParMatrixFromSparseMatrix(dofOffsetsy, dofOffsetsx, temp); - delete temp; - } + HYPRE_BigInt nPressureLoc = constraints_->numPressureDofs(); + HYPRE_BigInt pressure_offset = 0; + MPI_Scan(&nPressureLoc, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + HYPRE_BigInt cOffsets[2]; + cOffsets[1] = pressure_offset; + cOffsets[0] = pressure_offset - nPressureLoc; - // dQ / dx dimy x 0 matrix - { - int nentries = 0; - auto temp = new mfem::SparseMatrix(dimy, dimxglb, nentries); - dQdx_ = GenerateHypreParMatrixFromSparseMatrix(dofOffsetsx, dofOffsetsy, temp); - delete temp; - } + // HYPRE_BigInt* cOffsets = constraints_->pressureSpace().GetTrueDofOffsets(); + SetSizes(uOffsets, cOffsets); } template -void TiedContactProblem::F(const mfem::Vector& x, const mfem::Vector& y, mfem::Vector& feval, - int& Feval_err) const +mfem::Vector TiedContactProblem::residual(const mfem::Vector& u) const { - MFEM_VERIFY(x.Size() == dimx && y.Size() == dimy && feval.Size() == dimx, - "TiedContactProblem::F -- Inconsistent dimensions"); - feval = 0.0; - Feval_err = 0; + contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); + return res_vector; } -// Q = [ r + J^T l] -// [ -c ] -// dQ / dy = [ K J^T] -// [-J 0 ] template -void TiedContactProblem::Q(const mfem::Vector& x, const mfem::Vector& y, mfem::Vector& qeval, - int& Qeval_err) const +mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u) { - 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)); - auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); - + contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + auto drdu_unique = + weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); + MFEM_VERIFY(drdu_unique->Height() == dimu_, "size error"); - 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 (drdu_) { + delete drdu_; } - if (Qeval_err > 0 && mfem::Mpi::WorldRank() == 0) { - std::cout << "at least one nan entry\n"; - } -} - -template -mfem::HypreParMatrix* TiedContactProblem::DxF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) -{ - return dFdx_; + drdu_ = drdu_unique.release(); + return drdu_; } template -mfem::HypreParMatrix* TiedContactProblem::DyF(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) +mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u) const { - return dFdy_; + contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + auto gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_)); + return gap; } template -mfem::HypreParMatrix* TiedContactProblem::DxQ(const mfem::Vector& /*x*/, const mfem::Vector& /*y*/) +mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u) { - return dQdx_; + contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + auto dcdu_unique = + constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP); + if (dcdu_) { + delete dcdu_; + } + dcdu_ = dcdu_unique.release(); + return dcdu_; } template -mfem::HypreParMatrix* TiedContactProblem::DyQ(const mfem::Vector& /*x*/, const mfem::Vector& y) +mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, + const mfem::Vector& l) const { - 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 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_; + contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + auto res_contribution = constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), + l, serac::ContactFields::DISP); + return res_contribution; } template TiedContactProblem::~TiedContactProblem() { - delete[] uOffsets_; - delete[] cOffsets_; - delete dFdx_; - delete dFdy_; - delete dQdx_; - delete dQdy_; + delete dcdu_; + delete drdu_; } From 6cd27b6e75e5f9e7690e5ef7d319d826da17c39b Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 13 Oct 2025 11:18:54 -0700 Subject: [PATCH 18/82] clean up --- .../contact/homotopy/constraint_twist.cpp | 132 +++++++----------- 1 file changed, 51 insertions(+), 81 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 9ac5c9e46..c06c588ea 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -38,7 +38,8 @@ enum FIELD DENSITY = SolidWeakFormT::NUM_STATES }; -/* NLMCP of the form +/* Nonlinear mixed complementarity problem (NLMCP) + * of the form * 0 <= x \perp F(x, y) >= 0 * Q(x, y) = 0 * Here, F and x are both 0-dimensional @@ -46,12 +47,15 @@ enum FIELD * [-c(u)] * y = [ u ] * [ l ] + * + * wherein r(u) is the elasticity nonlinear residual + * c(u) are the tied gap contacts + * u are the displacement dofs + * l are the Lagrange multipliers + * * 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 class TiedContactProblem : public EqualityConstrainedHomotopyProblem { @@ -170,79 +174,42 @@ int main(int argc, char* argv[]) auto contact_state_ptrs = serac::getFieldPointers(contact_states); TiedContactProblem problem(contact_state_ptrs, non_const_states, mesh, solid_mechanics_weak_form, contact_constraint); - if (false) { - int dimx = problem.GetDimx(); - int dimy = problem.GetDimy(); - + if (true) { double nonlinear_absolute_tol = 1.e-4; - int nonlinear_max_iterations = 30; - mfem::Vector x0(dimx); - x0 = 0.0; - mfem::Vector y0(dimy); - y0 = 0.0; - mfem::Vector xf(dimx); - xf = 0.0; - mfem::Vector yf(dimy); - yf = 0.0; - - // x, y - // y = [u, multipliers], x = {} - // HomotopySolver: - // F_i(x, y) * x_i = 0, F_i, x_i >= 0 - // Q(x, y) = 0 - mfem::Vector q0(dimy); - q0 = 0.0; - mfem::Vector f0(dimx); - f0 = 0.0; - int qeval_err, feval_err; - qeval_err = 0; - feval_err = 0; - problem.Q(x0, y0, q0, qeval_err); - if (qeval_err) { - std::cout << "qeval_err\n"; - } - problem.F(x0, y0, f0, feval_err); - if (feval_err) { - std::cout << "feval_err\n"; - } - - // HomotopySolver solver(&problem); - // solver.SetTol(nonlinear_absolute_tol); - // solver.SetMaxIter(nonlinear_max_iterations); - - // solver.Mult(x0, y0, xf, yf); - // bool converged = solver.GetConverged(); - // int myid = mfem::Mpi::WorldRank(); - // if (myid == 0) { - // if (converged) { - // std::cout << "converged!\n"; - // } else { - // std::cout << "homotopy solver did not converge\n"; + int nonlinear_max_iterations = 2; + // optimization variables + auto X0 = problem.GetOptimizationVariable(); + auto Xf = problem.GetOptimizationVariable(); + + HomotopySolver solver(&problem); + solver.SetTol(nonlinear_absolute_tol); + solver.SetMaxIter(nonlinear_max_iterations); + solver.Mult(X0, Xf); + bool converged = solver.GetConverged(); + SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); + } else { + // double time = 0.0, dt = 1.0; + // int direction = serac::ContactFields::DISP; + // auto input_states = serac::getConstFieldPointers(contact_states); + // auto gap = contact_constraint->evaluate(time, dt, input_states); + // for (int i = 0; i < gap.Size(); i++) { + // if (std::isnan(gap(i))) { + // std::cout << "nan entry at " << i << std::endl; // } // } - } else { - double time = 0.0, dt = 1.0; - int direction = serac::ContactFields::DISP; - auto input_states = serac::getConstFieldPointers(contact_states); - auto gap = contact_constraint->evaluate(time, dt, input_states); - for (int i = 0; i < gap.Size(); i++) { - if (std::isnan(gap(i))) { - std::cout << "nan entry at " << i << std::endl; - } - } - auto gap_Jacobian = contact_constraint->jacobian(time, dt, input_states, direction); + // 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); - double gnorm = mfem::GlobalLpNorm(2, gap.Norml2(), MPI_COMM_WORLD); - std::cout << "||g||_2 = " << gnorm << std::endl; - std::cout << "||dg / du||_F = " << gap_Jacobian->FNorm() << std::endl; - // std::cout << "||tilde(dg / du)||_F = " << gap_Jacobian_tilde->FNorm() << std::endl; - // auto residual_Jacobian = contact_constraint->residual_contribution_jacobian(time, dt, - // input_states, multipliers, - // direction); + // double gnorm = mfem::GlobalLpNorm(2, gap.Norml2(), MPI_COMM_WORLD); + // std::cout << "||g||_2 = " << gnorm << std::endl; + // std::cout << "||dg / du||_F = " << gap_Jacobian->FNorm() << std::endl; + // std::cout << "||tilde(dg / du)||_F = " << gap_Jacobian_tilde->FNorm() << std::endl; + // auto residual_Jacobian = contact_constraint->residual_contribution_jacobian(time, dt, + // input_states, multipliers, + // direction); } return 0; } @@ -253,31 +220,33 @@ TiedContactProblem::TiedContactProblem(std::vector mesh, std::shared_ptr weak_form, std::shared_ptr constraints) - : EqualityConstrainedHomotopyProblem() + : EqualityConstrainedHomotopyProblem(), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) { - weak_form_ = weak_form; - mesh_ = mesh; - shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); - - constraints_ = constraints; - + // copy states all_states_.resize(all_states.size()); std::copy(all_states.begin(), all_states.end(), all_states_.begin()); + // copy contact states contact_states_.resize(contact_states.size()); std::copy(contact_states.begin(), contact_states.end(), contact_states_.begin()); + // obtain displacement dof information HYPRE_BigInt* uOffsets = all_states[FIELD::DISP]->space().GetTrueDofOffsets(); + dimu_ = all_states[FIELD::DISP]->space().GetTrueVSize(); - HYPRE_BigInt nPressureLoc = constraints_->numPressureDofs(); + // obtain pressure dof information + dimc_ = constraints_->numPressureDofs(); HYPRE_BigInt pressure_offset = 0; - MPI_Scan(&nPressureLoc, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + MPI_Scan(&dimc_, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); HYPRE_BigInt cOffsets[2]; cOffsets[1] = pressure_offset; - cOffsets[0] = pressure_offset - nPressureLoc; + cOffsets[0] = pressure_offset - dimc_; - // HYPRE_BigInt* cOffsets = constraints_->pressureSpace().GetTrueDofOffsets(); + // set pressure and displacement dof information SetSizes(uOffsets, cOffsets); + + // shape_disp field + shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); } template @@ -294,7 +263,8 @@ mfem::HypreParMatrix* TiedContactProblem::residualJacobian(co contact_states_[serac::ContactFields::DISP]->Set(1.0, u); auto drdu_unique = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); - MFEM_VERIFY(drdu_unique->Height() == dimu_, "size error"); + MFEM_VERIFY(drdu_unique->Height() == dimu_, + "weak form Jacobian/TiedContactProblem displacement dofs inconsistent sizes"); if (drdu_) { delete drdu_; From 87ce874d0cfd3581c76e8174c1d934f99398749f Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 13 Oct 2025 12:09:00 -0700 Subject: [PATCH 19/82] delete free/raw pointer avoidance inertia relief example --- .../inertia_relief/inertia_relief_example.cpp | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index 167ff9e96..cd294b9bd 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -128,8 +128,8 @@ auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector drdu_; + std::unique_ptr dcdu_; int dimu_; int dimc_; int dimuglb_; @@ -413,15 +413,11 @@ mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector& u) { obj_states_[DISP]->Set(1.0, u); - auto drdu_unique = - weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); + drdu_.reset(); + drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); - if (drdu_) { - delete drdu_; - } - drdu_ = drdu_unique.release(); SLIC_ERROR_ROOT_IF(drdu_->Height() != dimu_ || drdu_->Width() != dimu_, "residual Jacobian of an unexpected shape"); - return drdu_; + return drdu_.get(); } // constraint callback @@ -460,6 +456,7 @@ mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vect for (int i = 0; i < dimuglb_; i++) { cols[i] = i; } + std::unique_ptr globalGradVector; for (size_t i = 0; i < constraints_.size(); i++) { const int idx = static_cast(i); const size_t i2 = static_cast(idx); @@ -467,27 +464,15 @@ mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vect mfem::HypreParVector gradVector(MPI_COMM_WORLD, dimuglb_, uOffsets_); gradVector.Set( 1.0, constraints_[i]->gradient(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(obj_states_), DISP)); - mfem::Vector* globalGradVector = gradVector.GlobalVector(); + globalGradVector.reset(gradVector.GlobalVector()); if (myid == 0) { - dcdumat.SetRow(idx, cols, *globalGradVector); + dcdumat.SetRow(idx, cols, *globalGradVector.get()); } - delete globalGradVector; } dcdumat.Threshold(1.e-20); dcdumat.Finalize(); - if (dcdu_) { - delete dcdu_; - } - dcdu_ = GenerateHypreParMatrixFromSparseMatrix(uOffsets_, cOffsets_, &dcdumat); - return dcdu_; + dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(uOffsets_, cOffsets_, &dcdumat)); + return dcdu_.get(); } -InertialReliefProblem::~InertialReliefProblem() -{ - if (drdu_) { - delete drdu_; - } - if (dcdu_) { - delete dcdu_; - } -} +InertialReliefProblem::~InertialReliefProblem() {} From 02e12b2ee15acc8cca575e382f3192832d7f3b2c Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 13 Oct 2025 12:17:16 -0700 Subject: [PATCH 20/82] delete free/raw pointer avoidance in homotopy/contact example --- .../contact/homotopy/constraint_twist.cpp | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index c06c588ea..81749ea14 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -60,8 +60,8 @@ enum FIELD template class TiedContactProblem : public EqualityConstrainedHomotopyProblem { protected: - mfem::HypreParMatrix* drdu_ = nullptr; - mfem::HypreParMatrix* dcdu_ = nullptr; + std::unique_ptr drdu_; + std::unique_ptr dcdu_; int dimu_; int dimc_; mfem::Array y_partition_; @@ -261,16 +261,11 @@ template mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u) { contact_states_[serac::ContactFields::DISP]->Set(1.0, u); - auto drdu_unique = - weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); - MFEM_VERIFY(drdu_unique->Height() == dimu_, - "weak form Jacobian/TiedContactProblem displacement dofs inconsistent sizes"); - - if (drdu_) { - delete drdu_; - } - drdu_ = drdu_unique.release(); - return drdu_; + drdu_.reset(); + drdu_ = std::move( + weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_)); + MFEM_VERIFY(drdu_->Height() == dimu_, "weak form Jacobian/TiedContactProblem displacement dofs inconsistent sizes"); + return drdu_.get(); } template @@ -285,13 +280,10 @@ template mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u) { contact_states_[serac::ContactFields::DISP]->Set(1.0, u); - auto dcdu_unique = - constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP); - if (dcdu_) { - delete dcdu_; - } - dcdu_ = dcdu_unique.release(); - return dcdu_; + dcdu_.reset(); + dcdu_ = std::move( + constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP)); + return dcdu_.get(); } template @@ -307,6 +299,4 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const template TiedContactProblem::~TiedContactProblem() { - delete dcdu_; - delete drdu_; } From 3d54eb321a5e8a17b06b62659a945c840eb905e5 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 13 Oct 2025 12:57:11 -0700 Subject: [PATCH 21/82] minor removal of std::move --- examples/contact/homotopy/constraint_twist.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 81749ea14..50ea69a4a 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -262,8 +262,7 @@ mfem::HypreParMatrix* TiedContactProblem::residualJacobian(co { contact_states_[serac::ContactFields::DISP]->Set(1.0, u); drdu_.reset(); - drdu_ = std::move( - weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_)); + drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); MFEM_VERIFY(drdu_->Height() == dimu_, "weak form Jacobian/TiedContactProblem displacement dofs inconsistent sizes"); return drdu_.get(); } From 1556b8a76a3a5f6a5f18fb0e966e5bb5bafba346 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 13 Oct 2025 13:31:30 -0700 Subject: [PATCH 22/82] small std::move change --- examples/contact/homotopy/constraint_twist.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 50ea69a4a..0d9bdc8bb 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -280,8 +280,7 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( { contact_states_[serac::ContactFields::DISP]->Set(1.0, u); dcdu_.reset(); - dcdu_ = std::move( - constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP)); + dcdu_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP); return dcdu_.get(); } From 345cc7c13d0f8e1bd823f318d2333731617c0247 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 13 Oct 2025 15:09:04 -0700 Subject: [PATCH 23/82] ContinuationSolvers with adjoint solve, cleaning up other else testing branch --- ContinuationSolvers | 2 +- .../contact/homotopy/constraint_twist.cpp | 34 +++++++------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 14048e7c7..5dd8b6b7d 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 14048e7c714236127666712a07d32d5eb8b0f435 +Subproject commit 5dd8b6b7dc56102ab52f1b2905e8097e5899805e diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 0d9bdc8bb..74649466a 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -188,28 +188,18 @@ int main(int argc, char* argv[]) bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); } else { - // double time = 0.0, dt = 1.0; - // int direction = serac::ContactFields::DISP; - // auto input_states = serac::getConstFieldPointers(contact_states); - // auto gap = contact_constraint->evaluate(time, dt, input_states); - // for (int i = 0; i < gap.Size(); i++) { - // if (std::isnan(gap(i))) { - // std::cout << "nan entry at " << i << std::endl; - // } - // } - // 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); - // double gnorm = mfem::GlobalLpNorm(2, gap.Norml2(), MPI_COMM_WORLD); - // std::cout << "||g||_2 = " << gnorm << std::endl; - // std::cout << "||dg / du||_F = " << gap_Jacobian->FNorm() << std::endl; - // std::cout << "||tilde(dg / du)||_F = " << gap_Jacobian_tilde->FNorm() << std::endl; - // auto residual_Jacobian = contact_constraint->residual_contribution_jacobian(time, dt, - // input_states, multipliers, - // direction); + auto dimu = problem.GetDisplacementDim(); + mfem::Vector u(dimu); + u = 0.0; + auto gap = problem.constraint(u); + auto gapJacobian = problem.constraintJacobian(u); + double gapJacobianFNorm = gapJacobian->FNorm(); + if (gapJacobianFNorm > 1.e-12) { + auto adjoint = problem.GetOptimizationVariable(); + auto adjoint_load = problem.GetOptimizationVariable(); + adjoint_load = 1.0; + problem.AdjointSolve(u, adjoint_load, adjoint); + } } return 0; } From b70cf4bf40d37335098a63ab9a5e91ff5cd5e3a7 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 15 Oct 2025 11:48:30 -0700 Subject: [PATCH 24/82] updating ContinuationSolver branch, adding in print_level for the in progress constraint_twist example --- ContinuationSolvers | 2 +- examples/contact/homotopy/constraint_twist.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 5dd8b6b7d..e0c585d1c 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 5dd8b6b7dc56102ab52f1b2905e8097e5899805e +Subproject commit e0c585d1c8aa7b2885d883af7dca52f0e37fde3e diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 74649466a..0a9b1a213 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -176,7 +176,8 @@ int main(int argc, char* argv[]) contact_constraint); if (true) { double nonlinear_absolute_tol = 1.e-4; - int nonlinear_max_iterations = 2; + int nonlinear_max_iterations = 30; + int nonlinear_print_level = 1; // optimization variables auto X0 = problem.GetOptimizationVariable(); auto Xf = problem.GetOptimizationVariable(); @@ -184,6 +185,7 @@ int main(int argc, char* argv[]) HomotopySolver solver(&problem); solver.SetTol(nonlinear_absolute_tol); solver.SetMaxIter(nonlinear_max_iterations); + solver.SetPrintLevel(nonlinear_print_level); solver.Mult(X0, Xf); bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); From 78ed2ea53a7fdf6b24cb9d4b2bc9c7ea307cda68 Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 17 Oct 2025 11:24:10 -0700 Subject: [PATCH 25/82] moving forward with a function argument in which we specify whether or not the computation is to be done at a new point or a point previously evaluated in order to cache computations, especially with regard to contact mechanics solves --- src/serac/physics/constraint.hpp | 25 ++++++++++++++-------- src/serac/physics/contact_constraint.hpp | 27 +++++++++++++++--------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/serac/physics/constraint.hpp b/src/serac/physics/constraint.hpp index d2b670003..19a10a699 100644 --- a/src/serac/physics/constraint.hpp +++ b/src/serac/physics/constraint.hpp @@ -41,9 +41,11 @@ class Constraint { * @param time time * @param dt time step * @param fields vector of serac::FiniteElementState* + * @param new_point boolean indicating if this is a new point or not * @return mfem::Vector which is the constraint evaluation */ - virtual mfem::Vector evaluate(double time, double dt, const std::vector& fields) const = 0; + virtual mfem::Vector evaluate(double time, double dt, const std::vector& fields, + bool new_point = true) const = 0; /** @brief Virtual interface for computing constraint Jacobian from a vector of serac::FiniteElementState* * @@ -51,11 +53,12 @@ class Constraint { * @param dt time step * @param fields vector of serac::FiniteElementState* * @param direction index for which field to take the gradient with respect to + * @param new_point boolean indicating if this is a new point or not * @return std::unique_ptr */ virtual std::unique_ptr jacobian(double time, double dt, - const std::vector& fields, - int direction) const = 0; + const std::vector& fields, int direction, + bool new_point = true) const = 0; /** @brief Virtual interface for computing constraint Jacobian_tilde from a vector of serac::FiniteElementState* * Jacobian_tilde is an optional approximation of the true Jacobian @@ -64,13 +67,14 @@ class Constraint { * @param dt time step * @param fields vector of serac::FiniteElementState* * @param direction index for which field to take the gradient with respect to + * @param new_point boolean indicating if this is a new point or not * @return std::unique_ptr */ virtual std::unique_ptr jacobian_tilde(double time, double dt, - const std::vector& fields, - int direction) const + const std::vector& fields, int direction, + bool new_point = true) const { - return jacobian(time, dt, fields, direction); + return jacobian(time, dt, fields, direction, new_point); }; /** @brief Virtual interface for computing residual contribution Jacobian_tilde^T multiplier from a vector of @@ -81,12 +85,14 @@ class Constraint { * @param fields vector of serac::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to + * @param new_point boolean indicating if this is a new point or not * @return std::Vector */ virtual mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, - const mfem::Vector& multipliers, int direction) const + const mfem::Vector& multipliers, int direction, + bool new_point = true) const { - std::unique_ptr jac = jacobian_tilde(time, dt, fields, direction); + std::unique_ptr jac = jacobian_tilde(time, dt, fields, direction, new_point); mfem::Vector y(jac->Width()); y = 0.0; SLIC_ERROR_ROOT_IF(jac->Height() != multipliers.Size(), "Incompatible matrix and vector sizes."); @@ -102,12 +108,13 @@ class Constraint { * @param fields vector of serac::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to + * @param new_point boolean indicating if this is a new point or not * @return std::unique_ptr */ virtual std::unique_ptr residual_contribution_jacobian( [[maybe_unused]] double time, [[maybe_unused]] double dt, [[maybe_unused]] const std::vector& fields, [[maybe_unused]] const mfem::Vector& multipliers, - [[maybe_unused]] int direction) const + [[maybe_unused]] int direction, [[maybe_unused]] bool new_point = true) const { SLIC_ERROR_ROOT(axom::fmt::format("Base class must override residual_contribution_jacobian before usage")); std::unique_ptr res_contr_jacobian = nullptr; diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 34068c864..1cb5acf55 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -70,7 +70,7 @@ static std::unique_ptr safelyObtainBlock(mfem::BlockOperat } } auto Ablk = dynamic_cast(&block_operator->GetBlock(iblock, jblock)); - SLIC_ERROR_IF(!Ablk, "failed cast to mfem::HypreParMatrix"); + SLIC_ERROR_IF(!Ablk, "failed cast block to mfem::HypreParMatrix"); std::unique_ptr Ablk_unique(Ablk); return Ablk_unique; }; @@ -80,7 +80,7 @@ class FiniteElementState; /** * @brief A ContactConstraint defines a gap constraint associated to contact problem * - * This class stores the details of a single contact interaction between two surfaces. It also interfaces provides a + * This class stores the details of a single contact interaction between two surfaces. It also provides a * description of a contact constraint given by a single contact interaction. A ContactConstraint can have a single * ContactInteraction and will default to LagrangeMultiplier as it will be up to the solver that takes this * ContactConstraint to determine how it will enforce the constraint. @@ -116,9 +116,11 @@ class ContactConstraint : public Constraint { * @param time time * @param dt time step * @param fields vector of serac::FiniteElementState* + * @param new_point boolean indicating if this is a new point or not * @return mfem::Vector which is the constraint evaluation */ - mfem::Vector evaluate(double time, double dt, const std::vector& fields) const + mfem::Vector evaluate(double time, double dt, const std::vector& fields, + [[maybe_unused]] bool new_point = true) const override { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); @@ -139,10 +141,11 @@ class ContactConstraint : public Constraint { * @param dt time step * @param fields vector of serac::FiniteElementState* * @param direction index for which field to take the gradient with respect to + * @param new_point boolean indicating if this is a new point or not * @return std::unique_ptr The true Jacobian */ std::unique_ptr jacobian(double time, double dt, const std::vector& fields, - int direction) const + int direction, [[maybe_unused]] bool new_point = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); @@ -166,10 +169,12 @@ class ContactConstraint : public Constraint { * @param fields vector of serac::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to + * @param new_point boolean indicating if this is a new point or not * @return std::Vector */ mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, - const mfem::Vector& multipliers, int direction) const + const mfem::Vector& multipliers, int direction, + [[maybe_unused]] bool new_point = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); @@ -200,12 +205,12 @@ class ContactConstraint : public Constraint { * @param fields vector of serac::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to + * @param new_point boolean indicating if this is a new point or not * @return std::unique_ptr */ - std::unique_ptr residual_contribution_jacobian(double time, double dt, - const std::vector& fields, - const mfem::Vector& multipliers, - int direction) const + std::unique_ptr residual_contribution_jacobian( + double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, int direction, + [[maybe_unused]] bool new_point = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); @@ -238,10 +243,12 @@ class ContactConstraint : public Constraint { * @param dt time step * @param fields vector of serac::FiniteElementState* * @param direction index for which field to take the gradient with respect to + * @param new_point boolean indicating if this is a new point or not * @return std::unique_ptr */ std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, - int direction) const + int direction, + [[maybe_unused]] bool new_point = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); From 17e30e0bd1827ed23150795436354772ab617fab Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 17 Oct 2025 14:10:20 -0700 Subject: [PATCH 26/82] propagating ability to specify whether constraint functions are being queried at a new point or not into the examples and into the EqualityConstrainedHomotopyProblem, as well as the mixed complementarity problem derivative calls --- ContinuationSolvers | 2 +- .../contact/homotopy/constraint_twist.cpp | 59 +++++++++++-------- .../inertia_relief/inertia_relief_example.cpp | 57 +++++++++++------- 3 files changed, 69 insertions(+), 49 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index e0c585d1c..abf58230b 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit e0c585d1c8aa7b2885d883af7dca52f0e37fde3e +Subproject commit abf58230bbfdea6d9bfeede0370636efaba60fcc diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 0a9b1a213..c5c7ffe7e 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -38,15 +38,11 @@ enum FIELD DENSITY = SolidWeakFormT::NUM_STATES }; -/* Nonlinear mixed complementarity problem (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 ] +/* Nonlinear problem of the form + * F(X) = [ r(u) + (dc/du)^T l ] = [ 0 ] + * [ -c(u) ] [ 0 ] + * X = [ u ] + * [ l ] * * wherein r(u) is the elasticity nonlinear residual * c(u) are the tied gap contacts @@ -54,8 +50,11 @@ enum FIELD * l are the Lagrange multipliers * * we use the approximate Jacobian - * dQ/dy \approx [ dr/du (dc/du)^T] + * dF/dX \approx [ dr/du (dc/du)^T] * [-dc/du 0 ] + * + * This problem inherits from EqualityConstrainedHomotopyProblem + * for compatibility with the HomotopySolver. */ template class TiedContactProblem : public EqualityConstrainedHomotopyProblem { @@ -79,11 +78,11 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { TiedContactProblem(std::vector contact_states, std::vector all_states, std::shared_ptr mesh, std::shared_ptr weak_form, std::shared_ptr constraints); - mfem::Vector residual(const mfem::Vector& u) const; - mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u); - mfem::Vector constraint(const mfem::Vector& u) const; - mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u); - mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l) const; + mfem::Vector residual(const mfem::Vector& u, bool new_point) const; + mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); + mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; + mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); + mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; virtual ~TiedContactProblem(); }; @@ -193,8 +192,9 @@ int main(int argc, char* argv[]) auto dimu = problem.GetDisplacementDim(); mfem::Vector u(dimu); u = 0.0; - auto gap = problem.constraint(u); - auto gapJacobian = problem.constraintJacobian(u); + bool new_point = true; + auto gap = problem.constraint(u, new_point); + auto gapJacobian = problem.constraintJacobian(u, new_point); double gapJacobianFNorm = gapJacobian->FNorm(); if (gapJacobianFNorm > 1.e-12) { auto adjoint = problem.GetOptimizationVariable(); @@ -242,7 +242,7 @@ TiedContactProblem::TiedContactProblem(std::vector -mfem::Vector TiedContactProblem::residual(const mfem::Vector& u) const +mfem::Vector TiedContactProblem::residual(const mfem::Vector& u, bool /*new_point*/) const { contact_states_[serac::ContactFields::DISP]->Set(1.0, u); auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); @@ -250,8 +250,11 @@ mfem::Vector TiedContactProblem::residual(const mfem::Vector& } template -mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u) +mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool new_point) { + if (!new_point) { + return drdu_.get(); + } contact_states_[serac::ContactFields::DISP]->Set(1.0, u); drdu_.reset(); drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); @@ -260,29 +263,33 @@ mfem::HypreParMatrix* TiedContactProblem::residualJacobian(co } template -mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u) const +mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u, bool new_point) const { contact_states_[serac::ContactFields::DISP]->Set(1.0, u); - auto gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_)); + auto gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point); return gap; } template -mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u) +mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u, bool new_point) { + if (!new_point) { + return dcdu_.get(); + } contact_states_[serac::ContactFields::DISP]->Set(1.0, u); dcdu_.reset(); - dcdu_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP); + dcdu_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, + new_point); return dcdu_.get(); } template -mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, - const mfem::Vector& l) const +mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, + bool new_point) const { contact_states_[serac::ContactFields::DISP]->Set(1.0, u); auto res_contribution = constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), - l, serac::ContactFields::DISP); + l, serac::ContactFields::DISP, new_point); return res_contribution; } diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index cd294b9bd..41a34186e 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -111,20 +111,23 @@ auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector= 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 ] +/* Nonlinear problem of the form + * F(X) = [ r(u) + (dc/du)^T l ] = [ 0 ] + * [ -c(u) ] [ 0 ] + * X = [ u ] + * [ l ] + * + * wherein r(u) is the elasticity nonlinear residual + * c(u) are the tied gap contacts + * u are the displacement dofs + * l are the Lagrange multipliers + * * we use the approximate Jacobian - * dQ/dy \approx [ dr/du (dc/du)^T] + * dF/dX \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. + * + * This problem inherits from EqualityConstrainedHomotopyProblem + * for compatibility with the HomotopySolver. */ class InertialReliefProblem : public EqualityConstrainedHomotopyProblem { protected: @@ -149,11 +152,11 @@ class InertialReliefProblem : public EqualityConstrainedHomotopyProblem { InertialReliefProblem(std::vector obj_states, std::vector all_states, std::shared_ptr mesh, std::shared_ptr weak_form, std::vector> constraints); - mfem::Vector residual(const mfem::Vector& u) const; - mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l) const; - mfem::Vector constraint(const mfem::Vector& u) const; - mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u); - mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u); + mfem::Vector residual(const mfem::Vector& u, bool new_point) const; + mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; + mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; + mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); + mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); virtual ~InertialReliefProblem(); }; @@ -333,6 +336,12 @@ int main(int argc, char* argv[]) double multiplier_norm = mfem::GlobalLpNorm(2, multiplier_sol.Norml2(), MPI_COMM_WORLD); SLIC_INFO_ROOT(axom::fmt::format("||displacement|| = {}", displacement_norm)); SLIC_INFO_ROOT(axom::fmt::format("||multiplier|| = {}", multiplier_norm)); + auto adjoint = problem.GetOptimizationVariable(); + auto adjoint_load = problem.GetOptimizationVariable(); + adjoint = 0.0; + adjoint_load = 1.0; + problem.AdjointSolve(displacement_sol, adjoint_load, adjoint); + if (visualize) { writer.write(1, 1.0, objective_states); } @@ -377,7 +386,7 @@ InertialReliefProblem::InertialReliefProblem(std::vectorSet(1.0, u); auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); @@ -385,7 +394,8 @@ mfem::Vector InertialReliefProblem::residual(const mfem::Vector& u) const } // constraint Jacobian transpose vector product -mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l) const +mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, + bool /*new_point*/) const { obj_states_[DISP]->Set(1.0, u); std::vector multipliers(constraints_.size()); @@ -410,7 +420,7 @@ mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, } // Jacobian of the residual -mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector& u) +mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) { obj_states_[DISP]->Set(1.0, u); drdu_.reset(); @@ -421,7 +431,7 @@ mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector } // constraint callback -mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u) const +mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool /*new_point*/) const { obj_states_[DISP]->Set(1.0, u); mfem::Vector output_vec(dimc_); @@ -442,8 +452,11 @@ mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u) const } // Jacobian of the constraint -mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vector& u) +mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vector& u, bool new_pt) { + if (!new_pt) { + return dcdu_.get(); + } obj_states_[DISP]->Set(1.0, u); int myid = mfem::Mpi::WorldRank(); int nentries = dimuglb_; From 3f98407c69c748fb777482aa416948dac1abed4a Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 20 Oct 2025 11:49:35 -0700 Subject: [PATCH 27/82] updating ContinuationSolver branch in order to cache the Q evaluations in the HomotopySolve of EqualityConstrainedHomotopyProblems such as the InertiaRelief problem --- ContinuationSolvers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index abf58230b..1745f5f9a 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit abf58230bbfdea6d9bfeede0370636efaba60fcc +Subproject commit 1745f5f9a41132f9a129f2f3bdb82f0be3b9cd76 From c8223ff871b857beb882f86cac8e0a3347cd643c Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 20 Oct 2025 13:27:41 -0700 Subject: [PATCH 28/82] using utility function that has function arguments ordered (row, column) in a way consistent with general usage --- ContinuationSolvers | 2 +- examples/inertia_relief/inertia_relief_example.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 1745f5f9a..e7af685ae 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 1745f5f9a41132f9a129f2f3bdb82f0be3b9cd76 +Subproject commit e7af685aec88e679ce3b074d54e876dfc259ff77 diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index 41a34186e..1fcd1aed0 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -484,7 +484,7 @@ mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vect } dcdumat.Threshold(1.e-20); dcdumat.Finalize(); - dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(uOffsets_, cOffsets_, &dcdumat)); + dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix2(cOffsets_, uOffsets_, &dcdumat)); return dcdu_.get(); } From 8923fb33883c03dc7fb235aef0fca08ee2ceef22 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 20 Oct 2025 13:44:01 -0700 Subject: [PATCH 29/82] updating submodule --- ContinuationSolvers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index e7af685ae..ef0f67938 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit e7af685aec88e679ce3b074d54e876dfc259ff77 +Subproject commit ef0f679383d8cb2ebfff5a816e53bbab108c0db5 From 66d333a4a93b3cdd6870fddf2349f408afe6ad62 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 20 Oct 2025 14:07:40 -0700 Subject: [PATCH 30/82] utility function rename --- ContinuationSolvers | 2 +- examples/inertia_relief/inertia_relief_example.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index ef0f67938..227c5f30d 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit ef0f679383d8cb2ebfff5a816e53bbab108c0db5 +Subproject commit 227c5f30dd800c723dbd09252be429685fe9d685 diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index 1fcd1aed0..1f8e52e34 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -484,7 +484,7 @@ mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vect } dcdumat.Threshold(1.e-20); dcdumat.Finalize(); - dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix2(cOffsets_, uOffsets_, &dcdumat)); + dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(cOffsets_, uOffsets_, &dcdumat)); return dcdu_.get(); } From 604d8f709224c874bb02b3040779e00222c5cfde Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 22 Oct 2025 10:01:13 -0700 Subject: [PATCH 31/82] more fully utilizing new_pt function evaluation caching --- ContinuationSolvers | 2 +- .../inertia_relief/inertia_relief_example.cpp | 71 ++++++++++--------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 227c5f30d..493e4b9ea 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 227c5f30dd800c723dbd09252be429685fe9d685 +Subproject commit 493e4b9ea581a3d7dc1647339b676009ab7ce3c0 diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index 1f8e52e34..d827b2d1b 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -326,6 +326,7 @@ int main(int argc, char* argv[]) solver.SetMaxIter(nonlinear_max_iterations); // solve the inertia relief problem + solver.SetPrintLevel(2); solver.Mult(X0, Xf); // extract displacement and Lagrange multipliers mfem::Vector displacement_sol = problem.GetDisplacement(Xf); @@ -420,12 +421,13 @@ mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, } // Jacobian of the residual -mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) +mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector& u, bool new_point) { - obj_states_[DISP]->Set(1.0, u); - drdu_.reset(); - drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); - + if (new_point) { + obj_states_[DISP]->Set(1.0, u); + drdu_.reset(); + drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); + } SLIC_ERROR_ROOT_IF(drdu_->Height() != dimu_ || drdu_->Width() != dimu_, "residual Jacobian of an unexpected shape"); return drdu_.get(); } @@ -452,39 +454,38 @@ mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool /*new } // Jacobian of the constraint -mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vector& u, bool new_pt) +mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vector& u, bool new_point) { - if (!new_pt) { - return dcdu_.get(); - } - obj_states_[DISP]->Set(1.0, u); - int myid = mfem::Mpi::WorldRank(); - int nentries = dimuglb_; - if (myid > 0) { - nentries = 0; - } - mfem::SparseMatrix dcdumat(dimc_, dimuglb_, nentries); - mfem::Array cols; - cols.SetSize(dimuglb_); - for (int i = 0; i < dimuglb_; i++) { - cols[i] = i; - } - std::unique_ptr globalGradVector; - for (size_t i = 0; i < constraints_.size(); i++) { - const int idx = static_cast(i); - const size_t i2 = static_cast(idx); - SLIC_ERROR_ROOT_IF(i2 != i, "Constraint index is out of range, bad cast from size_t to int"); - mfem::HypreParVector gradVector(MPI_COMM_WORLD, dimuglb_, uOffsets_); - gradVector.Set( - 1.0, constraints_[i]->gradient(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(obj_states_), DISP)); - globalGradVector.reset(gradVector.GlobalVector()); - if (myid == 0) { - dcdumat.SetRow(idx, cols, *globalGradVector.get()); + if (new_point) { + obj_states_[DISP]->Set(1.0, u); + int myid = mfem::Mpi::WorldRank(); + int nentries = dimuglb_; + if (myid > 0) { + nentries = 0; + } + mfem::SparseMatrix dcdumat(dimc_, dimuglb_, nentries); + mfem::Array cols; + cols.SetSize(dimuglb_); + for (int i = 0; i < dimuglb_; i++) { + cols[i] = i; + } + std::unique_ptr globalGradVector; + for (size_t i = 0; i < constraints_.size(); i++) { + const int idx = static_cast(i); + const size_t i2 = static_cast(idx); + SLIC_ERROR_ROOT_IF(i2 != i, "Constraint index is out of range, bad cast from size_t to int"); + mfem::HypreParVector gradVector(MPI_COMM_WORLD, dimuglb_, uOffsets_); + gradVector.Set(1.0, constraints_[i]->gradient(time_, dt_, shape_disp_.get(), + serac::getConstFieldPointers(obj_states_), DISP)); + globalGradVector.reset(gradVector.GlobalVector()); + if (myid == 0) { + dcdumat.SetRow(idx, cols, *globalGradVector.get()); + } } + dcdumat.Threshold(1.e-20); + dcdumat.Finalize(); + dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(cOffsets_, uOffsets_, &dcdumat)); } - dcdumat.Threshold(1.e-20); - dcdumat.Finalize(); - dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(cOffsets_, uOffsets_, &dcdumat)); return dcdu_.get(); } From 9915788a5753f8717b83e8595f46310fb88bdbc4 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 29 Oct 2025 11:14:07 -0700 Subject: [PATCH 32/82] updating inertia relief problem (minor), and updating ContinuationSolvers that has has an exception catching mechanism on EqualityConstrainedHomotopyProblem::Q --- ContinuationSolvers | 2 +- .../inertia_relief/inertia_relief_example.cpp | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 493e4b9ea..a0085aebe 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 493e4b9ea581a3d7dc1647339b676009ab7ce3c0 +Subproject commit a0085aebe187579552b58e56926af8ddad510c84 diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index d827b2d1b..2275060c8 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -230,7 +230,7 @@ int main(int argc, char* argv[]) // construct residual auto solid_mechanics_weak_form = - std::make_shared(physics_name, mesh, states[DISP].space(), getSpaces(params)); + std::make_shared(physics_name, mesh, states[FIELD::DISP].space(), getSpaces(params)); SolidMaterial mat; mat.K = 1.0; @@ -261,9 +261,9 @@ int main(int argc, char* argv[]) double time = 0.0; double dt = 1.0; auto all_states = getConstFieldPointers(states, params); - auto objective_states = {all_states[DISP], all_states[DENSITY]}; + auto objective_states = {all_states[FIELD::DISP], all_states[FIELD::DENSITY]}; - ObjectiveT::SpacesT param_space_ptrs{&all_states[DISP]->space(), &all_states[DENSITY]->space()}; + ObjectiveT::SpacesT param_space_ptrs{&all_states[FIELD::DISP]->space(), &all_states[FIELD::DENSITY]->space()}; ObjectiveT mass_objective("mass constraining", mesh, param_space_ptrs); @@ -271,7 +271,7 @@ int main(int argc, char* argv[]) [](double /*t*/, auto /*X*/, auto RHO) { return get(RHO); }); double mass = mass_objective.evaluate(time, dt, shape_disp.get(), objective_states); - serac::tensor initial_cg; + serac::tensor initial_cg; // center of gravity for (int i = 0; i < dim; ++i) { auto cg_objective = std::make_shared("translation " + std::to_string(i), mesh, param_space_ptrs); @@ -312,7 +312,7 @@ int main(int argc, char* argv[]) } auto non_const_states = getFieldPointers(states, params); // create an inertial relief problem - InertialReliefProblem problem({non_const_states[DISP], non_const_states[DENSITY]}, non_const_states, mesh, + InertialReliefProblem problem({non_const_states[FIELD::DISP], non_const_states[FIELD::DENSITY]}, non_const_states, mesh, solid_mechanics_weak_form, constraints); // optimization variables @@ -389,7 +389,7 @@ InertialReliefProblem::InertialReliefProblem(std::vectorSet(1.0, u); + obj_states_[FIELD::DISP]->Set(1.0, u); auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); return res_vector; } @@ -398,7 +398,7 @@ mfem::Vector InertialReliefProblem::residual(const mfem::Vector& u, bool /*new_p mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool /*new_point*/) const { - obj_states_[DISP]->Set(1.0, u); + obj_states_[FIELD::DISP]->Set(1.0, u); std::vector multipliers(constraints_.size()); for (int i = 0; i < dimc_; i++) { multipliers[static_cast(i)] = l(i); @@ -413,7 +413,7 @@ mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, for (size_t i = 0; i < constraints_.size(); i++) { mfem::Vector grad_temp = - constraints_[i]->gradient(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(obj_states_), DISP); + constraints_[i]->gradient(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(obj_states_), FIELD::DISP); constraint_gradient.Set(1.0, grad_temp); output_vec.Add(multipliers[i], constraint_gradient); } @@ -424,7 +424,7 @@ mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector& u, bool new_point) { if (new_point) { - obj_states_[DISP]->Set(1.0, u); + obj_states_[FIELD::DISP]->Set(1.0, u); drdu_.reset(); drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); } @@ -435,7 +435,7 @@ mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector // constraint callback mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool /*new_point*/) const { - obj_states_[DISP]->Set(1.0, u); + obj_states_[FIELD::DISP]->Set(1.0, u); mfem::Vector output_vec(dimc_); output_vec = 0.0; @@ -457,7 +457,7 @@ mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool /*new mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vector& u, bool new_point) { if (new_point) { - obj_states_[DISP]->Set(1.0, u); + obj_states_[FIELD::DISP]->Set(1.0, u); int myid = mfem::Mpi::WorldRank(); int nentries = dimuglb_; if (myid > 0) { @@ -476,7 +476,7 @@ mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vect SLIC_ERROR_ROOT_IF(i2 != i, "Constraint index is out of range, bad cast from size_t to int"); mfem::HypreParVector gradVector(MPI_COMM_WORLD, dimuglb_, uOffsets_); gradVector.Set(1.0, constraints_[i]->gradient(time_, dt_, shape_disp_.get(), - serac::getConstFieldPointers(obj_states_), DISP)); + serac::getConstFieldPointers(obj_states_), FIELD::DISP)); globalGradVector.reset(gradVector.GlobalVector()); if (myid == 0) { dcdumat.SetRow(idx, cols, *globalGradVector.get()); From 41682a28f2586795297ec29e3f52e7f792c4b241 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 29 Oct 2025 11:49:06 -0700 Subject: [PATCH 33/82] removal of one usage of mfem::Mpi::WorldRank and comments --- .../inertia_relief/inertia_relief_example.cpp | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index 2275060c8..d936dfc99 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -122,31 +122,26 @@ auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector drdu_; - std::unique_ptr dcdu_; - int dimu_; - int dimc_; - int dimuglb_; - int dimcglb_; - mfem::Array y_partition_; - std::vector obj_states_; - std::vector all_states_; - std::shared_ptr weak_form_; - std::unique_ptr shape_disp_; + std::unique_ptr drdu_; // Jacobian of residual + std::unique_ptr dcdu_; // Jacobian of constraint + int dimu_; // dimension of displacement + int dimc_; // dimension of constraints + int dimuglb_; // global dimension of displacement + int dimcglb_; // global dimension of constraints + std::vector obj_states_; // states for objective evaluation + std::vector all_states_; // states for weak_form evaluation + std::shared_ptr weak_form_; // weak_form + std::unique_ptr shape_disp_; // shape displacement std::shared_ptr mesh_; - std::vector> constraints_; - double time_ = 0.0; - double dt_ = 0.0; - std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; + std::vector> constraints_; // vector of constraints + double time_ = 0.0; // parameter for constraint and weak_form member function calls + double dt_ = 0.0; // parameter for constraint and weak_form member function calls + std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; // weights for weak_form_->jacobian calls public: InertialReliefProblem(std::vector obj_states, std::vector all_states, @@ -271,7 +266,7 @@ int main(int argc, char* argv[]) [](double /*t*/, auto /*X*/, auto RHO) { return get(RHO); }); double mass = mass_objective.evaluate(time, dt, shape_disp.get(), objective_states); - serac::tensor initial_cg; // center of gravity + serac::tensor initial_cg; // center of gravity for (int i = 0; i < dim; ++i) { auto cg_objective = std::make_shared("translation " + std::to_string(i), mesh, param_space_ptrs); @@ -312,8 +307,8 @@ int main(int argc, char* argv[]) } auto non_const_states = getFieldPointers(states, params); // create an inertial relief problem - InertialReliefProblem problem({non_const_states[FIELD::DISP], non_const_states[FIELD::DENSITY]}, non_const_states, mesh, - solid_mechanics_weak_form, constraints); + InertialReliefProblem problem({non_const_states[FIELD::DISP], non_const_states[FIELD::DENSITY]}, non_const_states, + mesh, solid_mechanics_weak_form, constraints); // optimization variables auto X0 = problem.GetOptimizationVariable(); @@ -412,8 +407,8 @@ mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, output_vec = 0.0; for (size_t i = 0; i < constraints_.size(); i++) { - mfem::Vector grad_temp = - constraints_[i]->gradient(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(obj_states_), FIELD::DISP); + mfem::Vector grad_temp = constraints_[i]->gradient(time_, dt_, shape_disp_.get(), + serac::getConstFieldPointers(obj_states_), FIELD::DISP); constraint_gradient.Set(1.0, grad_temp); output_vec.Add(multipliers[i], constraint_gradient); } @@ -458,9 +453,8 @@ mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vect { if (new_point) { obj_states_[FIELD::DISP]->Set(1.0, u); - int myid = mfem::Mpi::WorldRank(); int nentries = dimuglb_; - if (myid > 0) { + if (dimc_ == 0) { nentries = 0; } mfem::SparseMatrix dcdumat(dimc_, dimuglb_, nentries); @@ -478,7 +472,7 @@ mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vect gradVector.Set(1.0, constraints_[i]->gradient(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(obj_states_), FIELD::DISP)); globalGradVector.reset(gradVector.GlobalVector()); - if (myid == 0) { + if (dimc_ > 0) { dcdumat.SetRow(idx, cols, *globalGradVector.get()); } } From 5d0e4145286d4f28f63bae63e0fef271a43611a3 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 29 Oct 2025 14:19:53 -0700 Subject: [PATCH 34/82] Caching contact computations --- ContinuationSolvers | 2 +- src/serac/physics/contact_constraint.hpp | 128 ++++++++++++++--------- 2 files changed, 78 insertions(+), 52 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index a0085aebe..70ad80f51 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit a0085aebe187579552b58e56926af8ddad510c84 +Subproject commit 70ad80f51377c99ca741fc421e2718d96a561508 diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 1cb5acf55..444949f1d 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -52,7 +52,7 @@ enum ContactFields * @return std::unique_ptr The requested block of block_operator */ static std::unique_ptr safelyObtainBlock(mfem::BlockOperator* block_operator, int iblock, - int jblock) + int jblock, bool own_blocks = false) { SLIC_ERROR_IF(iblock < 0 || jblock < 0, "block indicies must be non-negative"); SLIC_ERROR_IF(iblock > block_operator->NumRowBlocks() || jblock > block_operator->NumColBlocks(), @@ -64,15 +64,21 @@ static std::unique_ptr safelyObtainBlock(mfem::BlockOperat if (i == iblock && j == jblock) { continue; } - if (!block_operator->IsZeroBlock(i, j)) { + if (!block_operator->IsZeroBlock(i, j) && !own_blocks) { delete &block_operator->GetBlock(i, j); } } } auto Ablk = dynamic_cast(&block_operator->GetBlock(iblock, jblock)); SLIC_ERROR_IF(!Ablk, "failed cast block to mfem::HypreParMatrix"); - std::unique_ptr Ablk_unique(Ablk); - return Ablk_unique; + if (own_blocks) { + // deep copy --> unique_ptr + auto Ablk_unique = std::make_unique(*Ablk); + return Ablk_unique; + } else { + std::unique_ptr Ablk_unique(Ablk); + return Ablk_unique; + } }; class FiniteElementState; @@ -120,14 +126,16 @@ class ContactConstraint : public Constraint { * @return mfem::Vector which is the constraint evaluation */ mfem::Vector evaluate(double time, double dt, const std::vector& fields, - [[maybe_unused]] bool new_point = true) const override + bool new_point = true) const override { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); - // note: Tribol does not use cycle. - int cycle = 0; - contact_.update(cycle, time, dt); + if (new_point) { + // note: Tribol does not use cycle. + int cycle = 0; + contact_.update(cycle, time, dt); + } auto gaps_hpv = contact_.mergedGaps(false); // Note: this copy is needed to prevent the HypreParVector pointer from going out of scope. see // https://github.com/mfem/mfem/issues/5029 @@ -145,19 +153,20 @@ class ContactConstraint : public Constraint { * @return std::unique_ptr The true Jacobian */ std::unique_ptr jacobian(double time, double dt, const std::vector& fields, - int direction, [[maybe_unused]] bool new_point = true) const override + int direction, bool new_point = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); - int cycle = 0; - contact_.update(cycle, time, dt); - auto J_contact = contact_.mergedJacobian(); - + if (new_point) { + int cycle = 0; + contact_.update(cycle, time, dt); + J_contact_ = contact_.mergedJacobian(); + } int iblock = 1; int jblock = 0; - auto dgdu = safelyObtainBlock(J_contact.get(), iblock, jblock); + auto dgdu = safelyObtainBlock(J_contact_.get(), iblock, jblock, own_blocks_); return dgdu; }; @@ -174,25 +183,27 @@ class ContactConstraint : public Constraint { */ mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, int direction, - [[maybe_unused]] bool new_point = true) const override + bool new_point = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); - int cycle = 0; - // we need to call update first to update gaps - for (auto& interaction : contact_.getContactInteractions()) { - interaction.evalJacobian(false); - } - contact_.update(cycle, time, dt); - // with updated gaps, we can update pressure for contact interactions with penalty enforcement - contact_.setPressures(multipliers); - // call update again with the right pressures - for (auto& interaction : contact_.getContactInteractions()) { - interaction.evalJacobian(true); + if (new_point) { + int cycle = 0; + // we need to call update first to update gaps + for (auto& interaction : contact_.getContactInteractions()) { + interaction.evalJacobian(false); + } + contact_.update(cycle, time, dt); + // with updated gaps, we can update pressure for contact interactions with penalty enforcement + contact_.setPressures(multipliers); + // call update again with the right pressures + for (auto& interaction : contact_.getContactInteractions()) { + interaction.evalJacobian(true); + } + contact_.update(cycle, time, dt); } - contact_.update(cycle, time, dt); return contact_.forces(); }; @@ -208,32 +219,35 @@ class ContactConstraint : public Constraint { * @param new_point boolean indicating if this is a new point or not * @return std::unique_ptr */ - std::unique_ptr residual_contribution_jacobian( - double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, int direction, - [[maybe_unused]] bool new_point = true) const override + std::unique_ptr residual_contribution_jacobian(double time, double dt, + const std::vector& fields, + const mfem::Vector& multipliers, int direction, + bool new_point = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); - int cycle = 0; - // we need to call update first to update gaps - for (auto& interaction : contact_.getContactInteractions()) { - interaction.evalJacobian(false); - } - contact_.update(cycle, time, dt); - // with updated gaps, we can update pressure for contact interactions with penalty enforcement - contact_.setPressures(multipliers); - // call update again with the right pressures - for (auto& interaction : contact_.getContactInteractions()) { - interaction.evalJacobian(true); - } - contact_.update(cycle, time, dt); + if (new_point) { + int cycle = 0; + // we need to call update first to update gaps + for (auto& interaction : contact_.getContactInteractions()) { + interaction.evalJacobian(false); + } + contact_.update(cycle, time, dt); + // with updated gaps, we can update pressure for contact interactions with penalty enforcement + contact_.setPressures(multipliers); + // call update again with the right pressures + for (auto& interaction : contact_.getContactInteractions()) { + interaction.evalJacobian(true); + } + contact_.update(cycle, time, dt); - auto J_contact = contact_.mergedJacobian(); + J_contact_ = contact_.mergedJacobian(); + } int iblock = 0; int jblock = 0; - auto Hessian = safelyObtainBlock(J_contact.get(), iblock, jblock); + auto Hessian = safelyObtainBlock(J_contact_.get(), iblock, jblock, own_blocks_); return Hessian; }; @@ -247,19 +261,21 @@ class ContactConstraint : public Constraint { * @return std::unique_ptr */ std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, - int direction, - [[maybe_unused]] bool new_point = true) const override + int direction, bool new_point = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); - int cycle = 0; - contact_.update(cycle, time, dt); - auto J_contact = contact_.mergedJacobian(); + if (new_point) { + int cycle = 0; + contact_.update(cycle, time, dt); + J_contact_.reset(); + J_contact_ = contact_.mergedJacobian(); + } int iblock = 0; int jblock = 1; - auto dgduT = safelyObtainBlock(J_contact.get(), iblock, jblock); + auto dgduT = safelyObtainBlock(J_contact_.get(), iblock, jblock, own_blocks_); std::unique_ptr dgdu(dgduT->Transpose()); return dgdu; }; @@ -281,6 +297,16 @@ class ContactConstraint : public Constraint { * @brief interaction_id Unique identifier for the ContactInteraction (used in Tribol) */ int interaction_id_; + + /** + * @brief J_contact_ to hold contact derivatives + */ + mutable std::unique_ptr J_contact_; + + /** + * @brief bool + */ + const bool own_blocks_ = true; }; } // namespace serac From de4fbf7d6ac7eb65d742b766ccf0674bd5ba060a Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 29 Oct 2025 16:21:52 -0700 Subject: [PATCH 35/82] Complete caching of function calls. Good for testing the infastructure, that is that the convergence has not been impacted due to an error in implementation of the function caching --- ContinuationSolvers | 2 +- .../inertia_relief/inertia_relief_example.cpp | 90 +++++++++++-------- 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 70ad80f51..ca82277fb 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 70ad80f51377c99ca741fc421e2718d96a561508 +Subproject commit ca82277fb4d2ee31de9ad8b6e5601a4922002d4f diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index d936dfc99..ccca5684d 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -142,6 +142,9 @@ class InertialReliefProblem : public EqualityConstrainedHomotopyProblem { double time_ = 0.0; // parameter for constraint and weak_form member function calls double dt_ = 0.0; // parameter for constraint and weak_form member function calls std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; // weights for weak_form_->jacobian calls + mutable mfem::Vector constraint_cached_; + mutable mfem::Vector residual_cached_; + mutable mfem::Vector JTvp_cached_; public: InertialReliefProblem(std::vector obj_states, std::vector all_states, @@ -378,41 +381,50 @@ InertialReliefProblem::InertialReliefProblem(std::vectorSet(1.0, u); - auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); - return res_vector; + if (new_point) { + obj_states_[FIELD::DISP]->Set(1.0, u); + residual_cached_.Set( + 1.0, weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_))); + } + return residual_cached_; } // constraint Jacobian transpose vector product mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, - bool /*new_point*/) const + bool new_point) const { - obj_states_[FIELD::DISP]->Set(1.0, u); - std::vector multipliers(constraints_.size()); - for (int i = 0; i < dimc_; i++) { - multipliers[static_cast(i)] = l(i); - } - const int nconstraints = static_cast(constraints_.size()); - MPI_Bcast(multipliers.data(), nconstraints, MPI_DOUBLE, 0, MPI_COMM_WORLD); - - mfem::Vector constraint_gradient(dimu_); - constraint_gradient = 0.0; - mfem::Vector output_vec(dimu_); - output_vec = 0.0; - - for (size_t i = 0; i < constraints_.size(); i++) { - mfem::Vector grad_temp = constraints_[i]->gradient(time_, dt_, shape_disp_.get(), - serac::getConstFieldPointers(obj_states_), FIELD::DISP); - constraint_gradient.Set(1.0, grad_temp); - output_vec.Add(multipliers[i], constraint_gradient); + if (new_point) { + obj_states_[FIELD::DISP]->Set(1.0, u); + std::vector multipliers(constraints_.size()); + for (int i = 0; i < dimc_; i++) { + multipliers[static_cast(i)] = l(i); + } + const int nconstraints = static_cast(constraints_.size()); + MPI_Bcast(multipliers.data(), nconstraints, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + mfem::Vector constraint_gradient(dimu_); + constraint_gradient = 0.0; + JTvp_cached_ = 0.0; + for (size_t i = 0; i < constraints_.size(); i++) { + mfem::Vector grad_temp = constraints_[i]->gradient(time_, dt_, shape_disp_.get(), + serac::getConstFieldPointers(obj_states_), FIELD::DISP); + constraint_gradient.Set(1.0, grad_temp); + JTvp_cached_.Add(multipliers[i], constraint_gradient); + } } - return output_vec; + return JTvp_cached_; } // Jacobian of the residual @@ -428,24 +440,24 @@ mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector } // constraint callback -mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool /*new_point*/) const +mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool new_point) const { - obj_states_[FIELD::DISP]->Set(1.0, u); - mfem::Vector output_vec(dimc_); - output_vec = 0.0; - - for (size_t i = 0; i < constraints_.size(); i++) { - const int idx = static_cast(i); - const size_t i2 = static_cast(idx); - SLIC_ERROR_ROOT_IF(i2 != i, "Constraint index is out of range, bad cast from size_t to int"); - - double constraint_i = - constraints_[i]->evaluate(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(obj_states_)); - if (dimc_ > 0) { - output_vec(idx) = constraint_i; + if (new_point) { + obj_states_[FIELD::DISP]->Set(1.0, u); + + for (size_t i = 0; i < constraints_.size(); i++) { + const int idx = static_cast(i); + const size_t i2 = static_cast(idx); + SLIC_ERROR_ROOT_IF(i2 != i, "Constraint index is out of range, bad cast from size_t to int"); + + double constraint_i = + constraints_[i]->evaluate(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(obj_states_)); + if (dimc_ > 0) { + constraint_cached_(idx) = constraint_i; + } } } - return output_vec; + return constraint_cached_; } // Jacobian of the constraint From 94ec08cbf8c6cfb97b325049b1eff7a4bcd81ec0 Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 6 Nov 2025 14:06:53 -0800 Subject: [PATCH 36/82] updating, fixed bug. more to do --- .../contact/homotopy/constraint_twist.cpp | 283 ++++++++++++++---- 1 file changed, 230 insertions(+), 53 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index c5c7ffe7e..fabc1b573 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -12,6 +12,8 @@ #include "axom/slic.hpp" #include "mfem.hpp" +#include "serac/physics/contact/contact_config.hpp" +#include "shared/mesh/MeshBuilder.hpp" // ContinuationSolver headers #include "problems/Problems.hpp" @@ -28,6 +30,67 @@ using VectorSpace = serac::H1; using DensitySpace = serac::L2; using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; + +//struct MyLinearIsotropic { +// using State = serac::Empty; ///< this material has no internal variables +// +// /** +// * @brief stress calculation for a linear isotropic material model +// * +// * When applied to 2D displacement gradients, the stress is computed in plane strain, +// * returning only the in-plane components. +// * +// * @tparam T Number-like type for the displacement gradient components +// * @tparam dim Dimensionality of space +// * @param du_dX Displacement gradient with respect to the reference configuration +// * @return The stress +// */ +// template +// SERAC_HOST_DEVICE auto operator()(State& /* state */, const serac::tensor& du_dX) const +// { +// auto I = serac::Identity(); +// auto lambda = K - (2.0 / 3.0) * G; +// auto epsilon = 0.5 * (transpose(du_dX) + du_dX); +// return lambda * tr(epsilon) * I + 2.0 * G * epsilon; +// } +// template +// SERAC_HOST_DEVICE auto pkStress(State& /* state */, const serac::tensor& du_dX, const Density&) const +// { +// auto I = serac::Identity(); +// auto lambda = K - (2.0 / 3.0) * G; +// auto epsilon = 0.5 * (transpose(du_dX) + du_dX); +// return lambda * tr(epsilon) * I + 2.0 * G * epsilon; +// //using std::log1p; +// //constexpr auto I = Identity(); +// //auto lambda = K - (2.0 / 3.0) * G; +// //auto B_minus_I = dot(du_dX, transpose(du_dX)) + transpose(du_dX) + du_dX; +// +// //auto logJ = log1p(detApIm1(du_dX)); +// //// Kirchoff stress, in form that avoids cancellation error when F is near I +// //auto TK = lambda * logJ * I + G * B_minus_I; +// +// //// Pull back to Piola +// //auto F = du_dX + I; +// //return dot(TK, inv(transpose(F))); +// } +// +// /// @brief interpolates density field +// template +// SERAC_HOST_DEVICE auto density(const Density& density) const +// { +// return get(density); +// } +// +// //double density; ///< mass density +// double K; ///< bulk modulus +// double G; ///< shear modulus +//}; +// +//using SolidMaterial = MyLinearIsotropic; + + + + using SolidWeakFormT = serac::SolidWeakForm>; enum FIELD @@ -39,19 +102,15 @@ enum FIELD }; /* Nonlinear problem of the form - * F(X) = [ r(u) + (dc/du)^T l ] = [ 0 ] - * [ -c(u) ] [ 0 ] + * F(X) = [ r(u) + (dc/du)^T l ] = [ 0 ] + * [ -c(u) ] [ 0 ] * X = [ u ] * [ l ] * * wherein r(u) is the elasticity nonlinear residual * c(u) are the tied gap contacts - * u are the displacement dofs - * l are the Lagrange multipliers - * - * we use the approximate Jacobian - * dF/dX \approx [ dr/du (dc/du)^T] - * [-dc/du 0 ] + * u are the displacement dofs + * l are the Lagrange multipliers * * This problem inherits from EqualityConstrainedHomotopyProblem * for compatibility with the HomotopySolver. @@ -63,6 +122,7 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::unique_ptr dcdu_; int dimu_; int dimc_; + int dimufull_; mfem::Array y_partition_; std::vector contact_states_; std::vector all_states_; @@ -73,16 +133,20 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { double time_ = 0.0; double dt_ = 0.0; std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; - + std::unique_ptr R; + std::unique_ptr P; + mutable mfem::Vector ufull_; public: TiedContactProblem(std::vector contact_states, std::vector all_states, std::shared_ptr mesh, std::shared_ptr weak_form, - std::shared_ptr constraints); + std::shared_ptr constraints, + mfem::Array ess_tdof_list); mfem::Vector residual(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; + //void SetDisplacementDirichletData(const Vector & delta, Array essbdr); virtual ~TiedContactProblem(); }; @@ -149,20 +213,21 @@ int main(int argc, char* argv[]) auto solid_mechanics_weak_form = std::make_shared(physics_name, mesh, states[FIELD::DISP].space(), getSpaces(params)); - SolidMaterial mat; - mat.K = 1.0; - mat.G = 0.5; + SolidMaterial mat{10.0, 10.0}; 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(1)); - solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.0 * n; }); + mesh->addDomainOfBoundaryElements(surface_name, serac::by_attr(6)); + solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.0e-2 * n; }); serac::tensor constant_force{}; - for (int i = 0; i < dim; i++) { - constant_force[i] = 1.e0; - } + constant_force[0] = 0.0; + constant_force[1] = 0.0; + constant_force[2] = 0.0; + //for (int i = 0; i < dim; i++) { + // constant_force[i] = (double) i; + //} solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { return serac::tuple{constant_force, 0.0 * serac::get(x)}; @@ -171,10 +236,36 @@ int main(int argc, char* argv[]) auto all_states = serac::getConstFieldPointers(states, params); auto non_const_states = serac::getFieldPointers(states, params); auto contact_state_ptrs = serac::getFieldPointers(contact_states); + + mfem::Array ess_bdr_marker(mesh->mfemParMesh().bdr_attributes.Max()); + ess_bdr_marker = 0; + //ess_bdr_marker[1-1] = 1; + //ess_bdr_marker[2-1] = 1; + ess_bdr_marker[3] = 1; + ess_bdr_marker[6] = 1; + //ess_bdr_marker[7] = 1; + mfem::Array ess_fixed_tdof_list; + + //Array ess_driven_tdof_list; + states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_fixed_tdof_list); + std::cout << "|tdof_list| = " << ess_fixed_tdof_list.Size() << std::endl; + std::cout << "numd dofs = " << states[FIELD::DISP].space().GetTrueVSize() << std::endl; + + //for (int i = 0; i < ess_fixed_tdof_list.Size(); i++) + //{ + // std::cout << "tdof = " << ess_fixed_tdof_list[i] << std::endl; + //} + //ess_bdr_marker = 0; + //ess_bdr_marker[6] = 1; + //states[FIELD::DISP]->space().GetEssentialTrueDofs(ess_bdr_marker, ess_driven_tdof_list); + + TiedContactProblem problem(contact_state_ptrs, non_const_states, mesh, solid_mechanics_weak_form, - contact_constraint); + contact_constraint, ess_fixed_tdof_list); if (true) { double nonlinear_absolute_tol = 1.e-4; + // double beta0 = 1.0; + // double delta_MAX = 5.0; int nonlinear_max_iterations = 30; int nonlinear_print_level = 1; // optimization variables @@ -185,23 +276,59 @@ int main(int argc, char* argv[]) solver.SetTol(nonlinear_absolute_tol); solver.SetMaxIter(nonlinear_max_iterations); solver.SetPrintLevel(nonlinear_print_level); + // solver.SetNeighborhoodParameter(beta0); + // solver.SetDeltaMax(delta_MAX); solver.Mult(X0, Xf); bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); } else { + + // finite difference check on residual + // check r(u0 + eps * udir) - r(u0) / eps - dr/dr(u0) * udir = O(eps) + + auto dimu = problem.GetDisplacementDim(); - mfem::Vector u(dimu); - u = 0.0; + mfem::Vector u0(dimu); + u0 = 1.0; + mfem::Vector u1(dimu); + u1 = 0.0; + mfem::Vector udir(dimu); + udir.Randomize(); udir *= 1.e-1; bool new_point = true; - auto gap = problem.constraint(u, new_point); - auto gapJacobian = problem.constraintJacobian(u, new_point); - double gapJacobianFNorm = gapJacobian->FNorm(); - if (gapJacobianFNorm > 1.e-12) { - auto adjoint = problem.GetOptimizationVariable(); - auto adjoint_load = problem.GetOptimizationVariable(); - adjoint_load = 1.0; - problem.AdjointSolve(u, adjoint_load, adjoint); + auto res0 = problem.residual(u0, new_point); + + auto resJacobian = problem.residualJacobian(u0, new_point); + + + mfem::Vector resJacobianudir(resJacobian->Height());resJacobianudir = 0.0; + mfem::Vector error(resJacobian->Height()); error = 0.0; + resJacobian->Mult(udir, resJacobianudir); + double eps = 1.0; + for (int i = 0; i < 30; i++) { + u1.Set(1.0, u0); + u1.Add(eps, udir); + + auto res1 = problem.residual(u1, new_point); + error.Set(1.0, res1); + error.Add(-1.0, res0); + error /= eps; + error.Add(-1.0, resJacobianudir); + double err = mfem::GlobalLpNorm(mfem::infinity(), error.Normlinf(), MPI_COMM_WORLD); + SLIC_INFO_ROOT(axom::fmt::format( + "|| (res(u0 + eps * udir)||_inf = {}, eps = {}", res1.Normlinf(), eps)); + SLIC_INFO_ROOT(axom::fmt::format( + "|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_inf = {}, eps = {}", error.Normlinf(), eps)); + eps /= 2.0; } + //SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); + + // double gapJacobianFNorm = gapJacobian->FNorm(); + // if (gapJacobianFNorm > 1.e-12) { + // auto adjoint = problem.GetOptimizationVariable(); + // auto adjoint_load = problem.GetOptimizationVariable(); + // adjoint_load = 1.0; + // problem.AdjointSolve(u, adjoint_load, adjoint); + // } } return 0; } @@ -211,9 +338,12 @@ TiedContactProblem::TiedContactProblem(std::vector all_states, std::shared_ptr mesh, std::shared_ptr weak_form, - std::shared_ptr constraints) + std::shared_ptr constraints, + mfem::Array ess_tdof_list) : EqualityConstrainedHomotopyProblem(), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) { + + // copy states all_states_.resize(all_states.size()); std::copy(all_states.begin(), all_states.end(), all_states_.begin()); @@ -223,8 +353,26 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueDofOffsets(); - dimu_ = all_states[FIELD::DISP]->space().GetTrueVSize(); + //HYPRE_BigInt* uOffsets = all_states[FIELD::DISP]->space().GetTrueDofOffsets(); + dimufull_ = all_states[FIELD::DISP]->space().GetTrueVSize(); + ufull_.SetSize(dimufull_); ufull_ = 0.0; + dimu_ = dimufull_ - ess_tdof_list.Size(); + // TODO: memory manage uOffsets + std::unique_ptr uOffsets; + uOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); + // mask out essential tdofs + HYPRE_Int mask[dimufull_]; + for (int i = 0; i < dimufull_; i++) + { + mask[i] = 1; + } + for(int i = 0; i < ess_tdof_list.Size(); i++) + { + mask[i] = 0; + } + R.reset(GenerateProjector(uOffsets.get(), all_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask)); + + P.reset(R->Transpose()); // obtain pressure dof information dimc_ = constraints_->numPressureDofs(); @@ -235,37 +383,63 @@ TiedContactProblem::TiedContactProblem(std::vector(mesh->newShapeDisplacement()); + } template -mfem::Vector TiedContactProblem::residual(const mfem::Vector& u, bool /*new_point*/) const +mfem::Vector TiedContactProblem::residual(const mfem::Vector & u, bool /*new_point*/) const { - contact_states_[serac::ContactFields::DISP]->Set(1.0, u); - auto res_vector = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); - return res_vector; -} + // 1. prolongate u --> u_prolongated + // 2. obtain residual via u_prolongated + // 3. restrict residual + ufull_ = 0.0; + mfem::Vector res_vector(dimu_); res_vector = 0.0; + P->Mult(u, ufull_); + //contact_states_[1]->Set(1.0, u); + all_states_[FIELD::DISP]->Set(1.0, ufull_); + //contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); + mfem::Vector res(dimu_); res = 0.0; + R->Mult(resfull, res); + return res; +}; + + template -mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool new_point) +mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) { - if (!new_point) { - return drdu_.get(); - } - contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + //if (!new_point) { + // return drdu_.get(); + //} + //ufull_ = 0.0; + //P->Mult(u, ufull_); + //contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + ////drdu_.reset(); + //auto drdufull_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); + //drdu_.reset(mfem::RAP(drdufull_.get(), P.get())); + //MFEM_VERIFY(drdu_->Height() == dimu_, "weak form Jacobian/TiedContactProblem displacement dofs inconsistent sizes"); + //return drdu_.get(); + //contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + + ufull_ = 0.0; + P->Mult(u, ufull_); + all_states_[FIELD::DISP]->Set(1.0, u); drdu_.reset(); drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); - MFEM_VERIFY(drdu_->Height() == dimu_, "weak form Jacobian/TiedContactProblem displacement dofs inconsistent sizes"); return drdu_.get(); } template mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u, bool new_point) const { - contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + ufull_ = 0.0; + P->Mult(u, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); auto gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point); return gap; } @@ -273,13 +447,12 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto template mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u, bool new_point) { - if (!new_point) { - return dcdu_.get(); - } - contact_states_[serac::ContactFields::DISP]->Set(1.0, u); - dcdu_.reset(); - dcdu_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, + ufull_ = 0.0; + P->Mult(u, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, new_point); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), true)); return dcdu_.get(); } @@ -287,10 +460,14 @@ template mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const { - contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + ufull_ = 0.0; + P->Mult(u, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); auto res_contribution = constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), l, serac::ContactFields::DISP, new_point); - return res_contribution; + mfem::Vector res(dimu_); res = 0.0; + R->Mult(res_contribution, res); + return res; } template From 399c85d6740d29467ef42460b7b56bfd6449adf0 Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 6 Nov 2025 17:19:29 -0800 Subject: [PATCH 37/82] full commit --- ContinuationSolvers | 2 +- src/serac/physics/contact_constraint.hpp | 29 ++++++++++++++++++++---- tribol | 2 +- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index ca82277fb..3eb9a58b2 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit ca82277fb4d2ee31de9ad8b6e5601a4922002d4f +Subproject commit 3eb9a58b2ebbd6f9ea4e0410a9ad148d2c98e184 diff --git a/src/serac/physics/contact_constraint.hpp b/src/serac/physics/contact_constraint.hpp index 444949f1d..3530769e3 100644 --- a/src/serac/physics/contact_constraint.hpp +++ b/src/serac/physics/contact_constraint.hpp @@ -135,6 +135,7 @@ class ContactConstraint : public Constraint { // note: Tribol does not use cycle. int cycle = 0; contact_.update(cycle, time, dt); + pressures_set_ = false; } auto gaps_hpv = contact_.mergedGaps(false); // Note: this copy is needed to prevent the HypreParVector pointer from going out of scope. see @@ -163,6 +164,7 @@ class ContactConstraint : public Constraint { int cycle = 0; contact_.update(cycle, time, dt); J_contact_ = contact_.mergedJacobian(); + pressures_set_ = false; } int iblock = 1; int jblock = 0; @@ -189,13 +191,17 @@ class ContactConstraint : public Constraint { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); + int cycle = 0; if (new_point) { - int cycle = 0; // we need to call update first to update gaps for (auto& interaction : contact_.getContactInteractions()) { interaction.evalJacobian(false); } contact_.update(cycle, time, dt); + pressures_set_ = false; + } + if (!pressures_set_) + { // with updated gaps, we can update pressure for contact interactions with penalty enforcement contact_.setPressures(multipliers); // call update again with the right pressures @@ -203,6 +209,7 @@ class ContactConstraint : public Constraint { interaction.evalJacobian(true); } contact_.update(cycle, time, dt); + pressures_set_ = true; } return contact_.forces(); @@ -228,13 +235,17 @@ class ContactConstraint : public Constraint { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); + int cycle = 0; if (new_point) { - int cycle = 0; // we need to call update first to update gaps for (auto& interaction : contact_.getContactInteractions()) { interaction.evalJacobian(false); } contact_.update(cycle, time, dt); + pressures_set_ = false; + } + if (!pressures_set_) + { // with updated gaps, we can update pressure for contact interactions with penalty enforcement contact_.setPressures(multipliers); // call update again with the right pressures @@ -242,8 +253,8 @@ class ContactConstraint : public Constraint { interaction.evalJacobian(true); } contact_.update(cycle, time, dt); - J_contact_ = contact_.mergedJacobian(); + pressures_set_ = true; } int iblock = 0; int jblock = 0; @@ -267,11 +278,12 @@ class ContactConstraint : public Constraint { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); + int cycle = 0; if (new_point) { - int cycle = 0; contact_.update(cycle, time, dt); J_contact_.reset(); J_contact_ = contact_.mergedJacobian(); + pressures_set_ = false; } int iblock = 0; int jblock = 1; @@ -304,9 +316,16 @@ class ContactConstraint : public Constraint { mutable std::unique_ptr J_contact_; /** - * @brief bool + * @brief own_blocks_ temporary boolean */ const bool own_blocks_ = true; + + /** + * @brief pressures_set_ are the Lagrange multipliers set + */ + mutable bool pressures_set_ = false; + + }; } // namespace serac diff --git a/tribol b/tribol index 1f0ea2a9f..6bbc0f662 160000 --- a/tribol +++ b/tribol @@ -1 +1 @@ -Subproject commit 1f0ea2a9f5a3efbf1d3281af66abd6515b05b78b +Subproject commit 6bbc0f66268361d88e55ba6b52aa21f2479dd1f8 From bb24929ecfbb8e7c8f8707d846f39227da54a39f Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 13 Nov 2025 12:38:54 -0800 Subject: [PATCH 38/82] updating constraint twist example, linearized contact and no contact examples --- .../contact/homotopy/constraint_twist.cpp | 435 +++++++++++------- 1 file changed, 273 insertions(+), 162 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index fabc1b573..2fd2071b1 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -25,71 +25,71 @@ static constexpr int dim = 3; static constexpr int disp_order = 1; +bool linearized_contact = true; +bool contact = false; + using VectorSpace = serac::H1; using DensitySpace = serac::L2; -using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; - -//struct MyLinearIsotropic { -// using State = serac::Empty; ///< this material has no internal variables -// -// /** -// * @brief stress calculation for a linear isotropic material model -// * -// * When applied to 2D displacement gradients, the stress is computed in plane strain, -// * returning only the in-plane components. -// * -// * @tparam T Number-like type for the displacement gradient components -// * @tparam dim Dimensionality of space -// * @param du_dX Displacement gradient with respect to the reference configuration -// * @return The stress -// */ -// template -// SERAC_HOST_DEVICE auto operator()(State& /* state */, const serac::tensor& du_dX) const -// { -// auto I = serac::Identity(); -// auto lambda = K - (2.0 / 3.0) * G; -// auto epsilon = 0.5 * (transpose(du_dX) + du_dX); -// return lambda * tr(epsilon) * I + 2.0 * G * epsilon; -// } -// template -// SERAC_HOST_DEVICE auto pkStress(State& /* state */, const serac::tensor& du_dX, const Density&) const -// { -// auto I = serac::Identity(); -// auto lambda = K - (2.0 / 3.0) * G; -// auto epsilon = 0.5 * (transpose(du_dX) + du_dX); -// return lambda * tr(epsilon) * I + 2.0 * G * epsilon; -// //using std::log1p; -// //constexpr auto I = Identity(); -// //auto lambda = K - (2.0 / 3.0) * G; -// //auto B_minus_I = dot(du_dX, transpose(du_dX)) + transpose(du_dX) + du_dX; -// -// //auto logJ = log1p(detApIm1(du_dX)); -// //// Kirchoff stress, in form that avoids cancellation error when F is near I -// //auto TK = lambda * logJ * I + G * B_minus_I; -// -// //// Pull back to Piola -// //auto F = du_dX + I; -// //return dot(TK, inv(transpose(F))); -// } -// -// /// @brief interpolates density field -// template -// SERAC_HOST_DEVICE auto density(const Density& density) const -// { -// return get(density); -// } -// -// //double density; ///< mass density -// double K; ///< bulk modulus -// double G; ///< shear modulus -//}; -// -//using SolidMaterial = MyLinearIsotropic; +// using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; + +struct MyLinearIsotropic { + using State = serac::Empty; ///< this material has no internal variables + + /** + * @brief stress calculation for a linear isotropic material model + * + * When applied to 2D displacement gradients, the stress is computed in plane strain, + * returning only the in-plane components. + * + * @tparam T Number-like type for the displacement gradient components + * @tparam dim Dimensionality of space + * @param du_dX Displacement gradient with respect to the reference configuration + * @return The stress + */ + template + SERAC_HOST_DEVICE auto operator()(State& /* state */, const serac::tensor& du_dX) const + { + auto I = serac::Identity(); + auto lambda = K - (2.0 / 3.0) * G; + auto epsilon = 0.5 * (transpose(du_dX) + du_dX); + return lambda * tr(epsilon) * I + 2.0 * G * epsilon; + } + template + SERAC_HOST_DEVICE auto pkStress(State& /* state */, const serac::tensor& du_dX, const Density&) const + { + auto I = serac::Identity(); + auto lambda = K - (2.0 / 3.0) * G; + auto epsilon = 0.5 * (transpose(du_dX) + du_dX); + return lambda * tr(epsilon) * I + 2.0 * G * epsilon; + // using std::log1p; + // constexpr auto I = Identity(); + // auto lambda = K - (2.0 / 3.0) * G; + // auto B_minus_I = dot(du_dX, transpose(du_dX)) + transpose(du_dX) + du_dX; + + // auto logJ = log1p(detApIm1(du_dX)); + //// Kirchoff stress, in form that avoids cancellation error when F is near I + // auto TK = lambda * logJ * I + G * B_minus_I; + + //// Pull back to Piola + // auto F = du_dX + I; + // return dot(TK, inv(transpose(F))); + } + /// @brief interpolates density field + template + SERAC_HOST_DEVICE auto density(const Density& density) const + { + return get(density); + } + // double density; ///< mass density + double K; ///< bulk modulus + double G; ///< shear modulus +}; +using SolidMaterial = MyLinearIsotropic; using SolidWeakFormT = serac::SolidWeakForm>; @@ -136,20 +136,89 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::unique_ptr R; std::unique_ptr P; mutable mfem::Vector ufull_; + mfem::Vector g0; + public: TiedContactProblem(std::vector contact_states, std::vector all_states, std::shared_ptr mesh, std::shared_ptr weak_form, - std::shared_ptr constraints, - mfem::Array ess_tdof_list); + std::shared_ptr constraints, mfem::Array ess_tdof_list); mfem::Vector residual(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; - //void SetDisplacementDirichletData(const Vector & delta, Array essbdr); + void fullDisplacement(const mfem::Vector& x, mfem::Vector& u); virtual ~TiedContactProblem(); }; +class ParaviewWriter { + public: + using StateVecs = std::vector>; + using DualVecs = std::vector>; + + ParaviewWriter(std::unique_ptr pv_, const StateVecs& states_) + : pv(std::move(pv_)), states(states_) + { + } + + ParaviewWriter(std::unique_ptr pv_, const StateVecs& states_, const StateVecs& duals_) + : pv(std::move(pv_)), states(states_), dual_states(duals_) + { + } + + void write(int step, double time, const std::vector& current_states) + { + SERAC_MARK_FUNCTION; + SLIC_ERROR_ROOT_IF(current_states.size() != states.size(), "wrong number of output states to write"); + + for (size_t n = 0; n < states.size(); ++n) { + auto& state = states[n]; + *state = *current_states[n]; + state->gridFunction(); + } + + pv->SetCycle(step); + pv->SetTime(time); + pv->Save(); + } + + private: + std::unique_ptr pv; + StateVecs states; + StateVecs dual_states; +}; + +auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector& states, + std::string output_name) +{ + if (output_name == "") { + output_name = "default"; + } + + ParaviewWriter::StateVecs output_states; + for (const auto& s : states) { + output_states.push_back(std::make_shared(s->space(), s->name())); + } + + auto non_const_mesh = const_cast(&mesh); + auto paraview_dc = std::make_unique(output_name, non_const_mesh); + int max_order_in_fields = 0; + + // Find the maximum polynomial order in the physics module's states + for (const auto& state : output_states) { + paraview_dc->RegisterField(state->name(), &state->gridFunction()); + max_order_in_fields = std::max(max_order_in_fields, state->space().GetOrder(0)); + } + + // Set the options for the paraview output files + paraview_dc->SetLevelsOfDetail(max_order_in_fields); + paraview_dc->SetHighOrderOutput(true); + paraview_dc->SetDataFormat(mfem::VTKFormat::BINARY); + paraview_dc->SetCompression(true); + + return ParaviewWriter(std::move(paraview_dc), output_states, {}); +} + // this example is intended to eventually replace twist.cpp int main(int argc, char* argv[]) { @@ -219,15 +288,15 @@ int main(int argc, char* argv[]) // apply some traction boundary conditions std::string surface_name = "side"; mesh->addDomainOfBoundaryElements(surface_name, serac::by_attr(6)); - solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.0e-2 * n; }); + solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.e-2 * n; }); serac::tensor constant_force{}; constant_force[0] = 0.0; constant_force[1] = 0.0; constant_force[2] = 0.0; - //for (int i = 0; i < dim; i++) { - // constant_force[i] = (double) i; - //} + // for (int i = 0; i < dim; i++) { + // constant_force[i] = (double) i; + // } solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { return serac::tuple{constant_force, 0.0 * serac::get(x)}; @@ -236,29 +305,20 @@ int main(int argc, char* argv[]) auto all_states = serac::getConstFieldPointers(states, params); auto non_const_states = serac::getFieldPointers(states, params); auto contact_state_ptrs = serac::getFieldPointers(contact_states); - + mfem::Array ess_bdr_marker(mesh->mfemParMesh().bdr_attributes.Max()); ess_bdr_marker = 0; - //ess_bdr_marker[1-1] = 1; - //ess_bdr_marker[2-1] = 1; - ess_bdr_marker[3] = 1; - ess_bdr_marker[6] = 1; - //ess_bdr_marker[7] = 1; + ess_bdr_marker[2] = 1; + ess_bdr_marker[5] = 1; + if (!contact) { + ess_bdr_marker[3] = 1; + ess_bdr_marker[4] = 1; + } mfem::Array ess_fixed_tdof_list; - - //Array ess_driven_tdof_list; + states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_fixed_tdof_list); std::cout << "|tdof_list| = " << ess_fixed_tdof_list.Size() << std::endl; std::cout << "numd dofs = " << states[FIELD::DISP].space().GetTrueVSize() << std::endl; - - //for (int i = 0; i < ess_fixed_tdof_list.Size(); i++) - //{ - // std::cout << "tdof = " << ess_fixed_tdof_list[i] << std::endl; - //} - //ess_bdr_marker = 0; - //ess_bdr_marker[6] = 1; - //states[FIELD::DISP]->space().GetEssentialTrueDofs(ess_bdr_marker, ess_driven_tdof_list); - TiedContactProblem problem(contact_state_ptrs, non_const_states, mesh, solid_mechanics_weak_form, contact_constraint, ess_fixed_tdof_list); @@ -278,30 +338,38 @@ int main(int argc, char* argv[]) solver.SetPrintLevel(nonlinear_print_level); // solver.SetNeighborhoodParameter(beta0); // solver.SetDeltaMax(delta_MAX); + // auto objective_states = {states[FIELD::DISP], states[FIELD::DENSITY]}; + auto writer = createParaviewOutput(mesh->mfemParMesh(), all_states, "contact"); + writer.write(0, 0.0, all_states); solver.Mult(X0, Xf); bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); + mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); + u = 0.0; + problem.fullDisplacement(Xf, u); + states[FIELD::DISP].Set(1.0, u); + writer.write(1, 1.0, all_states); } else { - // finite difference check on residual - // check r(u0 + eps * udir) - r(u0) / eps - dr/dr(u0) * udir = O(eps) - - + // check r(u0 + eps * udir) - r(u0) / eps - dr/dr(u0) * udir = O(eps) + auto dimu = problem.GetDisplacementDim(); mfem::Vector u0(dimu); u0 = 1.0; mfem::Vector u1(dimu); u1 = 0.0; mfem::Vector udir(dimu); - udir.Randomize(); udir *= 1.e-1; + udir.Randomize(); + udir *= 1.e-1; bool new_point = true; auto res0 = problem.residual(u0, new_point); auto resJacobian = problem.residualJacobian(u0, new_point); - - - mfem::Vector resJacobianudir(resJacobian->Height());resJacobianudir = 0.0; - mfem::Vector error(resJacobian->Height()); error = 0.0; + + mfem::Vector resJacobianudir(resJacobian->Height()); + resJacobianudir = 0.0; + mfem::Vector error(resJacobian->Height()); + error = 0.0; resJacobian->Mult(udir, resJacobianudir); double eps = 1.0; for (int i = 0; i < 30; i++) { @@ -314,13 +382,12 @@ int main(int argc, char* argv[]) error /= eps; error.Add(-1.0, resJacobianudir); double err = mfem::GlobalLpNorm(mfem::infinity(), error.Normlinf(), MPI_COMM_WORLD); - SLIC_INFO_ROOT(axom::fmt::format( - "|| (res(u0 + eps * udir)||_inf = {}, eps = {}", res1.Normlinf(), eps)); - SLIC_INFO_ROOT(axom::fmt::format( - "|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_inf = {}, eps = {}", error.Normlinf(), eps)); + SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir)||_inf = {}, eps = {}", res1.Normlinf(), eps)); + SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_inf = {}, eps = {}", + error.Normlinf(), eps)); eps /= 2.0; } - //SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); + // SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); // double gapJacobianFNorm = gapJacobian->FNorm(); // if (gapJacobianFNorm > 1.e-12) { @@ -339,11 +406,9 @@ TiedContactProblem::TiedContactProblem(std::vector mesh, std::shared_ptr weak_form, std::shared_ptr constraints, - mfem::Array ess_tdof_list) + mfem::Array ess_tdof_list) : EqualityConstrainedHomotopyProblem(), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) { - - // copy states all_states_.resize(all_states.size()); std::copy(all_states.begin(), all_states.end(), all_states_.begin()); @@ -353,21 +418,20 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueDofOffsets(); dimufull_ = all_states[FIELD::DISP]->space().GetTrueVSize(); - ufull_.SetSize(dimufull_); ufull_ = 0.0; + ufull_.SetSize(dimufull_); + ufull_ = 0.0; dimu_ = dimufull_ - ess_tdof_list.Size(); - // TODO: memory manage uOffsets + std::unique_ptr uOffsets; uOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); + // mask out essential tdofs HYPRE_Int mask[dimufull_]; - for (int i = 0; i < dimufull_; i++) - { + for (int i = 0; i < dimufull_; i++) { mask[i] = 1; } - for(int i = 0; i < ess_tdof_list.Size(); i++) - { + for (int i = 0; i < ess_tdof_list.Size(); i++) { mask[i] = 0; } R.reset(GenerateProjector(uOffsets.get(), all_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask)); @@ -375,84 +439,116 @@ TiedContactProblem::TiedContactProblem(std::vectorTranspose()); // obtain pressure dof information - dimc_ = constraints_->numPressureDofs(); - HYPRE_BigInt pressure_offset = 0; - MPI_Scan(&dimc_, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); HYPRE_BigInt cOffsets[2]; - cOffsets[1] = pressure_offset; - cOffsets[0] = pressure_offset - dimc_; - + if (contact) { + dimc_ = constraints_->numPressureDofs(); + HYPRE_BigInt pressure_offset = 0; + MPI_Scan(&dimc_, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + cOffsets[0] = pressure_offset - dimc_; + cOffsets[1] = pressure_offset; + } else { + dimc_ = 0; + cOffsets[0] = 0; + cOffsets[1] = 0; + } // set pressure and displacement dof information SetSizes(uOffsets.get(), cOffsets); // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); + if (contact) { + // Linearize + if (linearized_contact) { + mfem::Vector u0(dimu_); + u0 = 0.0; + P->Mult(u0, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + g0.SetSize(dimc_); + g0.Set(1.0, + constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); + + auto dcdufull_ = + constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, + true); // new_point); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), true)); + // dcdu_->Print("gapJacobian"); + } + } else { + int nentries = 0; + int dimuglb_; + dimuglb_ = 0; + MPI_Allreduce(&dimu_, &dimuglb_, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + auto temp = std::make_unique(dimc_, dimuglb_, nentries); + dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(cOffsets, uOffsets.get(), temp.get())); + } } template -mfem::Vector TiedContactProblem::residual(const mfem::Vector & u, bool /*new_point*/) const +mfem::Vector TiedContactProblem::residual(const mfem::Vector& u, bool /*new_point*/) const { - // 1. prolongate u --> u_prolongated - // 2. obtain residual via u_prolongated - // 3. restrict residual - ufull_ = 0.0; - mfem::Vector res_vector(dimu_); res_vector = 0.0; - P->Mult(u, ufull_); - //contact_states_[1]->Set(1.0, u); - all_states_[FIELD::DISP]->Set(1.0, ufull_); - //contact_states_[serac::ContactFields::DISP]->Set(1.0, u); - auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); - mfem::Vector res(dimu_); res = 0.0; - R->Mult(resfull, res); - return res; + // 1. prolongate u --> u_prolongated + // 2. obtain residual via u_prolongated + // 3. restrict residual + ufull_ = 0.0; + mfem::Vector res_vector(dimu_); + res_vector = 0.0; + P->Mult(u, ufull_); + // contact_states_[1]->Set(1.0, u); + all_states_[FIELD::DISP]->Set(1.0, ufull_); + // contact_states_[serac::ContactFields::DISP]->Set(1.0, u); + auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); + mfem::Vector res(dimu_); + res = 0.0; + R->Mult(resfull, res); + return res; }; - - template mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) { - //if (!new_point) { - // return drdu_.get(); - //} - //ufull_ = 0.0; - //P->Mult(u, ufull_); - //contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - ////drdu_.reset(); - //auto drdufull_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); - //drdu_.reset(mfem::RAP(drdufull_.get(), P.get())); - //MFEM_VERIFY(drdu_->Height() == dimu_, "weak form Jacobian/TiedContactProblem displacement dofs inconsistent sizes"); - //return drdu_.get(); - //contact_states_[serac::ContactFields::DISP]->Set(1.0, u); - ufull_ = 0.0; - P->Mult(u, ufull_); - all_states_[FIELD::DISP]->Set(1.0, u); - drdu_.reset(); - drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); + P->Mult(u, ufull_); + all_states_[FIELD::DISP]->Set(1.0, u); + // drdu_.reset(); + auto drdufull_ = + weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); + drdu_.reset(RAP(drdufull_.get(), P.get())); return drdu_.get(); } template mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u, bool new_point) const { - ufull_ = 0.0; - P->Mult(u, ufull_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - auto gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point); + mfem::Vector gap(dimc_); + gap = 0.0; + if (contact) { + if (linearized_contact) { + dcdu_->Mult(u, gap); + gap.Add(1.0, g0); + } else { + P->Mult(u, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + gap.Set(1.0, + constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); + } + } return gap; } template mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u, bool new_point) { - ufull_ = 0.0; - P->Mult(u, ufull_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, - new_point); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), true)); + if (contact) { + if (!linearized_contact) { + P->Mult(u, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + auto dcdufull_ = + constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, + true); // new_point); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), true)); + } + } return dcdu_.get(); } @@ -460,16 +556,31 @@ template mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const { - ufull_ = 0.0; - P->Mult(u, ufull_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - auto res_contribution = constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), - l, serac::ContactFields::DISP, new_point); - mfem::Vector res(dimu_); res = 0.0; - R->Mult(res_contribution, res); + mfem::Vector res(dimu_); + res = 0.0; + if (contact) { + if (linearized_contact) { + dcdu_->MultTranspose(l, res); + } else { + P->Mult(u, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + auto res_contribution = + constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), l, + serac::ContactFields::DISP, true); // new_point); + R->Mult(res_contribution, res); + } + } return res; } +template +void TiedContactProblem::fullDisplacement(const mfem::Vector& y, mfem::Vector& u) +{ + mfem::BlockVector yblock(y_partition); + yblock.Set(1.0, y); + P->Mult(yblock.GetBlock(0), u); +} + template TiedContactProblem::~TiedContactProblem() { From fd3479f626ef1d0a435f3270004406be6e9f1e1d Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 13 Nov 2025 14:49:21 -0800 Subject: [PATCH 39/82] bug fix --- .../contact/homotopy/constraint_twist.cpp | 36 +++++++------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 2fd2071b1..7658efa82 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -32,7 +32,7 @@ using VectorSpace = serac::H1; using DensitySpace = serac::L2; -// using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; +//using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; struct MyLinearIsotropic { using State = serac::Empty; ///< this material has no internal variables @@ -267,7 +267,7 @@ int main(int argc, char* argv[]) // initialize displacement contact_states[serac::ContactFields::DISP].setFromFieldFunction([](serac::tensor x) { - auto u = 0.0 * x; // 0.1 * x; + auto u = 0.0 * x; return u; }); @@ -291,12 +291,9 @@ int main(int argc, char* argv[]) solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.e-2 * n; }); serac::tensor constant_force{}; - constant_force[0] = 0.0; + constant_force[0] = 1.0; constant_force[1] = 0.0; constant_force[2] = 0.0; - // for (int i = 0; i < dim; i++) { - // constant_force[i] = (double) i; - // } solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { return serac::tuple{constant_force, 0.0 * serac::get(x)}; @@ -306,6 +303,7 @@ int main(int argc, char* argv[]) auto non_const_states = serac::getFieldPointers(states, params); auto contact_state_ptrs = serac::getFieldPointers(contact_states); + // boundary conditions mfem::Array ess_bdr_marker(mesh->mfemParMesh().bdr_attributes.Max()); ess_bdr_marker = 0; ess_bdr_marker[2] = 1; @@ -315,15 +313,12 @@ int main(int argc, char* argv[]) ess_bdr_marker[4] = 1; } mfem::Array ess_fixed_tdof_list; - states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_fixed_tdof_list); - std::cout << "|tdof_list| = " << ess_fixed_tdof_list.Size() << std::endl; - std::cout << "numd dofs = " << states[FIELD::DISP].space().GetTrueVSize() << std::endl; TiedContactProblem problem(contact_state_ptrs, non_const_states, mesh, solid_mechanics_weak_form, contact_constraint, ess_fixed_tdof_list); if (true) { - double nonlinear_absolute_tol = 1.e-4; + double nonlinear_absolute_tol = 1.e-6; // double beta0 = 1.0; // double delta_MAX = 5.0; int nonlinear_max_iterations = 30; @@ -338,17 +333,17 @@ int main(int argc, char* argv[]) solver.SetPrintLevel(nonlinear_print_level); // solver.SetNeighborhoodParameter(beta0); // solver.SetDeltaMax(delta_MAX); - // auto objective_states = {states[FIELD::DISP], states[FIELD::DENSITY]}; - auto writer = createParaviewOutput(mesh->mfemParMesh(), all_states, "contact"); - writer.write(0, 0.0, all_states); + + + auto writer = createParaviewOutput(mesh->mfemParMesh(), serac::getConstFieldPointers(states), "contact"); + writer.write(0, 0.0, serac::getConstFieldPointers(states)); solver.Mult(X0, Xf); bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); - u = 0.0; problem.fullDisplacement(Xf, u); states[FIELD::DISP].Set(1.0, u); - writer.write(1, 1.0, all_states); + writer.write(1, 1.0, serac::getConstFieldPointers(states)); } else { // finite difference check on residual // check r(u0 + eps * udir) - r(u0) / eps - dr/dr(u0) * udir = O(eps) @@ -375,7 +370,6 @@ int main(int argc, char* argv[]) for (int i = 0; i < 30; i++) { u1.Set(1.0, u0); u1.Add(eps, udir); - auto res1 = problem.residual(u1, new_point); error.Set(1.0, res1); error.Add(-1.0, res0); @@ -387,9 +381,7 @@ int main(int argc, char* argv[]) error.Normlinf(), eps)); eps /= 2.0; } - // SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); - - // double gapJacobianFNorm = gapJacobian->FNorm(); + SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); // if (gapJacobianFNorm > 1.e-12) { // auto adjoint = problem.GetOptimizationVariable(); // auto adjoint_load = problem.GetOptimizationVariable(); @@ -432,12 +424,11 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueDofOffsets(), mask)); P.reset(R->Transpose()); - // obtain pressure dof information HYPRE_BigInt cOffsets[2]; if (contact) { @@ -472,7 +463,6 @@ TiedContactProblem::TiedContactProblem(std::vectorjacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, true); // new_point); dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), true)); - // dcdu_->Print("gapJacobian"); } } else { int nentries = 0; @@ -509,7 +499,7 @@ mfem::HypreParMatrix* TiedContactProblem::residualJacobian(co { ufull_ = 0.0; P->Mult(u, ufull_); - all_states_[FIELD::DISP]->Set(1.0, u); + all_states_[FIELD::DISP]->Set(1.0, ufull_); // drdu_.reset(); auto drdufull_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); From 552d118fcebb657303f50a549f121b9b1c0840a8 Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 13 Nov 2025 14:50:49 -0800 Subject: [PATCH 40/82] formatting --- examples/contact/homotopy/constraint_twist.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 7658efa82..4873332ca 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -32,7 +32,7 @@ using VectorSpace = serac::H1; using DensitySpace = serac::L2; -//using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; +// using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; struct MyLinearIsotropic { using State = serac::Empty; ///< this material has no internal variables @@ -333,8 +333,7 @@ int main(int argc, char* argv[]) solver.SetPrintLevel(nonlinear_print_level); // solver.SetNeighborhoodParameter(beta0); // solver.SetDeltaMax(delta_MAX); - - + auto writer = createParaviewOutput(mesh->mfemParMesh(), serac::getConstFieldPointers(states), "contact"); writer.write(0, 0.0, serac::getConstFieldPointers(states)); solver.Mult(X0, Xf); From c7ec3431aa2af0dc3b03903ab9d511ed0d8b08d2 Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 13 Nov 2025 15:42:28 -0800 Subject: [PATCH 41/82] getting rid of warning messages --- .../contact/homotopy/constraint_twist.cpp | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 4873332ca..fa557efd4 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -26,13 +26,12 @@ static constexpr int dim = 3; static constexpr int disp_order = 1; bool linearized_contact = true; -bool contact = false; +bool contact = true; using VectorSpace = serac::H1; using DensitySpace = serac::L2; -// using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; struct MyLinearIsotropic { using State = serac::Empty; ///< this material has no internal variables @@ -90,6 +89,7 @@ struct MyLinearIsotropic { }; using SolidMaterial = MyLinearIsotropic; +//using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; using SolidWeakFormT = serac::SolidWeakForm>; @@ -122,7 +122,6 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::unique_ptr dcdu_; int dimu_; int dimc_; - int dimufull_; mfem::Array y_partition_; std::vector contact_states_; std::vector all_states_; @@ -377,7 +376,7 @@ int main(int argc, char* argv[]) double err = mfem::GlobalLpNorm(mfem::infinity(), error.Normlinf(), MPI_COMM_WORLD); SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir)||_inf = {}, eps = {}", res1.Normlinf(), eps)); SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_inf = {}, eps = {}", - error.Normlinf(), eps)); + err, eps)); eps /= 2.0; } SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); @@ -409,7 +408,7 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize(); + const int dimufull_ = all_states[FIELD::DISP]->space().GetTrueVSize(); ufull_.SetSize(dimufull_); ufull_ = 0.0; dimu_ = dimufull_ - ess_tdof_list.Size(); @@ -418,7 +417,7 @@ TiedContactProblem::TiedContactProblem(std::vector(dimufull_)]; for (int i = 0; i < dimufull_; i++) { mask[i] = 1; } @@ -426,7 +425,7 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueDofOffsets(), mask)); - + delete[] mask; P.reset(R->Transpose()); // obtain pressure dof information HYPRE_BigInt cOffsets[2]; @@ -461,6 +460,7 @@ TiedContactProblem::TiedContactProblem(std::vectorjacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, true); // new_point); + dcdufull_->Print("contactJacobian"); dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), true)); } } else { @@ -483,9 +483,7 @@ mfem::Vector TiedContactProblem::residual(const mfem::Vector& mfem::Vector res_vector(dimu_); res_vector = 0.0; P->Mult(u, ufull_); - // contact_states_[1]->Set(1.0, u); all_states_[FIELD::DISP]->Set(1.0, ufull_); - // contact_states_[serac::ContactFields::DISP]->Set(1.0, u); auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); mfem::Vector res(dimu_); res = 0.0; @@ -499,7 +497,6 @@ mfem::HypreParMatrix* TiedContactProblem::residualJacobian(co ufull_ = 0.0; P->Mult(u, ufull_); all_states_[FIELD::DISP]->Set(1.0, ufull_); - // drdu_.reset(); auto drdufull_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); drdu_.reset(RAP(drdufull_.get(), P.get())); @@ -507,8 +504,9 @@ mfem::HypreParMatrix* TiedContactProblem::residualJacobian(co } template -mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u, bool new_point) const +mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u, bool /*new_point*/) const { + bool new_point = true; mfem::Vector gap(dimc_); gap = 0.0; if (contact) { @@ -518,24 +516,24 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto } else { P->Mult(u, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - gap.Set(1.0, - constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); + gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point); } } return gap; } template -mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u, bool new_point) +mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u, bool /*new_point*/) { + bool new_point = true; if (contact) { if (!linearized_contact) { P->Mult(u, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, - true); // new_point); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), true)); + new_point); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), new_point)); } } return dcdu_.get(); @@ -543,8 +541,9 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( template mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, - bool new_point) const + bool /*new_point*/) const { + bool new_point = true; mfem::Vector res(dimu_); res = 0.0; if (contact) { @@ -555,7 +554,7 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); auto res_contribution = constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), l, - serac::ContactFields::DISP, true); // new_point); + serac::ContactFields::DISP, new_point); R->Mult(res_contribution, res); } } From 932cdb3205d7e936e3631913416a399f981abe20 Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 13 Nov 2025 20:06:38 -0800 Subject: [PATCH 42/82] more descriptive naming, proper conventions for member variables, more readable code --- .../contact/homotopy/constraint_twist.cpp | 92 ++++++++++--------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index fa557efd4..0d02b5507 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -122,7 +122,6 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::unique_ptr dcdu_; int dimu_; int dimc_; - mfem::Array y_partition_; std::vector contact_states_; std::vector all_states_; std::shared_ptr weak_form_; @@ -132,10 +131,10 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { double time_ = 0.0; double dt_ = 0.0; std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; - std::unique_ptr R; - std::unique_ptr P; + std::unique_ptr restriction_; + std::unique_ptr prolongation_; mutable mfem::Vector ufull_; - mfem::Vector g0; + mfem::Vector g0_; public: TiedContactProblem(std::vector contact_states, std::vector all_states, @@ -408,6 +407,8 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize(); ufull_.SetSize(dimufull_); ufull_ = 0.0; @@ -415,20 +416,9 @@ TiedContactProblem::TiedContactProblem(std::vector uOffsets; uOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); + std::unique_ptr cOffsets = std::make_unique(2); - // mask out essential tdofs - HYPRE_Int * mask = new HYPRE_Int[static_cast(dimufull_)]; - for (int i = 0; i < dimufull_; i++) { - mask[i] = 1; - } - for (int i = 0; i < ess_tdof_list.Size(); i++) { - mask[ess_tdof_list[i]] = 0; - } - R.reset(GenerateProjector(uOffsets.get(), all_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask)); - delete[] mask; - P.reset(R->Transpose()); // obtain pressure dof information - HYPRE_BigInt cOffsets[2]; if (contact) { dimc_ = constraints_->numPressureDofs(); HYPRE_BigInt pressure_offset = 0; @@ -441,27 +431,47 @@ TiedContactProblem::TiedContactProblem(std::vector ith dof will be made visible to solver + // these dofs will be made visiible via explicit use + // of prolongation/restriction operators in residual/constraint member functions + std::unique_ptr mask = std::make_unique(static_cast(dimufull_)); + for (int i = 0; i < dimufull_; i++) { + mask[static_cast(i)] = 1; + } + for (int i = 0; i < ess_tdof_list.Size(); i++) { + mask[static_cast(ess_tdof_list[i])] = 0; + } + restriction_.reset(GenerateProjector(uOffsets.get(), all_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask.get())); + + + prolongation_.reset(restriction_->Transpose()); + + + + // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); if (contact) { - // Linearize + // Linearize gap about zero displacement state if (linearized_contact) { mfem::Vector u0(dimu_); u0 = 0.0; - P->Mult(u0, ufull_); + prolongation_->Mult(u0, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - g0.SetSize(dimc_); - g0.Set(1.0, + g0_.SetSize(dimc_); + g0_.Set(1.0, constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, true); // new_point); - dcdufull_->Print("contactJacobian"); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), true)); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), true)); } } else { int nentries = 0; @@ -469,7 +479,7 @@ TiedContactProblem::TiedContactProblem(std::vector(dimc_, dimuglb_, nentries); - dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(cOffsets, uOffsets.get(), temp.get())); + dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(cOffsets.get(), uOffsets.get(), temp.get())); } } @@ -477,29 +487,25 @@ template mfem::Vector TiedContactProblem::residual(const mfem::Vector& u, bool /*new_point*/) const { // 1. prolongate u --> u_prolongated - // 2. obtain residual via u_prolongated + // 2. obtain full residual via u_prolongated // 3. restrict residual - ufull_ = 0.0; - mfem::Vector res_vector(dimu_); - res_vector = 0.0; - P->Mult(u, ufull_); + prolongation_->Mult(u, ufull_); all_states_[FIELD::DISP]->Set(1.0, ufull_); auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); mfem::Vector res(dimu_); res = 0.0; - R->Mult(resfull, res); + restriction_->Mult(resfull, res); return res; }; template mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) { - ufull_ = 0.0; - P->Mult(u, ufull_); + prolongation_->Mult(u, ufull_); all_states_[FIELD::DISP]->Set(1.0, ufull_); auto drdufull_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); - drdu_.reset(RAP(drdufull_.get(), P.get())); + drdu_.reset(RAP(drdufull_.get(), prolongation_.get())); return drdu_.get(); } @@ -512,9 +518,9 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto if (contact) { if (linearized_contact) { dcdu_->Mult(u, gap); - gap.Add(1.0, g0); + gap.Add(1.0, g0_); } else { - P->Mult(u, ufull_); + prolongation_->Mult(u, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point); } @@ -528,12 +534,12 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( bool new_point = true; if (contact) { if (!linearized_contact) { - P->Mult(u, ufull_); + prolongation_->Mult(u, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, new_point); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), P.get(), new_point)); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), new_point)); } } return dcdu_.get(); @@ -550,23 +556,23 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const if (linearized_contact) { dcdu_->MultTranspose(l, res); } else { - P->Mult(u, ufull_); + prolongation_->Mult(u, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); auto res_contribution = constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), l, serac::ContactFields::DISP, new_point); - R->Mult(res_contribution, res); + restriction_->Mult(res_contribution, res); } } return res; } template -void TiedContactProblem::fullDisplacement(const mfem::Vector& y, mfem::Vector& u) +void TiedContactProblem::fullDisplacement(const mfem::Vector& X, mfem::Vector& u) { - mfem::BlockVector yblock(y_partition); - yblock.Set(1.0, y); - P->Mult(yblock.GetBlock(0), u); + mfem::BlockVector Xblock(y_partition); + Xblock.Set(1.0, X); + prolongation_->Mult(Xblock.GetBlock(0), u); } template From 793c068a62a63910ce935a8b7ff35abfb4bb258f Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 13 Nov 2025 20:13:07 -0800 Subject: [PATCH 43/82] more descriptive residual_state variable name instead of all_states --- .../contact/homotopy/constraint_twist.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 0d02b5507..82befa182 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -123,7 +123,7 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { int dimu_; int dimc_; std::vector contact_states_; - std::vector all_states_; + std::vector residual_states_; std::shared_ptr weak_form_; std::unique_ptr shape_disp_; std::shared_ptr mesh_; @@ -137,7 +137,7 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { mfem::Vector g0_; public: - TiedContactProblem(std::vector contact_states, std::vector all_states, + TiedContactProblem(std::vector contact_states, std::vector residual_states, std::shared_ptr mesh, std::shared_ptr weak_form, std::shared_ptr constraints, mfem::Array ess_tdof_list); mfem::Vector residual(const mfem::Vector& u, bool new_point) const; @@ -298,7 +298,7 @@ int main(int argc, char* argv[]) }); auto all_states = serac::getConstFieldPointers(states, params); - auto non_const_states = serac::getFieldPointers(states, params); + auto residual_state_ptrs = serac::getFieldPointers(states, params); auto contact_state_ptrs = serac::getFieldPointers(contact_states); // boundary conditions @@ -313,7 +313,7 @@ int main(int argc, char* argv[]) mfem::Array ess_fixed_tdof_list; states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_fixed_tdof_list); - TiedContactProblem problem(contact_state_ptrs, non_const_states, mesh, solid_mechanics_weak_form, + TiedContactProblem problem(contact_state_ptrs, residual_state_ptrs, mesh, solid_mechanics_weak_form, contact_constraint, ess_fixed_tdof_list); if (true) { double nonlinear_absolute_tol = 1.e-6; @@ -391,16 +391,16 @@ int main(int argc, char* argv[]) template TiedContactProblem::TiedContactProblem(std::vector contact_states, - std::vector all_states, + std::vector residual_states, std::shared_ptr mesh, std::shared_ptr weak_form, std::shared_ptr constraints, mfem::Array ess_tdof_list) : EqualityConstrainedHomotopyProblem(), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) { - // copy states - all_states_.resize(all_states.size()); - std::copy(all_states.begin(), all_states.end(), all_states_.begin()); + // copy residual states + residual_states_.resize(residual_states.size()); + std::copy(residual_states.begin(), residual_states.end(), residual_states_.begin()); // copy contact states contact_states_.resize(contact_states.size()); @@ -409,7 +409,7 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize(); + const int dimufull_ = residual_states[FIELD::DISP]->space().GetTrueVSize(); ufull_.SetSize(dimufull_); ufull_ = 0.0; dimu_ = dimufull_ - ess_tdof_list.Size(); @@ -445,7 +445,7 @@ TiedContactProblem::TiedContactProblem(std::vector(ess_tdof_list[i])] = 0; } - restriction_.reset(GenerateProjector(uOffsets.get(), all_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask.get())); + restriction_.reset(GenerateProjector(uOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask.get())); prolongation_.reset(restriction_->Transpose()); @@ -490,8 +490,8 @@ mfem::Vector TiedContactProblem::residual(const mfem::Vector& // 2. obtain full residual via u_prolongated // 3. restrict residual prolongation_->Mult(u, ufull_); - all_states_[FIELD::DISP]->Set(1.0, ufull_); - auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(all_states_)); + residual_states_[FIELD::DISP]->Set(1.0, ufull_); + auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(residual_states_)); mfem::Vector res(dimu_); res = 0.0; restriction_->Mult(resfull, res); @@ -502,9 +502,9 @@ template mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) { prolongation_->Mult(u, ufull_); - all_states_[FIELD::DISP]->Set(1.0, ufull_); + residual_states_[FIELD::DISP]->Set(1.0, ufull_); auto drdufull_ = - weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); + weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(residual_states_), jacobian_weights_); drdu_.reset(RAP(drdufull_.get(), prolongation_.get())); return drdu_.get(); } From 54b90aa4869806f57cfb01ebe43a395465be3338 Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 13 Nov 2025 20:13:50 -0800 Subject: [PATCH 44/82] formatting --- .../contact/homotopy/constraint_twist.cpp | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 82befa182..d9545a83c 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -32,7 +32,6 @@ using VectorSpace = serac::H1; using DensitySpace = serac::L2; - struct MyLinearIsotropic { using State = serac::Empty; ///< this material has no internal variables @@ -89,7 +88,7 @@ struct MyLinearIsotropic { }; using SolidMaterial = MyLinearIsotropic; -//using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; +// using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; using SolidWeakFormT = serac::SolidWeakForm>; @@ -374,8 +373,8 @@ int main(int argc, char* argv[]) error.Add(-1.0, resJacobianudir); double err = mfem::GlobalLpNorm(mfem::infinity(), error.Normlinf(), MPI_COMM_WORLD); SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir)||_inf = {}, eps = {}", res1.Normlinf(), eps)); - SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_inf = {}, eps = {}", - err, eps)); + SLIC_INFO_ROOT( + axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_inf = {}, eps = {}", err, eps)); eps /= 2.0; } SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); @@ -432,8 +431,7 @@ TiedContactProblem::TiedContactProblem(std::vector ith dof will be made visible to solver // these dofs will be made visiible via explicit use @@ -445,14 +443,10 @@ TiedContactProblem::TiedContactProblem(std::vector(ess_tdof_list[i])] = 0; } - restriction_.reset(GenerateProjector(uOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask.get())); - - - prolongation_.reset(restriction_->Transpose()); - - - + restriction_.reset( + GenerateProjector(uOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask.get())); + prolongation_.reset(restriction_->Transpose()); // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); @@ -466,7 +460,7 @@ TiedContactProblem::TiedContactProblem(std::vectorSet(1.0, ufull_); g0_.SetSize(dimc_); g0_.Set(1.0, - constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); + constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, @@ -529,16 +523,16 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto } template -mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u, bool /*new_point*/) +mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u, + bool /*new_point*/) { bool new_point = true; if (contact) { if (!linearized_contact) { prolongation_->Mult(u, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - auto dcdufull_ = - constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, - new_point); + auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), + serac::ContactFields::DISP, new_point); dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), new_point)); } } @@ -558,9 +552,8 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const } else { prolongation_->Mult(u, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - auto res_contribution = - constraints_->residual_contribution(time_, dt_, serac::getConstFieldPointers(contact_states_), l, - serac::ContactFields::DISP, new_point); + auto res_contribution = constraints_->residual_contribution( + time_, dt_, serac::getConstFieldPointers(contact_states_), l, serac::ContactFields::DISP, new_point); restriction_->Mult(res_contribution, res); } } From 047585921fede67fdb6ede5a7b2fb8ddde3392e4 Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 13 Nov 2025 20:48:47 -0800 Subject: [PATCH 45/82] small cleanup --- examples/contact/homotopy/constraint_twist.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index d9545a83c..6bd31226b 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -296,7 +296,6 @@ int main(int argc, char* argv[]) return serac::tuple{constant_force, 0.0 * serac::get(x)}; }); - auto all_states = serac::getConstFieldPointers(states, params); auto residual_state_ptrs = serac::getFieldPointers(states, params); auto contact_state_ptrs = serac::getFieldPointers(contact_states); @@ -371,10 +370,11 @@ int main(int argc, char* argv[]) error.Add(-1.0, res0); error /= eps; error.Add(-1.0, resJacobianudir); - double err = mfem::GlobalLpNorm(mfem::infinity(), error.Normlinf(), MPI_COMM_WORLD); - SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir)||_inf = {}, eps = {}", res1.Normlinf(), eps)); + double err = mfem::GlobalLpNorm(2, error.Norml2(), MPI_COMM_WORLD); + double res_norm = mfem::GlobalLpNorm(2, res1.Norml2(), MPI_COMM_WORLD); + SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir)||_2 = {}, eps = {}", res_norm, eps)); SLIC_INFO_ROOT( - axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_inf = {}, eps = {}", err, eps)); + axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_2 = {}, eps = {}", err, eps)); eps /= 2.0; } SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); From 8d8bd2945f12a1f60bb3203dae7385c57f7f478b Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 17 Nov 2025 11:03:57 -0800 Subject: [PATCH 46/82] command line options, Frictionless --> TiedNormal contact in order that we get all gap computations. fd check at u0 in order to not tangle the mesh when using homogeneous Dirichlet conditions. Changing the forcing term --- .../contact/homotopy/constraint_twist.cpp | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 6bd31226b..7f8a15342 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -25,8 +25,8 @@ static constexpr int dim = 3; static constexpr int disp_order = 1; -bool linearized_contact = true; -bool contact = true; +int linearized_contact = 1; +int contact = 0; using VectorSpace = serac::H1; @@ -222,6 +222,22 @@ int main(int argc, char* argv[]) // Initialize and automatically finalize MPI and other libraries serac::ApplicationManager applicationManager(argc, argv); + int fd_check = 0; // finite difference check or homotopy solve + // command line arguments + axom::CLI::App app{"Constraint twist."}; + app.add_option("--contact", contact, "enable contact (1) or use Dirichlet BCs for contact surface (0)") + ->default_val("0") + ->check(axom::CLI::Range(0, 1)); + app.add_option("--lincontact", linearized_contact, "use linearized contact (1) or nonlinear contact (0)") + ->default_val("1") + ->check(axom::CLI::Range(0, 1)); + app.add_option("--fdcheck", fd_check, "finite difference check (1) or Homotopy solve (0)") + ->default_val("1") + ->check(axom::CLI::Range(0, 1)); + app.set_help_flag("--help"); + + CLI11_PARSE(app, argc, argv); + // Create DataStore std::string name = "contact_twist_example"; axom::sidre::DataStore datastore; @@ -236,7 +252,7 @@ int main(int argc, char* argv[]) serac::ContactOptions contact_options{.method = serac::ContactMethod::SingleMortar, .enforcement = serac::ContactEnforcement::LagrangeMultiplier, - .type = serac::ContactType::Frictionless, + .type = serac::ContactType::TiedNormal, .jacobian = serac::ContactJacobian::Exact}; std::string contact_constraint_name = "default_contact"; @@ -279,18 +295,22 @@ int main(int argc, char* argv[]) auto solid_mechanics_weak_form = std::make_shared(physics_name, mesh, states[FIELD::DISP].space(), getSpaces(params)); - SolidMaterial mat{10.0, 10.0}; + 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(6)); - solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.e-2 * n; }); + // std::string surface_name = "side"; + // mesh->addDomainOfBoundaryElements(surface_name, serac::by_attr(6)); + // solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.e-2 * n; }); serac::tensor constant_force{}; - constant_force[0] = 1.0; - constant_force[1] = 0.0; - constant_force[2] = 0.0; + // constant_force[0] = 1.0; + for (int i = 0; i < dim; i++) { + constant_force[i] = 0.0; + } + constant_force[dim - 1] = -0.1; solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { return serac::tuple{constant_force, 0.0 * serac::get(x)}; @@ -299,7 +319,7 @@ int main(int argc, char* argv[]) auto residual_state_ptrs = serac::getFieldPointers(states, params); auto contact_state_ptrs = serac::getFieldPointers(contact_states); - // boundary conditions + // homogeneous Dirichlet boundary conditions mfem::Array ess_bdr_marker(mesh->mfemParMesh().bdr_attributes.Max()); ess_bdr_marker = 0; ess_bdr_marker[2] = 1; @@ -313,7 +333,7 @@ int main(int argc, char* argv[]) TiedContactProblem problem(contact_state_ptrs, residual_state_ptrs, mesh, solid_mechanics_weak_form, contact_constraint, ess_fixed_tdof_list); - if (true) { + if (!fd_check) { double nonlinear_absolute_tol = 1.e-6; // double beta0 = 1.0; // double delta_MAX = 5.0; @@ -341,11 +361,13 @@ int main(int argc, char* argv[]) writer.write(1, 1.0, serac::getConstFieldPointers(states)); } else { // finite difference check on residual - // check r(u0 + eps * udir) - r(u0) / eps - dr/dr(u0) * udir = O(eps) + // check that the finite difference quotient residual + // r(u0 + eps * udir) - r(u0) / eps - dr/dr(u0) * udir + // is O(eps) auto dimu = problem.GetDisplacementDim(); mfem::Vector u0(dimu); - u0 = 1.0; + u0 = 0.0; mfem::Vector u1(dimu); u1 = 0.0; mfem::Vector udir(dimu); @@ -536,6 +558,7 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), new_point)); } } + SLIC_INFO_ROOT(axom::fmt::format("|| contact gap Jacobian ||_F = {}", dcdu_->FNorm())); return dcdu_.get(); } From 807db0c849b6d9bff736465e829f6a1c358732b7 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 17 Nov 2025 11:37:44 -0800 Subject: [PATCH 47/82] visualize option, fd check on gap constraint --- .../contact/homotopy/constraint_twist.cpp | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 7f8a15342..0bd063fed 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -223,6 +223,7 @@ int main(int argc, char* argv[]) serac::ApplicationManager applicationManager(argc, argv); int fd_check = 0; // finite difference check or homotopy solve + int visualize = 1; // command line arguments axom::CLI::App app{"Constraint twist."}; app.add_option("--contact", contact, "enable contact (1) or use Dirichlet BCs for contact surface (0)") @@ -232,7 +233,10 @@ int main(int argc, char* argv[]) ->default_val("1") ->check(axom::CLI::Range(0, 1)); app.add_option("--fdcheck", fd_check, "finite difference check (1) or Homotopy solve (0)") - ->default_val("1") + ->default_val("0") + ->check(axom::CLI::Range(0, 1)); + app.add_option("--visualize", visualize, "solution visualization") + ->default_val("1") // Matches value set above ->check(axom::CLI::Range(0, 1)); app.set_help_flag("--help"); @@ -349,16 +353,20 @@ int main(int argc, char* argv[]) solver.SetPrintLevel(nonlinear_print_level); // solver.SetNeighborhoodParameter(beta0); // solver.SetDeltaMax(delta_MAX); - - auto writer = createParaviewOutput(mesh->mfemParMesh(), serac::getConstFieldPointers(states), "contact"); - writer.write(0, 0.0, serac::getConstFieldPointers(states)); solver.Mult(X0, Xf); bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); - mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); - problem.fullDisplacement(Xf, u); - states[FIELD::DISP].Set(1.0, u); - writer.write(1, 1.0, serac::getConstFieldPointers(states)); + + auto writer = createParaviewOutput(mesh->mfemParMesh(), serac::getConstFieldPointers(states), "contact"); + if (visualize) { + mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); + problem.fullDisplacement(X0, u); + states[FIELD::DISP].Set(1.0, u); + writer.write(0, 0.0, serac::getConstFieldPointers(states)); + problem.fullDisplacement(Xf, u); + states[FIELD::DISP].Set(1.0, u); + writer.write(1, 1.0, serac::getConstFieldPointers(states)); + } } else { // finite difference check on residual // check that the finite difference quotient residual @@ -374,9 +382,9 @@ int main(int argc, char* argv[]) udir.Randomize(); udir *= 1.e-1; bool new_point = true; - auto res0 = problem.residual(u0, new_point); + auto res0 = problem.constraint(u0, new_point); - auto resJacobian = problem.residualJacobian(u0, new_point); + auto resJacobian = problem.constraintJacobian(u0, new_point); mfem::Vector resJacobianudir(resJacobian->Height()); resJacobianudir = 0.0; @@ -387,7 +395,7 @@ int main(int argc, char* argv[]) for (int i = 0; i < 30; i++) { u1.Set(1.0, u0); u1.Add(eps, udir); - auto res1 = problem.residual(u1, new_point); + auto res1 = problem.constraint(u1, new_point); error.Set(1.0, res1); error.Add(-1.0, res0); error /= eps; From 99f5ec93dc1e05aeb020218a239e0af23d42685b Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 18 Nov 2025 14:41:49 -0800 Subject: [PATCH 48/82] finite difference check along each iterate direction --- ContinuationSolvers | 2 +- .../contact/homotopy/constraint_twist.cpp | 63 +++++++++++++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 3eb9a58b2..213864d6c 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 3eb9a58b2ebbd6f9ea4e0410a9ad148d2c98e184 +Subproject commit 213864d6ceeb6555579df7c9de14104e6a5a8412 diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 0bd063fed..fd005d18f 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -224,6 +224,7 @@ int main(int argc, char* argv[]) int fd_check = 0; // finite difference check or homotopy solve int visualize = 1; + int visualize_all_iterates = 1; // command line arguments axom::CLI::App app{"Constraint twist."}; app.add_option("--contact", contact, "enable contact (1) or use Dirichlet BCs for contact surface (0)") @@ -314,7 +315,7 @@ int main(int argc, char* argv[]) for (int i = 0; i < dim; i++) { constant_force[i] = 0.0; } - constant_force[dim - 1] = -0.1; + constant_force[dim - 1] = -0.01; solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { return serac::tuple{constant_force, 0.0 * serac::get(x)}; @@ -353,6 +354,7 @@ int main(int argc, char* argv[]) solver.SetPrintLevel(nonlinear_print_level); // solver.SetNeighborhoodParameter(beta0); // solver.SetDeltaMax(delta_MAX); + solver.EnableSaveIterates(); solver.Mult(X0, Xf); bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); @@ -363,9 +365,62 @@ int main(int argc, char* argv[]) problem.fullDisplacement(X0, u); states[FIELD::DISP].Set(1.0, u); writer.write(0, 0.0, serac::getConstFieldPointers(states)); - problem.fullDisplacement(Xf, u); - states[FIELD::DISP].Set(1.0, u); - writer.write(1, 1.0, serac::getConstFieldPointers(states)); + if (!visualize_all_iterates) { + problem.fullDisplacement(Xf, u); + states[FIELD::DISP].Set(1.0, u); + writer.write(1, 1.0, serac::getConstFieldPointers(states)); + } + else { + // TODO: write iterates of the solver + auto iterates = solver.GetIterates(); + for (int i = 0; i < iterates.Size(); i++) + { + problem.fullDisplacement(*iterates[i], u); + states[FIELD::DISP].Set(1.0, u); + writer.write((i+1), static_cast(i+1), serac::getConstFieldPointers(states)); + } + + // finite difference check along each displacement u_(i+1) - u_i + auto dimu = problem.GetDisplacementDim(); + for (int j = 0; j < iterates.Size() - 1; j++) { + mfem::Vector u0(dimu); + u0 = 0.0; + u0.Set(1.0, *iterates[j]); + mfem::Vector u1(dimu); + u1 = 0.0; + bool new_point = true; + mfem::Vector udir(dimu); + udir.Set(1.0, *iterates[j+1]); + udir.Add(-1.0, *iterates[j]); + //udir.Randomize(); + //udir *= 1.e-1; + auto res0 = problem.constraint(u0, new_point); + + auto resJacobian = problem.constraintJacobian(u0, new_point); + + mfem::Vector resJacobianudir(resJacobian->Height()); + resJacobianudir = 0.0; + mfem::Vector error(resJacobian->Height()); + error = 0.0; + resJacobian->Mult(udir, resJacobianudir); + double eps = 1.0; + for (int i = 0; i < 30; i++) { + u1.Set(1.0, u0); + u1.Add(eps, udir); + auto res1 = problem.constraint(u1, new_point); + error.Set(1.0, res1); + error.Add(-1.0, res0); + error /= eps; + error.Add(-1.0, resJacobianudir); + double err = mfem::GlobalLpNorm(2, error.Norml2(), MPI_COMM_WORLD); + double res_norm = mfem::GlobalLpNorm(2, res1.Norml2(), MPI_COMM_WORLD); + SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir)||_2 = {}, eps = {}", res_norm, eps)); + SLIC_INFO_ROOT( + axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_2 = {}, eps = {}", err, eps)); + eps /= 2.0; + } + } + } } } else { // finite difference check on residual From f5439cb478ab2209762cba3d3586a183ded5543b Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 19 Nov 2025 08:55:57 -0800 Subject: [PATCH 49/82] saving displacement BC progress before working on this same item on another machine. --- .../contact/homotopy/constraint_twist.cpp | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index fd005d18f..18225f5ea 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -132,13 +132,19 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; std::unique_ptr restriction_; std::unique_ptr prolongation_; + std::unique_ptr disp_restriction_; + std::unique_ptr disp_prolongation_; mutable mfem::Vector ufull_; mfem::Vector g0_; + mfem::Vector dispBC_; public: TiedContactProblem(std::vector contact_states, std::vector residual_states, std::shared_ptr mesh, std::shared_ptr weak_form, - std::shared_ptr constraints, mfem::Array ess_tdof_list); + std::shared_ptr constraints, + mfem::Array disp_ess_tdof_list, + mfem::Vector dispBC, + mfem::Array ess_tdof_list); mfem::Vector residual(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; @@ -479,6 +485,8 @@ TiedContactProblem::TiedContactProblem(std::vector mesh, std::shared_ptr weak_form, std::shared_ptr constraints, + mfem::Array disp_ess_tdof_list, + mfem::Vector dispBC, mfem::Array ess_tdof_list) : EqualityConstrainedHomotopyProblem(), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) { @@ -496,7 +504,7 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize(); ufull_.SetSize(dimufull_); ufull_ = 0.0; - dimu_ = dimufull_ - ess_tdof_list.Size(); + dimu_ = dimufull_ - ess_tdof_list.Size() - disp_ess_tdof_list.Size(); std::unique_ptr uOffsets; uOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); @@ -519,7 +527,7 @@ TiedContactProblem::TiedContactProblem(std::vector ith dof will be made visible to solver - // these dofs will be made visiible via explicit use + // these dofs will be made visible via explicit use // of prolongation/restriction operators in residual/constraint member functions std::unique_ptr mask = std::make_unique(static_cast(dimufull_)); for (int i = 0; i < dimufull_; i++) { @@ -528,11 +536,24 @@ TiedContactProblem::TiedContactProblem(std::vector(ess_tdof_list[i])] = 0; } + for (int i = 0; i < disp_ess_tdof_list.Size(); i++) { + mask[static_cast(disp_ess_tdof_list[i])] = 0; + } + // TODO: check that the intersection of ess_tdof_list and disp_ess_tdof_list is empty! + restriction_.reset( GenerateProjector(uOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask.get())); prolongation_.reset(restriction_->Transpose()); + // need disp offsets + std::unique_ptr dispuOffsets; + dispuOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); + // + + + + // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); From 44fbe4f94e2bb619c09b104556fabadaf80973e1 Mon Sep 17 00:00:00 2001 From: Tucker Hartland Date: Wed, 19 Nov 2025 08:58:45 -0800 Subject: [PATCH 50/82] pushing up changes --- .../contact/homotopy/constraint_twist.cpp | 85 +++++++++++++++---- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index fd005d18f..8680b9db7 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -134,6 +134,7 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::unique_ptr prolongation_; mutable mfem::Vector ufull_; mfem::Vector g0_; + mfem::Vector u0_; public: TiedContactProblem(std::vector contact_states, std::vector residual_states, @@ -145,6 +146,8 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; void fullDisplacement(const mfem::Vector& x, mfem::Vector& u); + void setLinearizationPoint(const mfem::Vector & u0); + void Linearize(); virtual ~TiedContactProblem(); }; @@ -315,7 +318,7 @@ int main(int argc, char* argv[]) for (int i = 0; i < dim; i++) { constant_force[i] = 0.0; } - constant_force[dim - 1] = -0.01; + constant_force[dim - 1] = -0.8; solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { return serac::tuple{constant_force, 0.0 * serac::get(x)}; @@ -348,6 +351,41 @@ int main(int argc, char* argv[]) auto X0 = problem.GetOptimizationVariable(); auto Xf = problem.GetOptimizationVariable(); + int num_lin_steps = 10; + if (!linearized_contact) + { + num_lin_steps = 1; + } + + for (int i = 0; i < num_lin_steps -1; i++) + { + HomotopySolver solver(&problem); + solver.SetTol(nonlinear_absolute_tol); + solver.SetMaxIter(nonlinear_max_iterations); + solver.SetPrintLevel(nonlinear_print_level); + // solver.SetNeighborhoodParameter(beta0); + // solver.SetDeltaMax(delta_MAX); + //solver.EnableSaveIterates(); + solver.Mult(X0, Xf); + bool converged = solver.GetConverged(); + SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); + + auto dimu = problem.GetDisplacementDim(); + mfem::Array offsets(3); + offsets[0] = 0; + offsets[1] = dimu; + offsets[2] = X0.Size() - offsets[1]; + offsets.PartialSum(); + mfem::BlockVector Xfblock(offsets); + Xfblock.Set(1.0, Xf); + mfem::Vector uf(dimu); + uf.Set(1.0, Xfblock.GetBlock(0)); + std::cout << "||uf|| = " << uf.Norml2() << std::endl; + problem.setLinearizationPoint(uf); + X0.Set(1.0, Xf); + } + + HomotopySolver solver(&problem); solver.SetTol(nonlinear_absolute_tol); solver.SetMaxIter(nonlinear_max_iterations); @@ -358,7 +396,6 @@ int main(int argc, char* argv[]) solver.Mult(X0, Xf); bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); - auto writer = createParaviewOutput(mesh->mfemParMesh(), serac::getConstFieldPointers(states), "contact"); if (visualize) { mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); @@ -539,18 +576,9 @@ TiedContactProblem::TiedContactProblem(std::vectorMult(u0, ufull_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - g0_.SetSize(dimc_); - g0_.Set(1.0, - constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); - - auto dcdufull_ = - constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, - true); // new_point); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), true)); + u0_.SetSize(dimu_); + u0_ = 0.0; + Linearize(); } } else { int nentries = 0; @@ -596,7 +624,10 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto gap = 0.0; if (contact) { if (linearized_contact) { - dcdu_->Mult(u, gap); + mfem::Vector du(dimu_); + du.Set(1.0, u); + du.Add(-1.0, u0_); + dcdu_->Mult(du, gap); gap.Add(1.0, g0_); } else { prolongation_->Mult(u, ufull_); @@ -625,6 +656,30 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( return dcdu_.get(); } +template +void TiedContactProblem::setLinearizationPoint(const mfem::Vector & u0) +{ + u0_.Set(1.0, u0); + Linearize(); +} + +template +void TiedContactProblem::Linearize() +{ + prolongation_->Mult(u0_, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + g0_.SetSize(dimc_); + g0_.Set(1.0, + constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); + + auto dcdufull_ = + constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, + true); // new_point); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), true)); +} + + + template mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool /*new_point*/) const From 42b1fd9f378090ce919e85c36741e5d1d715704a Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 19 Nov 2025 09:25:11 -0800 Subject: [PATCH 51/82] resolving conflict --- examples/contact/homotopy/constraint_twist.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 4bd37d3a1..212024199 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -136,11 +136,8 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::unique_ptr disp_prolongation_; mutable mfem::Vector ufull_; mfem::Vector g0_; -<<<<<<< HEAD mfem::Vector u0_; -======= mfem::Vector dispBC_; ->>>>>>> f5439cb478ab2209762cba3d3586a183ded5543b public: TiedContactProblem(std::vector contact_states, std::vector residual_states, From f4b70f976b112490b2f3f8dbf0cc86996845fda4 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 19 Nov 2025 09:54:31 -0800 Subject: [PATCH 52/82] working nonlinear elasticity/contact problem/ --- ContinuationSolvers | 2 +- .../contact/homotopy/constraint_twist.cpp | 38 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 213864d6c..86ba12ce0 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 213864d6ceeb6555579df7c9de14104e6a5a8412 +Subproject commit 86ba12ce04b99808a1d8d43a189965b0967c9a01 diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 212024199..52c121c29 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -87,8 +87,8 @@ struct MyLinearIsotropic { double G; ///< shear modulus }; -using SolidMaterial = MyLinearIsotropic; -// using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; +//using SolidMaterial = MyLinearIsotropic; +using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; using SolidWeakFormT = serac::SolidWeakForm>; @@ -343,10 +343,16 @@ int main(int argc, char* argv[]) ess_bdr_marker[4] = 1; } mfem::Array ess_fixed_tdof_list; + mfem::Array ess_disp_tdof_list; states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_fixed_tdof_list); + ess_bdr_marker = 0; + states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_disp_tdof_list); + mfem::Vector uDC(states[FIELD::DISP].space().GetTrueVSize()); uDC = 0.0; + + TiedContactProblem problem(contact_state_ptrs, residual_state_ptrs, mesh, solid_mechanics_weak_form, - contact_constraint, ess_fixed_tdof_list); + contact_constraint, ess_disp_tdof_list, uDC, ess_fixed_tdof_list); if (!fd_check) { double nonlinear_absolute_tol = 1.e-6; // double beta0 = 1.0; @@ -357,7 +363,7 @@ int main(int argc, char* argv[]) auto X0 = problem.GetOptimizationVariable(); auto Xf = problem.GetOptimizationVariable(); - int num_lin_steps = 10; + int num_lin_steps = 1; if (!linearized_contact) { num_lin_steps = 1; @@ -391,11 +397,12 @@ int main(int argc, char* argv[]) X0.Set(1.0, Xf); } - + double theta0 = 1.e-6; HomotopySolver solver(&problem); solver.SetTol(nonlinear_absolute_tol); solver.SetMaxIter(nonlinear_max_iterations); solver.SetPrintLevel(nonlinear_print_level); + solver.SetContinuationParameter(theta0); // solver.SetNeighborhoodParameter(beta0); // solver.SetDeltaMax(delta_MAX); solver.EnableSaveIterates(); @@ -541,6 +548,8 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize(); ufull_.SetSize(dimufull_); ufull_ = 0.0; + dispBC_.SetSize(dimufull_); + dispBC_.Set(1.0, dispBC); dimu_ = dimufull_ - ess_tdof_list.Size() - disp_ess_tdof_list.Size(); std::unique_ptr uOffsets; @@ -576,6 +585,17 @@ TiedContactProblem::TiedContactProblem(std::vector(disp_ess_tdof_list[i])] = 0; } + + // now mask out dofs that aren't disp BC dofs + std::unique_ptr dispmask = std::make_unique(static_cast(dimufull_)); + for (int i = 0; i < dimufull_; i++) + { + dispmask[static_cast(i)] = 0; + } + for (int i = 0; i < disp_ess_tdof_list.Size(); i++) { + dispmask[static_cast(disp_ess_tdof_list[i])] = 1; + } + // TODO: check that the intersection of ess_tdof_list and disp_ess_tdof_list is empty! restriction_.reset( @@ -585,11 +605,11 @@ TiedContactProblem::TiedContactProblem(std::vector dispuOffsets; - dispuOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); - // - + dispuOffsets.reset(offsetsFromLocalSizes(disp_ess_tdof_list.Size(), MPI_COMM_WORLD)); + disp_restriction_.reset( + GenerateProjector(dispuOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), dispmask.get())); - + disp_prolongation_.reset(disp_restriction_->Transpose()); // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); From 90977586a4bf16b412d0fbdfc549c693633a8a76 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 19 Nov 2025 10:50:59 -0800 Subject: [PATCH 53/82] initial cleanup following having working nonlinear contact solves via the homotopy solver --- .../contact/homotopy/constraint_twist.cpp | 246 ++++-------------- 1 file changed, 55 insertions(+), 191 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 52c121c29..4d596402c 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -87,7 +87,7 @@ struct MyLinearIsotropic { double G; ///< shear modulus }; -//using SolidMaterial = MyLinearIsotropic; +// using SolidMaterial = MyLinearIsotropic; using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; using SolidWeakFormT = serac::SolidWeakForm>; @@ -142,17 +142,15 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { public: TiedContactProblem(std::vector contact_states, std::vector residual_states, std::shared_ptr mesh, std::shared_ptr weak_form, - std::shared_ptr constraints, - mfem::Array disp_ess_tdof_list, - mfem::Vector dispBC, - mfem::Array ess_tdof_list); + std::shared_ptr constraints, mfem::Array disp_ess_tdof_list, + mfem::Vector dispBC, mfem::Array ess_tdof_list); mfem::Vector residual(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; void fullDisplacement(const mfem::Vector& x, mfem::Vector& u); - void setLinearizationPoint(const mfem::Vector & u0); + void setLinearizationPoint(const mfem::Vector& u0); void Linearize(); virtual ~TiedContactProblem(); }; @@ -347,178 +345,49 @@ int main(int argc, char* argv[]) states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_fixed_tdof_list); ess_bdr_marker = 0; states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_disp_tdof_list); - mfem::Vector uDC(states[FIELD::DISP].space().GetTrueVSize()); uDC = 0.0; - - + mfem::Vector uDC(states[FIELD::DISP].space().GetTrueVSize()); + uDC = 0.0; TiedContactProblem problem(contact_state_ptrs, residual_state_ptrs, mesh, solid_mechanics_weak_form, contact_constraint, ess_disp_tdof_list, uDC, ess_fixed_tdof_list); - if (!fd_check) { - double nonlinear_absolute_tol = 1.e-6; - // double beta0 = 1.0; - // double delta_MAX = 5.0; - int nonlinear_max_iterations = 30; - int nonlinear_print_level = 1; - // optimization variables - auto X0 = problem.GetOptimizationVariable(); - auto Xf = problem.GetOptimizationVariable(); - - int num_lin_steps = 1; - if (!linearized_contact) - { - num_lin_steps = 1; - } - - for (int i = 0; i < num_lin_steps -1; i++) - { - HomotopySolver solver(&problem); - solver.SetTol(nonlinear_absolute_tol); - solver.SetMaxIter(nonlinear_max_iterations); - solver.SetPrintLevel(nonlinear_print_level); - // solver.SetNeighborhoodParameter(beta0); - // solver.SetDeltaMax(delta_MAX); - //solver.EnableSaveIterates(); - solver.Mult(X0, Xf); - bool converged = solver.GetConverged(); - SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); - - auto dimu = problem.GetDisplacementDim(); - mfem::Array offsets(3); - offsets[0] = 0; - offsets[1] = dimu; - offsets[2] = X0.Size() - offsets[1]; - offsets.PartialSum(); - mfem::BlockVector Xfblock(offsets); - Xfblock.Set(1.0, Xf); - mfem::Vector uf(dimu); - uf.Set(1.0, Xfblock.GetBlock(0)); - std::cout << "||uf|| = " << uf.Norml2() << std::endl; - problem.setLinearizationPoint(uf); - X0.Set(1.0, Xf); - } - - double theta0 = 1.e-6; - HomotopySolver solver(&problem); - solver.SetTol(nonlinear_absolute_tol); - solver.SetMaxIter(nonlinear_max_iterations); - solver.SetPrintLevel(nonlinear_print_level); - solver.SetContinuationParameter(theta0); - // solver.SetNeighborhoodParameter(beta0); - // solver.SetDeltaMax(delta_MAX); - solver.EnableSaveIterates(); - solver.Mult(X0, Xf); - bool converged = solver.GetConverged(); - SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); - auto writer = createParaviewOutput(mesh->mfemParMesh(), serac::getConstFieldPointers(states), "contact"); - if (visualize) { - mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); - problem.fullDisplacement(X0, u); + double nonlinear_absolute_tol = 1.e-6; + int nonlinear_max_iterations = 30; + int nonlinear_print_level = 1; + // optimization variables + auto X0 = problem.GetOptimizationVariable(); + auto Xf = problem.GetOptimizationVariable(); + + double theta0 = 1.e-6; + HomotopySolver solver(&problem); + solver.SetTol(nonlinear_absolute_tol); + solver.SetMaxIter(nonlinear_max_iterations); + solver.SetPrintLevel(nonlinear_print_level); + solver.SetContinuationParameter(theta0); + solver.EnableSaveIterates(); + solver.Mult(X0, Xf); + bool converged = solver.GetConverged(); + SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); + + // visualize + auto writer = createParaviewOutput(mesh->mfemParMesh(), serac::getConstFieldPointers(states), "contact"); + if (visualize) { + mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); + problem.fullDisplacement(X0, u); + states[FIELD::DISP].Set(1.0, u); + writer.write(0, 0.0, serac::getConstFieldPointers(states)); + if (!visualize_all_iterates) { + problem.fullDisplacement(Xf, u); states[FIELD::DISP].Set(1.0, u); - writer.write(0, 0.0, serac::getConstFieldPointers(states)); - if (!visualize_all_iterates) { - problem.fullDisplacement(Xf, u); + writer.write(1, 1.0, serac::getConstFieldPointers(states)); + } else { + // TODO: write iterates of the solver + auto iterates = solver.GetIterates(); + for (int i = 0; i < iterates.Size(); i++) { + problem.fullDisplacement(*iterates[i], u); states[FIELD::DISP].Set(1.0, u); - writer.write(1, 1.0, serac::getConstFieldPointers(states)); + writer.write((i + 1), static_cast(i + 1), serac::getConstFieldPointers(states)); } - else { - // TODO: write iterates of the solver - auto iterates = solver.GetIterates(); - for (int i = 0; i < iterates.Size(); i++) - { - problem.fullDisplacement(*iterates[i], u); - states[FIELD::DISP].Set(1.0, u); - writer.write((i+1), static_cast(i+1), serac::getConstFieldPointers(states)); - } - - // finite difference check along each displacement u_(i+1) - u_i - auto dimu = problem.GetDisplacementDim(); - for (int j = 0; j < iterates.Size() - 1; j++) { - mfem::Vector u0(dimu); - u0 = 0.0; - u0.Set(1.0, *iterates[j]); - mfem::Vector u1(dimu); - u1 = 0.0; - bool new_point = true; - mfem::Vector udir(dimu); - udir.Set(1.0, *iterates[j+1]); - udir.Add(-1.0, *iterates[j]); - //udir.Randomize(); - //udir *= 1.e-1; - auto res0 = problem.constraint(u0, new_point); - - auto resJacobian = problem.constraintJacobian(u0, new_point); - - mfem::Vector resJacobianudir(resJacobian->Height()); - resJacobianudir = 0.0; - mfem::Vector error(resJacobian->Height()); - error = 0.0; - resJacobian->Mult(udir, resJacobianudir); - double eps = 1.0; - for (int i = 0; i < 30; i++) { - u1.Set(1.0, u0); - u1.Add(eps, udir); - auto res1 = problem.constraint(u1, new_point); - error.Set(1.0, res1); - error.Add(-1.0, res0); - error /= eps; - error.Add(-1.0, resJacobianudir); - double err = mfem::GlobalLpNorm(2, error.Norml2(), MPI_COMM_WORLD); - double res_norm = mfem::GlobalLpNorm(2, res1.Norml2(), MPI_COMM_WORLD); - SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir)||_2 = {}, eps = {}", res_norm, eps)); - SLIC_INFO_ROOT( - axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_2 = {}, eps = {}", err, eps)); - eps /= 2.0; - } - } - } - } - } else { - // finite difference check on residual - // check that the finite difference quotient residual - // r(u0 + eps * udir) - r(u0) / eps - dr/dr(u0) * udir - // is O(eps) - - auto dimu = problem.GetDisplacementDim(); - mfem::Vector u0(dimu); - u0 = 0.0; - mfem::Vector u1(dimu); - u1 = 0.0; - mfem::Vector udir(dimu); - udir.Randomize(); - udir *= 1.e-1; - bool new_point = true; - auto res0 = problem.constraint(u0, new_point); - - auto resJacobian = problem.constraintJacobian(u0, new_point); - - mfem::Vector resJacobianudir(resJacobian->Height()); - resJacobianudir = 0.0; - mfem::Vector error(resJacobian->Height()); - error = 0.0; - resJacobian->Mult(udir, resJacobianudir); - double eps = 1.0; - for (int i = 0; i < 30; i++) { - u1.Set(1.0, u0); - u1.Add(eps, udir); - auto res1 = problem.constraint(u1, new_point); - error.Set(1.0, res1); - error.Add(-1.0, res0); - error /= eps; - error.Add(-1.0, resJacobianudir); - double err = mfem::GlobalLpNorm(2, error.Norml2(), MPI_COMM_WORLD); - double res_norm = mfem::GlobalLpNorm(2, res1.Norml2(), MPI_COMM_WORLD); - SLIC_INFO_ROOT(axom::fmt::format("|| (res(u0 + eps * udir)||_2 = {}, eps = {}", res_norm, eps)); - SLIC_INFO_ROOT( - axom::fmt::format("|| (res(u0 + eps * udir) - res(u0)) / eps - J(u0) * udir||_2 = {}, eps = {}", err, eps)); - eps /= 2.0; } - SLIC_INFO_ROOT(axom::fmt::format("|| res Jacobian ||_F = {}", resJacobian->FNorm())); - // if (gapJacobianFNorm > 1.e-12) { - // auto adjoint = problem.GetOptimizationVariable(); - // auto adjoint_load = problem.GetOptimizationVariable(); - // adjoint_load = 1.0; - // problem.AdjointSolve(u, adjoint_load, adjoint); - // } } return 0; } @@ -529,8 +398,7 @@ TiedContactProblem::TiedContactProblem(std::vector mesh, std::shared_ptr weak_form, std::shared_ptr constraints, - mfem::Array disp_ess_tdof_list, - mfem::Vector dispBC, + mfem::Array disp_ess_tdof_list, mfem::Vector dispBC, mfem::Array ess_tdof_list) : EqualityConstrainedHomotopyProblem(), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) { @@ -588,8 +456,7 @@ TiedContactProblem::TiedContactProblem(std::vector dispmask = std::make_unique(static_cast(dimufull_)); - for (int i = 0; i < dimufull_; i++) - { + for (int i = 0; i < dimufull_; i++) { dispmask[static_cast(i)] = 0; } for (int i = 0; i < disp_ess_tdof_list.Size(); i++) { @@ -606,9 +473,9 @@ TiedContactProblem::TiedContactProblem(std::vector dispuOffsets; dispuOffsets.reset(offsetsFromLocalSizes(disp_ess_tdof_list.Size(), MPI_COMM_WORLD)); - disp_restriction_.reset( - GenerateProjector(dispuOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), dispmask.get())); - + disp_restriction_.reset(GenerateProjector( + dispuOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), dispmask.get())); + disp_prolongation_.reset(disp_restriction_->Transpose()); // shape_disp field @@ -698,7 +565,7 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( } template -void TiedContactProblem::setLinearizationPoint(const mfem::Vector & u0) +void TiedContactProblem::setLinearizationPoint(const mfem::Vector& u0) { u0_.Set(1.0, u0); Linearize(); @@ -707,20 +574,17 @@ void TiedContactProblem::setLinearizationPoint(const mfem::Ve template void TiedContactProblem::Linearize() { - prolongation_->Mult(u0_, ufull_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - g0_.SetSize(dimc_); - g0_.Set(1.0, - constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), true)); // new_point); - - auto dcdufull_ = - constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), serac::ContactFields::DISP, - true); // new_point); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), true)); + bool new_point = true; + prolongation_->Mult(u0_, ufull_); + contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + g0_.SetSize(dimc_); + g0_.Set(1.0, constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point)); + + auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), + serac::ContactFields::DISP, new_point); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), true)); } - - template mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool /*new_point*/) const From 45b19ef722d828c80ce31f44192a0c6794ec880c Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 19 Nov 2025 14:53:50 -0800 Subject: [PATCH 54/82] infastructure in place for nonlinear contact with displacement BCs. TODO: alter the driver in order to run such a problem --- examples/contact/homotopy/constraint_twist.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index 4d596402c..b0a996b82 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -352,17 +352,17 @@ int main(int argc, char* argv[]) contact_constraint, ess_disp_tdof_list, uDC, ess_fixed_tdof_list); double nonlinear_absolute_tol = 1.e-6; int nonlinear_max_iterations = 30; + double homotopy_solver_continuation_parameter = 1.e-6; // continuation parameter int nonlinear_print_level = 1; // optimization variables auto X0 = problem.GetOptimizationVariable(); auto Xf = problem.GetOptimizationVariable(); - double theta0 = 1.e-6; HomotopySolver solver(&problem); solver.SetTol(nonlinear_absolute_tol); solver.SetMaxIter(nonlinear_max_iterations); solver.SetPrintLevel(nonlinear_print_level); - solver.SetContinuationParameter(theta0); + solver.SetContinuationParameter(homotopy_solver_continuation_parameter); solver.EnableSaveIterates(); solver.Mult(X0, Xf); bool converged = solver.GetConverged(); @@ -478,6 +478,11 @@ TiedContactProblem::TiedContactProblem(std::vectorTranspose()); + // remove any nonzero entries in dispBC vec that are not strictly needed + mfem::Vector RdispBC(disp_restriction_->Height()); + disp_restriction_->Mult(dispBC_, RdispBC); + disp_prolongation_->Mult(RdispBC, dispBC_); + // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); @@ -505,6 +510,7 @@ mfem::Vector TiedContactProblem::residual(const mfem::Vector& // 2. obtain full residual via u_prolongated // 3. restrict residual prolongation_->Mult(u, ufull_); + ufull_.Add(1.0, dispBC_); residual_states_[FIELD::DISP]->Set(1.0, ufull_); auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(residual_states_)); mfem::Vector res(dimu_); @@ -517,6 +523,7 @@ template mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) { prolongation_->Mult(u, ufull_); + ufull_.Add(1.0, dispBC_); residual_states_[FIELD::DISP]->Set(1.0, ufull_); auto drdufull_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(residual_states_), jacobian_weights_); @@ -539,6 +546,7 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto gap.Add(1.0, g0_); } else { prolongation_->Mult(u, ufull_); + ufull_.Add(1.0, dispBC_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point); } @@ -553,6 +561,7 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( bool new_point = true; if (contact) { if (!linearized_contact) { + ufull_.Add(1.0, dispBC_); prolongation_->Mult(u, ufull_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), @@ -597,6 +606,7 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const dcdu_->MultTranspose(l, res); } else { prolongation_->Mult(u, ufull_); + ufull_.Add(1.0, dispBC_); contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); auto res_contribution = constraints_->residual_contribution( time_, dt_, serac::getConstFieldPointers(contact_states_), l, serac::ContactFields::DISP, new_point); From aaed0c7f078efc7bccceda70deb925bce02b98fd Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 20 Nov 2025 12:50:44 -0800 Subject: [PATCH 55/82] displacement boundary conditions --- .../contact/homotopy/constraint_twist.cpp | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index b0a996b82..bd1ef70d3 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -312,17 +312,11 @@ int main(int argc, char* argv[]) 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(6)); - // solid_mechanics_weak_form->addBoundaryFlux(surface_name, [](auto /*x*/, auto n, auto /*t*/) { return 1.e-2 * n; }); - serac::tensor constant_force{}; - // constant_force[0] = 1.0; for (int i = 0; i < dim; i++) { constant_force[i] = 0.0; } - constant_force[dim - 1] = -0.8; + constant_force[dim - 1] = 0.0; solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { return serac::tuple{constant_force, 0.0 * serac::get(x)}; @@ -332,27 +326,36 @@ int main(int argc, char* argv[]) auto contact_state_ptrs = serac::getFieldPointers(contact_states); // homogeneous Dirichlet boundary conditions + mfem::Array ess_fixed_tdof_list; + mfem::Array ess_disp_tdof_list; mfem::Array ess_bdr_marker(mesh->mfemParMesh().bdr_attributes.Max()); ess_bdr_marker = 0; ess_bdr_marker[2] = 1; - ess_bdr_marker[5] = 1; + ess_bdr_marker[5] = 0; if (!contact) { ess_bdr_marker[3] = 1; ess_bdr_marker[4] = 1; } - mfem::Array ess_fixed_tdof_list; - mfem::Array ess_disp_tdof_list; states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_fixed_tdof_list); ess_bdr_marker = 0; + ess_bdr_marker[5] = 1; states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_disp_tdof_list); mfem::Vector uDC(states[FIELD::DISP].space().GetTrueVSize()); uDC = 0.0; + auto applied_displacement = [](serac::tensor /*x*/) { + serac::tensor u{}; + u[0] = 0.0; + u[1] = 0.0; + u[2] = -0.06; + return u; + }; + states[FIELD::DISP].setFromFieldFunction(applied_displacement); + uDC.Set(1.0, states[FIELD::DISP]); TiedContactProblem problem(contact_state_ptrs, residual_state_ptrs, mesh, solid_mechanics_weak_form, contact_constraint, ess_disp_tdof_list, uDC, ess_fixed_tdof_list); double nonlinear_absolute_tol = 1.e-6; int nonlinear_max_iterations = 30; - double homotopy_solver_continuation_parameter = 1.e-6; // continuation parameter int nonlinear_print_level = 1; // optimization variables auto X0 = problem.GetOptimizationVariable(); @@ -362,7 +365,7 @@ int main(int argc, char* argv[]) solver.SetTol(nonlinear_absolute_tol); solver.SetMaxIter(nonlinear_max_iterations); solver.SetPrintLevel(nonlinear_print_level); - solver.SetContinuationParameter(homotopy_solver_continuation_parameter); + solver.EnableRegularizedNewtonMode(); solver.EnableSaveIterates(); solver.Mult(X0, Xf); bool converged = solver.GetConverged(); @@ -380,7 +383,6 @@ int main(int argc, char* argv[]) states[FIELD::DISP].Set(1.0, u); writer.write(1, 1.0, serac::getConstFieldPointers(states)); } else { - // TODO: write iterates of the solver auto iterates = solver.GetIterates(); for (int i = 0; i < iterates.Size(); i++) { problem.fullDisplacement(*iterates[i], u); @@ -463,8 +465,6 @@ TiedContactProblem::TiedContactProblem(std::vector(disp_ess_tdof_list[i])] = 1; } - // TODO: check that the intersection of ess_tdof_list and disp_ess_tdof_list is empty! - restriction_.reset( GenerateProjector(uOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask.get())); @@ -569,7 +569,6 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), new_point)); } } - SLIC_INFO_ROOT(axom::fmt::format("|| contact gap Jacobian ||_F = {}", dcdu_->FNorm())); return dcdu_.get(); } @@ -622,6 +621,7 @@ void TiedContactProblem::fullDisplacement(const mfem::Vector& mfem::BlockVector Xblock(y_partition); Xblock.Set(1.0, X); prolongation_->Mult(Xblock.GetBlock(0), u); + u.Add(1.0, dispBC_); } template From 6563fcbc2e0069fb8176cb909f461ad0c7daae7b Mon Sep 17 00:00:00 2001 From: thartland Date: Thu, 20 Nov 2025 12:54:21 -0800 Subject: [PATCH 56/82] updating ContinuationSolvers --- ContinuationSolvers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 86ba12ce0..57b2b5333 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 86ba12ce04b99808a1d8d43a189965b0967c9a01 +Subproject commit 57b2b5333edd1d78133c380c8fe0192da692d2c8 From e5821692f5ae0010c91427dc8dead1882ed0d5b3 Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 21 Nov 2025 12:46:49 -0800 Subject: [PATCH 57/82] consistent usage of RegularizedNewtonMode for solving nonlinear equations via the HomotopySolver --- examples/inertia_relief/inertia_relief_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index ccca5684d..ddcaabb10 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -322,7 +322,7 @@ int main(int argc, char* argv[]) // set solver options solver.SetTol(nonlinear_absolute_tol); solver.SetMaxIter(nonlinear_max_iterations); - + solver.EnableRegularizedNewtonMode(); // solve the inertia relief problem solver.SetPrintLevel(2); solver.Mult(X0, Xf); From 4a5590fd1feb5a6b70c0ada285c29b96ed09592e Mon Sep 17 00:00:00 2001 From: thartland Date: Sun, 23 Nov 2025 14:56:09 -0800 Subject: [PATCH 58/82] serac --> smith name change on contact/homotopy example --- examples/contact/homotopy/CMakeLists.txt | 8 +- .../contact/homotopy/constraint_twist.cpp | 152 +++++++++--------- 2 files changed, 80 insertions(+), 80 deletions(-) diff --git a/examples/contact/homotopy/CMakeLists.txt b/examples/contact/homotopy/CMakeLists.txt index 49a98e17c..355b734e5 100644 --- a/examples/contact/homotopy/CMakeLists.txt +++ b/examples/contact/homotopy/CMakeLists.txt @@ -4,19 +4,19 @@ # # SPDX-License-Identifier: (BSD-3-Clause) -if (TRIBOL_FOUND AND STRUMPACK_DIR AND SERAC_ENABLE_CONTINUATION) +if (TRIBOL_FOUND AND STRUMPACK_DIR AND SMITH_ENABLE_CONTINUATION) blt_add_executable( NAME constraint_twist_example SOURCES constraint_twist.cpp OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} - DEPENDS_ON serac_physics serac_mesh_utils continuation_solver + DEPENDS_ON smith_physics smith_mesh_utils continuation_solver ) - serac_add_example_test(NAME constraint_twist + smith_add_example_test(NAME constraint_twist COMMAND constraint_twist_example NUM_MPI_TASKS 4) install( FILES constraint_twist.cpp DESTINATION - examples/serac/constraint_twist + examples/smith/constraint_twist ) endif() diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/constraint_twist.cpp index bd1ef70d3..b4fcae5c2 100644 --- a/examples/contact/homotopy/constraint_twist.cpp +++ b/examples/contact/homotopy/constraint_twist.cpp @@ -12,7 +12,7 @@ #include "axom/slic.hpp" #include "mfem.hpp" -#include "serac/physics/contact/contact_config.hpp" +#include "smith/physics/contact/contact_config.hpp" #include "shared/mesh/MeshBuilder.hpp" // ContinuationSolver headers @@ -20,7 +20,7 @@ #include "solvers/HomotopySolver.hpp" #include "utilities.hpp" -#include "serac/serac.hpp" +#include "smith/smith.hpp" static constexpr int dim = 3; static constexpr int disp_order = 1; @@ -28,12 +28,12 @@ static constexpr int disp_order = 1; int linearized_contact = 1; int contact = 0; -using VectorSpace = serac::H1; +using VectorSpace = smith::H1; -using DensitySpace = serac::L2; +using DensitySpace = smith::L2; struct MyLinearIsotropic { - using State = serac::Empty; ///< this material has no internal variables + using State = smith::Empty; ///< this material has no internal variables /** * @brief stress calculation for a linear isotropic material model @@ -47,17 +47,17 @@ struct MyLinearIsotropic { * @return The stress */ template - SERAC_HOST_DEVICE auto operator()(State& /* state */, const serac::tensor& du_dX) const + SMITH_HOST_DEVICE auto operator()(State& /* state */, const smith::tensor& du_dX) const { - auto I = serac::Identity(); + auto I = smith::Identity(); auto lambda = K - (2.0 / 3.0) * G; auto epsilon = 0.5 * (transpose(du_dX) + du_dX); return lambda * tr(epsilon) * I + 2.0 * G * epsilon; } template - SERAC_HOST_DEVICE auto pkStress(State& /* state */, const serac::tensor& du_dX, const Density&) const + SMITH_HOST_DEVICE auto pkStress(State& /* state */, const smith::tensor& du_dX, const Density&) const { - auto I = serac::Identity(); + auto I = smith::Identity(); auto lambda = K - (2.0 / 3.0) * G; auto epsilon = 0.5 * (transpose(du_dX) + du_dX); return lambda * tr(epsilon) * I + 2.0 * G * epsilon; @@ -77,9 +77,9 @@ struct MyLinearIsotropic { /// @brief interpolates density field template - SERAC_HOST_DEVICE auto density(const Density& density) const + SMITH_HOST_DEVICE auto density(const Density& density) const { - return get(density); + return get(density); } // double density; ///< mass density @@ -88,9 +88,9 @@ struct MyLinearIsotropic { }; // using SolidMaterial = MyLinearIsotropic; -using SolidMaterial = serac::solid_mechanics::NeoHookeanWithFieldDensity; +using SolidMaterial = smith::solid_mechanics::NeoHookeanWithFieldDensity; -using SolidWeakFormT = serac::SolidWeakForm>; +using SolidWeakFormT = smith::SolidWeakForm>; enum FIELD { @@ -121,12 +121,12 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::unique_ptr dcdu_; int dimu_; int dimc_; - std::vector contact_states_; - std::vector residual_states_; + std::vector contact_states_; + std::vector residual_states_; std::shared_ptr weak_form_; - std::unique_ptr shape_disp_; - std::shared_ptr mesh_; - std::shared_ptr constraints_; + std::unique_ptr shape_disp_; + std::shared_ptr mesh_; + std::shared_ptr constraints_; double time_ = 0.0; double dt_ = 0.0; std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; @@ -140,9 +140,9 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { mfem::Vector dispBC_; public: - TiedContactProblem(std::vector contact_states, std::vector residual_states, - std::shared_ptr mesh, std::shared_ptr weak_form, - std::shared_ptr constraints, mfem::Array disp_ess_tdof_list, + TiedContactProblem(std::vector contact_states, std::vector residual_states, + std::shared_ptr mesh, std::shared_ptr weak_form, + std::shared_ptr constraints, mfem::Array disp_ess_tdof_list, mfem::Vector dispBC, mfem::Array ess_tdof_list); mfem::Vector residual(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); @@ -157,8 +157,8 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { class ParaviewWriter { public: - using StateVecs = std::vector>; - using DualVecs = std::vector>; + using StateVecs = std::vector>; + using DualVecs = std::vector>; ParaviewWriter(std::unique_ptr pv_, const StateVecs& states_) : pv(std::move(pv_)), states(states_) @@ -170,9 +170,9 @@ class ParaviewWriter { { } - void write(int step, double time, const std::vector& current_states) + void write(int step, double time, const std::vector& current_states) { - SERAC_MARK_FUNCTION; + SMITH_MARK_FUNCTION; SLIC_ERROR_ROOT_IF(current_states.size() != states.size(), "wrong number of output states to write"); for (size_t n = 0; n < states.size(); ++n) { @@ -192,7 +192,7 @@ class ParaviewWriter { StateVecs dual_states; }; -auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector& states, +auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector& states, std::string output_name) { if (output_name == "") { @@ -201,7 +201,7 @@ auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector(s->space(), s->name())); + output_states.push_back(std::make_shared(s->space(), s->name())); } auto non_const_mesh = const_cast(&mesh); @@ -227,7 +227,7 @@ auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector(serac::buildMeshFromFile(filename), "twist_mesh", 3, 0); + std::string filename = SMITH_REPO_DIR "/data/meshes/twohex_for_contact.mesh"; + auto mesh = std::make_shared(smith::buildMeshFromFile(filename), "twist_mesh", 3, 0); - mesh->addDomainOfBoundaryElements("fixed_surface", serac::by_attr(3)); - mesh->addDomainOfBoundaryElements("driven_surface", serac::by_attr(6)); + mesh->addDomainOfBoundaryElements("fixed_surface", smith::by_attr(3)); + mesh->addDomainOfBoundaryElements("driven_surface", smith::by_attr(6)); - serac::ContactOptions contact_options{.method = serac::ContactMethod::SingleMortar, - .enforcement = serac::ContactEnforcement::LagrangeMultiplier, - .type = serac::ContactType::TiedNormal, - .jacobian = serac::ContactJacobian::Exact}; + smith::ContactOptions contact_options{.method = smith::ContactMethod::SingleMortar, + .enforcement = smith::ContactEnforcement::LagrangeMultiplier, + .type = smith::ContactType::TiedNormal, + .jacobian = smith::ContactJacobian::Exact}; std::string contact_constraint_name = "default_contact"; @@ -273,30 +273,30 @@ int main(int argc, char* argv[]) auto contact_interaction_id = 0; std::set surface_1_boundary_attributes({4}); std::set surface_2_boundary_attributes({5}); - auto contact_constraint = std::make_shared( + auto contact_constraint = std::make_shared( 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()); + smith::FiniteElementState shape = smith::StateManager::newState(VectorSpace{}, "shape", mesh->tag()); + smith::FiniteElementState disp = smith::StateManager::newState(VectorSpace{}, "displacement", mesh->tag()); + smith::FiniteElementState velo = smith::StateManager::newState(VectorSpace{}, "velocity", mesh->tag()); + smith::FiniteElementState accel = smith::StateManager::newState(VectorSpace{}, "acceleration", mesh->tag()); + smith::FiniteElementState density = smith::StateManager::newState(DensitySpace{}, "density", mesh->tag()); - std::vector contact_states; - std::vector states; - std::vector params; + std::vector contact_states; + std::vector states; + std::vector params; contact_states = {shape, disp}; states = {disp, velo, accel}; params = {density}; // initialize displacement - contact_states[serac::ContactFields::DISP].setFromFieldFunction([](serac::tensor x) { + contact_states[smith::ContactFields::DISP].setFromFieldFunction([](smith::tensor x) { auto u = 0.0 * x; return u; }); - contact_states[serac::ContactFields::SHAPE] = 0.0; + contact_states[smith::ContactFields::SHAPE] = 0.0; states[FIELD::VELO] = 0.0; states[FIELD::ACCEL] = 0.0; params[0] = 1.0; @@ -310,20 +310,20 @@ int main(int argc, char* argv[]) SolidMaterial mat; mat.K = 1.0; mat.G = 0.5; - solid_mechanics_weak_form->setMaterial(serac::DependsOn<0>{}, mesh->entireBodyName(), mat); + solid_mechanics_weak_form->setMaterial(smith::DependsOn<0>{}, mesh->entireBodyName(), mat); - serac::tensor constant_force{}; + smith::tensor constant_force{}; for (int i = 0; i < dim; i++) { constant_force[i] = 0.0; } constant_force[dim - 1] = 0.0; solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { - return serac::tuple{constant_force, 0.0 * serac::get(x)}; + return smith::tuple{constant_force, 0.0 * smith::get(x)}; }); - auto residual_state_ptrs = serac::getFieldPointers(states, params); - auto contact_state_ptrs = serac::getFieldPointers(contact_states); + auto residual_state_ptrs = smith::getFieldPointers(states, params); + auto contact_state_ptrs = smith::getFieldPointers(contact_states); // homogeneous Dirichlet boundary conditions mfem::Array ess_fixed_tdof_list; @@ -342,8 +342,8 @@ int main(int argc, char* argv[]) states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_disp_tdof_list); mfem::Vector uDC(states[FIELD::DISP].space().GetTrueVSize()); uDC = 0.0; - auto applied_displacement = [](serac::tensor /*x*/) { - serac::tensor u{}; + auto applied_displacement = [](smith::tensor /*x*/) { + smith::tensor u{}; u[0] = 0.0; u[1] = 0.0; u[2] = -0.06; @@ -372,22 +372,22 @@ int main(int argc, char* argv[]) SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); // visualize - auto writer = createParaviewOutput(mesh->mfemParMesh(), serac::getConstFieldPointers(states), "contact"); + auto writer = createParaviewOutput(mesh->mfemParMesh(), smith::getConstFieldPointers(states), "contact"); if (visualize) { mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); problem.fullDisplacement(X0, u); states[FIELD::DISP].Set(1.0, u); - writer.write(0, 0.0, serac::getConstFieldPointers(states)); + writer.write(0, 0.0, smith::getConstFieldPointers(states)); if (!visualize_all_iterates) { problem.fullDisplacement(Xf, u); states[FIELD::DISP].Set(1.0, u); - writer.write(1, 1.0, serac::getConstFieldPointers(states)); + writer.write(1, 1.0, smith::getConstFieldPointers(states)); } else { auto iterates = solver.GetIterates(); for (int i = 0; i < iterates.Size(); i++) { problem.fullDisplacement(*iterates[i], u); states[FIELD::DISP].Set(1.0, u); - writer.write((i + 1), static_cast(i + 1), serac::getConstFieldPointers(states)); + writer.write((i + 1), static_cast(i + 1), smith::getConstFieldPointers(states)); } } } @@ -395,11 +395,11 @@ int main(int argc, char* argv[]) } template -TiedContactProblem::TiedContactProblem(std::vector contact_states, - std::vector residual_states, - std::shared_ptr mesh, +TiedContactProblem::TiedContactProblem(std::vector contact_states, + std::vector residual_states, + std::shared_ptr mesh, std::shared_ptr weak_form, - std::shared_ptr constraints, + std::shared_ptr constraints, mfem::Array disp_ess_tdof_list, mfem::Vector dispBC, mfem::Array ess_tdof_list) : EqualityConstrainedHomotopyProblem(), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) @@ -484,7 +484,7 @@ TiedContactProblem::TiedContactProblem(std::vectorMult(RdispBC, dispBC_); // shape_disp field - shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); + shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); if (contact) { // Linearize gap about zero displacement state @@ -512,7 +512,7 @@ mfem::Vector TiedContactProblem::residual(const mfem::Vector& prolongation_->Mult(u, ufull_); ufull_.Add(1.0, dispBC_); residual_states_[FIELD::DISP]->Set(1.0, ufull_); - auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), serac::getConstFieldPointers(residual_states_)); + auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), smith::getConstFieldPointers(residual_states_)); mfem::Vector res(dimu_); res = 0.0; restriction_->Mult(resfull, res); @@ -547,8 +547,8 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto } else { prolongation_->Mult(u, ufull_); ufull_.Add(1.0, dispBC_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - gap = constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point); + contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); + gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point); } } return gap; @@ -563,9 +563,9 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( if (!linearized_contact) { ufull_.Add(1.0, dispBC_); prolongation_->Mult(u, ufull_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); - auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), - serac::ContactFields::DISP, new_point); + contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); + auto dcdufull_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), + smith::ContactFields::DISP, new_point); dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), new_point)); } } @@ -584,12 +584,12 @@ void TiedContactProblem::Linearize() { bool new_point = true; prolongation_->Mult(u0_, ufull_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); g0_.SetSize(dimc_); - g0_.Set(1.0, constraints_->evaluate(time_, dt_, serac::getConstFieldPointers(contact_states_), new_point)); + g0_.Set(1.0, constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point)); - auto dcdufull_ = constraints_->jacobian(time_, dt_, serac::getConstFieldPointers(contact_states_), - serac::ContactFields::DISP, new_point); + auto dcdufull_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), + smith::ContactFields::DISP, new_point); dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), true)); } @@ -606,9 +606,9 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const } else { prolongation_->Mult(u, ufull_); ufull_.Add(1.0, dispBC_); - contact_states_[serac::ContactFields::DISP]->Set(1.0, ufull_); + contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); auto res_contribution = constraints_->residual_contribution( - time_, dt_, serac::getConstFieldPointers(contact_states_), l, serac::ContactFields::DISP, new_point); + time_, dt_, smith::getConstFieldPointers(contact_states_), l, smith::ContactFields::DISP, new_point); restriction_->Mult(res_contribution, res); } } From 4dfd35638d9ecfc9cf243b2d5037472c75cac59d Mon Sep 17 00:00:00 2001 From: thartland Date: Sun, 23 Nov 2025 15:03:29 -0800 Subject: [PATCH 59/82] constraint_twist --> two_blocks rename, I believe this is a better description of the contact problem --- examples/contact/homotopy/CMakeLists.txt | 12 ++++++------ .../{constraint_twist.cpp => two_blocks.cpp} | 0 2 files changed, 6 insertions(+), 6 deletions(-) rename examples/contact/homotopy/{constraint_twist.cpp => two_blocks.cpp} (100%) diff --git a/examples/contact/homotopy/CMakeLists.txt b/examples/contact/homotopy/CMakeLists.txt index 355b734e5..40c9d33b7 100644 --- a/examples/contact/homotopy/CMakeLists.txt +++ b/examples/contact/homotopy/CMakeLists.txt @@ -5,18 +5,18 @@ # SPDX-License-Identifier: (BSD-3-Clause) if (TRIBOL_FOUND AND STRUMPACK_DIR AND SMITH_ENABLE_CONTINUATION) - blt_add_executable( NAME constraint_twist_example - SOURCES constraint_twist.cpp + blt_add_executable( NAME two_blocks_example + SOURCES two_blocks.cpp OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} DEPENDS_ON smith_physics smith_mesh_utils continuation_solver ) - smith_add_example_test(NAME constraint_twist - COMMAND constraint_twist_example + smith_add_example_test(NAME two_blocks + COMMAND two_blocks_example NUM_MPI_TASKS 4) install( FILES - constraint_twist.cpp + two_blocks.cpp DESTINATION - examples/smith/constraint_twist + examples/smith/two_blocks ) endif() diff --git a/examples/contact/homotopy/constraint_twist.cpp b/examples/contact/homotopy/two_blocks.cpp similarity index 100% rename from examples/contact/homotopy/constraint_twist.cpp rename to examples/contact/homotopy/two_blocks.cpp From 7c2b7a9f921e951bf4637f3014d9ead70bea5d6a Mon Sep 17 00:00:00 2001 From: thartland Date: Sun, 23 Nov 2025 15:43:26 -0800 Subject: [PATCH 60/82] removing unneeded options such as the no contact option --- examples/contact/homotopy/two_blocks.cpp | 176 ++++++----------------- 1 file changed, 43 insertions(+), 133 deletions(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index b4fcae5c2..c46d47ccd 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -25,71 +25,12 @@ static constexpr int dim = 3; static constexpr int disp_order = 1; -int linearized_contact = 1; -int contact = 0; +int linearized_contact = 0; using VectorSpace = smith::H1; - using DensitySpace = smith::L2; -struct MyLinearIsotropic { - using State = smith::Empty; ///< this material has no internal variables - - /** - * @brief stress calculation for a linear isotropic material model - * - * When applied to 2D displacement gradients, the stress is computed in plane strain, - * returning only the in-plane components. - * - * @tparam T Number-like type for the displacement gradient components - * @tparam dim Dimensionality of space - * @param du_dX Displacement gradient with respect to the reference configuration - * @return The stress - */ - template - SMITH_HOST_DEVICE auto operator()(State& /* state */, const smith::tensor& du_dX) const - { - auto I = smith::Identity(); - auto lambda = K - (2.0 / 3.0) * G; - auto epsilon = 0.5 * (transpose(du_dX) + du_dX); - return lambda * tr(epsilon) * I + 2.0 * G * epsilon; - } - template - SMITH_HOST_DEVICE auto pkStress(State& /* state */, const smith::tensor& du_dX, const Density&) const - { - auto I = smith::Identity(); - auto lambda = K - (2.0 / 3.0) * G; - auto epsilon = 0.5 * (transpose(du_dX) + du_dX); - return lambda * tr(epsilon) * I + 2.0 * G * epsilon; - // using std::log1p; - // constexpr auto I = Identity(); - // auto lambda = K - (2.0 / 3.0) * G; - // auto B_minus_I = dot(du_dX, transpose(du_dX)) + transpose(du_dX) + du_dX; - - // auto logJ = log1p(detApIm1(du_dX)); - //// Kirchoff stress, in form that avoids cancellation error when F is near I - // auto TK = lambda * logJ * I + G * B_minus_I; - - //// Pull back to Piola - // auto F = du_dX + I; - // return dot(TK, inv(transpose(F))); - } - - /// @brief interpolates density field - template - SMITH_HOST_DEVICE auto density(const Density& density) const - { - return get(density); - } - - // double density; ///< mass density - double K; ///< bulk modulus - double G; ///< shear modulus -}; - -// using SolidMaterial = MyLinearIsotropic; using SolidMaterial = smith::solid_mechanics::NeoHookeanWithFieldDensity; - using SolidWeakFormT = smith::SolidWeakForm>; enum FIELD @@ -229,18 +170,11 @@ int main(int argc, char* argv[]) // Initialize and automatically finalize MPI and other libraries smith::ApplicationManager applicationManager(argc, argv); - int fd_check = 0; // finite difference check or homotopy solve int visualize = 1; int visualize_all_iterates = 1; // command line arguments - axom::CLI::App app{"Constraint twist."}; - app.add_option("--contact", contact, "enable contact (1) or use Dirichlet BCs for contact surface (0)") - ->default_val("0") - ->check(axom::CLI::Range(0, 1)); - app.add_option("--lincontact", linearized_contact, "use linearized contact (1) or nonlinear contact (0)") - ->default_val("1") - ->check(axom::CLI::Range(0, 1)); - app.add_option("--fdcheck", fd_check, "finite difference check (1) or Homotopy solve (0)") + axom::CLI::App app{"Two block contact."}; + app.add_option("--lincontact", linearized_contact, "nonlinear contact (0) or linearized contact (1)") ->default_val("0") ->check(axom::CLI::Range(0, 1)); app.add_option("--visualize", visualize, "solution visualization") @@ -251,13 +185,13 @@ int main(int argc, char* argv[]) CLI11_PARSE(app, argc, argv); // Create DataStore - std::string name = "contact_twist_example"; + std::string name = "two_block_example"; axom::sidre::DataStore datastore; smith::StateManager::initialize(datastore, name + "_data"); // Construct the appropriate dimension mesh and give it to the data store std::string filename = SMITH_REPO_DIR "/data/meshes/twohex_for_contact.mesh"; - auto mesh = std::make_shared(smith::buildMeshFromFile(filename), "twist_mesh", 3, 0); + auto mesh = std::make_shared(smith::buildMeshFromFile(filename), "two_block_mesh", 3, 0); mesh->addDomainOfBoundaryElements("fixed_surface", smith::by_attr(3)); mesh->addDomainOfBoundaryElements("driven_surface", smith::by_attr(6)); @@ -332,10 +266,6 @@ int main(int argc, char* argv[]) ess_bdr_marker = 0; ess_bdr_marker[2] = 1; ess_bdr_marker[5] = 0; - if (!contact) { - ess_bdr_marker[3] = 1; - ess_bdr_marker[4] = 1; - } states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_fixed_tdof_list); ess_bdr_marker = 0; ess_bdr_marker[5] = 1; @@ -427,17 +357,11 @@ TiedContactProblem::TiedContactProblem(std::vector cOffsets = std::make_unique(2); // obtain pressure dof information - if (contact) { - dimc_ = constraints_->numPressureDofs(); - HYPRE_BigInt pressure_offset = 0; - MPI_Scan(&dimc_, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - cOffsets[0] = pressure_offset - dimc_; - cOffsets[1] = pressure_offset; - } else { - dimc_ = 0; - cOffsets[0] = 0; - cOffsets[1] = 0; - } + dimc_ = constraints_->numPressureDofs(); + HYPRE_BigInt pressure_offset = 0; + MPI_Scan(&dimc_, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + cOffsets[0] = pressure_offset - dimc_; + cOffsets[1] = pressure_offset; // set pressure and displacement dof information SetSizes(uOffsets.get(), cOffsets.get()); @@ -486,20 +410,11 @@ TiedContactProblem::TiedContactProblem(std::vector(mesh->newShapeDisplacement()); - if (contact) { - // Linearize gap about zero displacement state - if (linearized_contact) { - u0_.SetSize(dimu_); - u0_ = 0.0; - Linearize(); - } - } else { - int nentries = 0; - int dimuglb_; - dimuglb_ = 0; - MPI_Allreduce(&dimu_, &dimuglb_, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - auto temp = std::make_unique(dimc_, dimuglb_, nentries); - dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(cOffsets.get(), uOffsets.get(), temp.get())); + // Linearize gap about zero displacement state + if (linearized_contact) { + u0_.SetSize(dimu_); + u0_ = 0.0; + Linearize(); } } @@ -537,19 +452,17 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto bool new_point = true; mfem::Vector gap(dimc_); gap = 0.0; - if (contact) { - if (linearized_contact) { - mfem::Vector du(dimu_); - du.Set(1.0, u); - du.Add(-1.0, u0_); - dcdu_->Mult(du, gap); - gap.Add(1.0, g0_); - } else { - prolongation_->Mult(u, ufull_); - ufull_.Add(1.0, dispBC_); - contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); - gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point); - } + if (linearized_contact) { + mfem::Vector du(dimu_); + du.Set(1.0, u); + du.Add(-1.0, u0_); + dcdu_->Mult(du, gap); + gap.Add(1.0, g0_); + } else { + prolongation_->Mult(u, ufull_); + ufull_.Add(1.0, dispBC_); + contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); + gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point); } return gap; } @@ -559,15 +472,13 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( bool /*new_point*/) { bool new_point = true; - if (contact) { - if (!linearized_contact) { - ufull_.Add(1.0, dispBC_); - prolongation_->Mult(u, ufull_); - contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); - auto dcdufull_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), - smith::ContactFields::DISP, new_point); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), new_point)); - } + if (!linearized_contact) { + ufull_.Add(1.0, dispBC_); + prolongation_->Mult(u, ufull_); + contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); + auto dcdufull_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), + smith::ContactFields::DISP, new_point); + dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), new_point)); } return dcdu_.get(); } @@ -600,18 +511,17 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const bool new_point = true; mfem::Vector res(dimu_); res = 0.0; - if (contact) { - if (linearized_contact) { - dcdu_->MultTranspose(l, res); - } else { - prolongation_->Mult(u, ufull_); - ufull_.Add(1.0, dispBC_); - contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); - auto res_contribution = constraints_->residual_contribution( - time_, dt_, smith::getConstFieldPointers(contact_states_), l, smith::ContactFields::DISP, new_point); - restriction_->Mult(res_contribution, res); - } + if (linearized_contact) { + dcdu_->MultTranspose(l, res); + } else { + prolongation_->Mult(u, ufull_); + ufull_.Add(1.0, dispBC_); + contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); + auto res_contribution = constraints_->residual_contribution( + time_, dt_, smith::getConstFieldPointers(contact_states_), l, smith::ContactFields::DISP, new_point); + restriction_->Mult(res_contribution, res); } + return res; } From 18b94aa61f7c586e26a1d63bce8ece724730ebf4 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 24 Nov 2025 07:06:17 -0800 Subject: [PATCH 61/82] style --- src/smith/physics/contact_constraint.hpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/smith/physics/contact_constraint.hpp b/src/smith/physics/contact_constraint.hpp index 72cc92ec8..bb36b1408 100644 --- a/src/smith/physics/contact_constraint.hpp +++ b/src/smith/physics/contact_constraint.hpp @@ -200,8 +200,7 @@ class ContactConstraint : public Constraint { contact_.update(cycle, time, dt); pressures_set_ = false; } - if (!pressures_set_) - { + if (!pressures_set_) { // with updated gaps, we can update pressure for contact interactions with penalty enforcement contact_.setPressures(multipliers); // call update again with the right pressures @@ -244,8 +243,7 @@ class ContactConstraint : public Constraint { contact_.update(cycle, time, dt); pressures_set_ = false; } - if (!pressures_set_) - { + if (!pressures_set_) { // with updated gaps, we can update pressure for contact interactions with penalty enforcement contact_.setPressures(multipliers); // call update again with the right pressures @@ -316,7 +314,7 @@ class ContactConstraint : public Constraint { mutable std::unique_ptr J_contact_; /** - * @brief own_blocks_ temporary boolean + * @brief own_blocks_ temporary boolean */ const bool own_blocks_ = true; @@ -324,8 +322,6 @@ class ContactConstraint : public Constraint { * @brief pressures_set_ are the Lagrange multipliers set */ mutable bool pressures_set_ = false; - - }; } // namespace smith From 4fc3a0938429c487f167d92b9a4b3db8d91eaf49 Mon Sep 17 00:00:00 2001 From: thartland Date: Mon, 24 Nov 2025 08:22:52 -0800 Subject: [PATCH 62/82] style --- examples/contact/homotopy/two_blocks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index c46d47ccd..cce1faa4c 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -521,7 +521,7 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const time_, dt_, smith::getConstFieldPointers(contact_states_), l, smith::ContactFields::DISP, new_point); restriction_->Mult(res_contribution, res); } - + return res; } From 7d1b74219955b7a261bfa704d57cd55c1ee0ef28 Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 25 Nov 2025 14:01:44 -0800 Subject: [PATCH 63/82] updating tribol version. I seem to have messed with the version used --- tribol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tribol b/tribol index 6bbc0f662..a44589220 160000 --- a/tribol +++ b/tribol @@ -1 +1 @@ -Subproject commit 6bbc0f66268361d88e55ba6b52aa21f2479dd1f8 +Subproject commit a445892205b31b9505d9427dd9a04f6d5714bcad From 4fd8d570a92af027e00034630a5f9104b82752c2 Mon Sep 17 00:00:00 2001 From: thartland Date: Tue, 25 Nov 2025 14:43:45 -0800 Subject: [PATCH 64/82] updating two block example -- using fixed and displacement dofs now hidden on the HomotopySolver solver backend --- ContinuationSolvers | 2 +- examples/contact/homotopy/two_blocks.cpp | 170 +++-------------------- 2 files changed, 22 insertions(+), 150 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 57b2b5333..ac99e95de 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 57b2b5333edd1d78133c380c8fe0192da692d2c8 +Subproject commit ac99e95de46b5d65ca21e1314afb56c89718d747 diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index cce1faa4c..eeaab4c58 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -25,8 +25,6 @@ static constexpr int dim = 3; static constexpr int disp_order = 1; -int linearized_contact = 0; - using VectorSpace = smith::H1; using DensitySpace = smith::L2; @@ -71,28 +69,17 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { double time_ = 0.0; double dt_ = 0.0; std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; - std::unique_ptr restriction_; - std::unique_ptr prolongation_; - std::unique_ptr disp_restriction_; - std::unique_ptr disp_prolongation_; - mutable mfem::Vector ufull_; - mfem::Vector g0_; - mfem::Vector u0_; - mfem::Vector dispBC_; - public: TiedContactProblem(std::vector contact_states, std::vector residual_states, std::shared_ptr mesh, std::shared_ptr weak_form, - std::shared_ptr constraints, mfem::Array disp_ess_tdof_list, - mfem::Vector dispBC, mfem::Array ess_tdof_list); + std::shared_ptr constraints, mfem::Array fixed_tdof_list, + mfem::Array disp_tdof_list, mfem::Vector uDC); mfem::Vector residual(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; void fullDisplacement(const mfem::Vector& x, mfem::Vector& u); - void setLinearizationPoint(const mfem::Vector& u0); - void Linearize(); virtual ~TiedContactProblem(); }; @@ -174,9 +161,6 @@ int main(int argc, char* argv[]) int visualize_all_iterates = 1; // command line arguments axom::CLI::App app{"Two block contact."}; - app.add_option("--lincontact", linearized_contact, "nonlinear contact (0) or linearized contact (1)") - ->default_val("0") - ->check(axom::CLI::Range(0, 1)); app.add_option("--visualize", visualize, "solution visualization") ->default_val("1") // Matches value set above ->check(axom::CLI::Range(0, 1)); @@ -283,7 +267,7 @@ int main(int argc, char* argv[]) uDC.Set(1.0, states[FIELD::DISP]); TiedContactProblem problem(contact_state_ptrs, residual_state_ptrs, mesh, solid_mechanics_weak_form, - contact_constraint, ess_disp_tdof_list, uDC, ess_fixed_tdof_list); + contact_constraint, ess_fixed_tdof_list, ess_disp_tdof_list, uDC); double nonlinear_absolute_tol = 1.e-6; int nonlinear_max_iterations = 30; int nonlinear_print_level = 1; @@ -330,9 +314,9 @@ TiedContactProblem::TiedContactProblem(std::vector mesh, std::shared_ptr weak_form, std::shared_ptr constraints, - mfem::Array disp_ess_tdof_list, mfem::Vector dispBC, - mfem::Array ess_tdof_list) - : EqualityConstrainedHomotopyProblem(), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) + mfem::Array fixed_tdof_list, + mfem::Array disp_tdof_list, mfem::Vector uDC) + : EqualityConstrainedHomotopyProblem(fixed_tdof_list, disp_tdof_list, uDC), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) { // copy residual states residual_states_.resize(residual_states.size()); @@ -346,11 +330,7 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize(); - ufull_.SetSize(dimufull_); - ufull_ = 0.0; - dispBC_.SetSize(dimufull_); - dispBC_.Set(1.0, dispBC); - dimu_ = dimufull_ - ess_tdof_list.Size() - disp_ess_tdof_list.Size(); + dimu_ = dimufull_ - fixed_tdof_list.Size() - disp_tdof_list.Size(); std::unique_ptr uOffsets; uOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); @@ -365,84 +345,24 @@ TiedContactProblem::TiedContactProblem(std::vector ith dof will be made visible to solver - // these dofs will be made visible via explicit use - // of prolongation/restriction operators in residual/constraint member functions - std::unique_ptr mask = std::make_unique(static_cast(dimufull_)); - for (int i = 0; i < dimufull_; i++) { - mask[static_cast(i)] = 1; - } - for (int i = 0; i < ess_tdof_list.Size(); i++) { - mask[static_cast(ess_tdof_list[i])] = 0; - } - for (int i = 0; i < disp_ess_tdof_list.Size(); i++) { - mask[static_cast(disp_ess_tdof_list[i])] = 0; - } - - // now mask out dofs that aren't disp BC dofs - std::unique_ptr dispmask = std::make_unique(static_cast(dimufull_)); - for (int i = 0; i < dimufull_; i++) { - dispmask[static_cast(i)] = 0; - } - for (int i = 0; i < disp_ess_tdof_list.Size(); i++) { - dispmask[static_cast(disp_ess_tdof_list[i])] = 1; - } - - restriction_.reset( - GenerateProjector(uOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), mask.get())); - - prolongation_.reset(restriction_->Transpose()); - - // need disp offsets - std::unique_ptr dispuOffsets; - dispuOffsets.reset(offsetsFromLocalSizes(disp_ess_tdof_list.Size(), MPI_COMM_WORLD)); - disp_restriction_.reset(GenerateProjector( - dispuOffsets.get(), residual_states_[FIELD::DISP]->space().GetTrueDofOffsets(), dispmask.get())); - - disp_prolongation_.reset(disp_restriction_->Transpose()); - - // remove any nonzero entries in dispBC vec that are not strictly needed - mfem::Vector RdispBC(disp_restriction_->Height()); - disp_restriction_->Mult(dispBC_, RdispBC); - disp_prolongation_->Mult(RdispBC, dispBC_); // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); - - // Linearize gap about zero displacement state - if (linearized_contact) { - u0_.SetSize(dimu_); - u0_ = 0.0; - Linearize(); - } } template mfem::Vector TiedContactProblem::residual(const mfem::Vector& u, bool /*new_point*/) const { - // 1. prolongate u --> u_prolongated - // 2. obtain full residual via u_prolongated - // 3. restrict residual - prolongation_->Mult(u, ufull_); - ufull_.Add(1.0, dispBC_); - residual_states_[FIELD::DISP]->Set(1.0, ufull_); - auto resfull = weak_form_->residual(time_, dt_, shape_disp_.get(), smith::getConstFieldPointers(residual_states_)); - mfem::Vector res(dimu_); - res = 0.0; - restriction_->Mult(resfull, res); + residual_states_[FIELD::DISP]->Set(1.0, u); + auto res = weak_form_->residual(time_, dt_, shape_disp_.get(), smith::getConstFieldPointers(residual_states_)); return res; }; template mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) { - prolongation_->Mult(u, ufull_); - ufull_.Add(1.0, dispBC_); - residual_states_[FIELD::DISP]->Set(1.0, ufull_); - auto drdufull_ = - weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(residual_states_), jacobian_weights_); - drdu_.reset(RAP(drdufull_.get(), prolongation_.get())); + residual_states_[FIELD::DISP]->Set(1.0, u); + drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), smith::getConstFieldPointers(residual_states_), jacobian_weights_); return drdu_.get(); } @@ -450,21 +370,9 @@ template mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u, bool /*new_point*/) const { bool new_point = true; - mfem::Vector gap(dimc_); - gap = 0.0; - if (linearized_contact) { - mfem::Vector du(dimu_); - du.Set(1.0, u); - du.Add(-1.0, u0_); - dcdu_->Mult(du, gap); - gap.Add(1.0, g0_); - } else { - prolongation_->Mult(u, ufull_); - ufull_.Add(1.0, dispBC_); - contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); - gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point); - } - return gap; + contact_states_[smith::ContactFields::DISP]->Set(1.0, u); + auto gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point); + return gap; } template @@ -472,57 +380,21 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( bool /*new_point*/) { bool new_point = true; - if (!linearized_contact) { - ufull_.Add(1.0, dispBC_); - prolongation_->Mult(u, ufull_); - contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); - auto dcdufull_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), + contact_states_[smith::ContactFields::DISP]->Set(1.0, u); + dcdu_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), smith::ContactFields::DISP, new_point); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), new_point)); - } return dcdu_.get(); } -template -void TiedContactProblem::setLinearizationPoint(const mfem::Vector& u0) -{ - u0_.Set(1.0, u0); - Linearize(); -} - -template -void TiedContactProblem::Linearize() -{ - bool new_point = true; - prolongation_->Mult(u0_, ufull_); - contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); - g0_.SetSize(dimc_); - g0_.Set(1.0, constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point)); - - auto dcdufull_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), - smith::ContactFields::DISP, new_point); - dcdu_.reset(mfem::ParMult(dcdufull_.get(), prolongation_.get(), true)); -} - template mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool /*new_point*/) const { bool new_point = true; - mfem::Vector res(dimu_); - res = 0.0; - if (linearized_contact) { - dcdu_->MultTranspose(l, res); - } else { - prolongation_->Mult(u, ufull_); - ufull_.Add(1.0, dispBC_); - contact_states_[smith::ContactFields::DISP]->Set(1.0, ufull_); - auto res_contribution = constraints_->residual_contribution( - time_, dt_, smith::getConstFieldPointers(contact_states_), l, smith::ContactFields::DISP, new_point); - restriction_->Mult(res_contribution, res); - } - - return res; + contact_states_[smith::ContactFields::DISP]->Set(1.0, u); + auto res_contribution = constraints_->residual_contribution( + time_, dt_, smith::getConstFieldPointers(contact_states_), l, smith::ContactFields::DISP, new_point); + return res_contribution; } template @@ -531,7 +403,7 @@ void TiedContactProblem::fullDisplacement(const mfem::Vector& mfem::BlockVector Xblock(y_partition); Xblock.Set(1.0, X); prolongation_->Mult(Xblock.GetBlock(0), u); - u.Add(1.0, dispBC_); + u.Add(1.0, uDC_); } template From 550f6ad98ac0f12a996ed000fd8c2c5bf081904a Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 26 Nov 2025 10:34:09 -0800 Subject: [PATCH 65/82] style --- examples/contact/homotopy/two_blocks.cpp | 26 ++++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index eeaab4c58..700dde4b0 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -69,11 +69,12 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { double time_ = 0.0; double dt_ = 0.0; std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; + public: TiedContactProblem(std::vector contact_states, std::vector residual_states, std::shared_ptr mesh, std::shared_ptr weak_form, std::shared_ptr constraints, mfem::Array fixed_tdof_list, - mfem::Array disp_tdof_list, mfem::Vector uDC); + mfem::Array disp_tdof_list, mfem::Vector uDC); mfem::Vector residual(const mfem::Vector& u, bool new_point) const; mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; @@ -314,9 +315,12 @@ TiedContactProblem::TiedContactProblem(std::vector mesh, std::shared_ptr weak_form, std::shared_ptr constraints, - mfem::Array fixed_tdof_list, - mfem::Array disp_tdof_list, mfem::Vector uDC) - : EqualityConstrainedHomotopyProblem(fixed_tdof_list, disp_tdof_list, uDC), weak_form_(weak_form), mesh_(mesh), constraints_(constraints) + mfem::Array fixed_tdof_list, + mfem::Array disp_tdof_list, mfem::Vector uDC) + : EqualityConstrainedHomotopyProblem(fixed_tdof_list, disp_tdof_list, uDC), + weak_form_(weak_form), + mesh_(mesh), + constraints_(constraints) { // copy residual states residual_states_.resize(residual_states.size()); @@ -345,7 +349,6 @@ TiedContactProblem::TiedContactProblem(std::vector(mesh->newShapeDisplacement()); } @@ -362,7 +365,8 @@ template mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) { residual_states_[FIELD::DISP]->Set(1.0, u); - drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), smith::getConstFieldPointers(residual_states_), jacobian_weights_); + drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), smith::getConstFieldPointers(residual_states_), + jacobian_weights_); return drdu_.get(); } @@ -372,7 +376,7 @@ mfem::Vector TiedContactProblem::constraint(const mfem::Vecto bool new_point = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); auto gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point); - return gap; + return gap; } template @@ -381,8 +385,8 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( { bool new_point = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); - dcdu_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), - smith::ContactFields::DISP, new_point); + dcdu_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), smith::ContactFields::DISP, + new_point); return dcdu_.get(); } @@ -392,8 +396,8 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const { bool new_point = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); - auto res_contribution = constraints_->residual_contribution( - time_, dt_, smith::getConstFieldPointers(contact_states_), l, smith::ContactFields::DISP, new_point); + auto res_contribution = constraints_->residual_contribution(time_, dt_, smith::getConstFieldPointers(contact_states_), + l, smith::ContactFields::DISP, new_point); return res_contribution; } From fb923b6c49be7414aad3e00229ea95f0e81277a5 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 26 Nov 2025 11:13:14 -0800 Subject: [PATCH 66/82] updates to push more things to backend --- ContinuationSolvers | 2 +- examples/contact/homotopy/two_blocks.cpp | 44 ++++++++++-------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index ac99e95de..1ab6fc2b6 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit ac99e95de46b5d65ca21e1314afb56c89718d747 +Subproject commit 1ab6fc2b6b4fd2f955bb62c7a12d404e31a0bded diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index 700dde4b0..8f173c441 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -58,8 +58,6 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { protected: std::unique_ptr drdu_; std::unique_ptr dcdu_; - int dimu_; - int dimc_; std::vector contact_states_; std::vector residual_states_; std::shared_ptr weak_form_; @@ -333,21 +331,26 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize(); - dimu_ = dimufull_ - fixed_tdof_list.Size() - disp_tdof_list.Size(); - - std::unique_ptr uOffsets; - uOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); - std::unique_ptr cOffsets = std::make_unique(2); - - // obtain pressure dof information - dimc_ = constraints_->numPressureDofs(); - HYPRE_BigInt pressure_offset = 0; - MPI_Scan(&dimc_, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - cOffsets[0] = pressure_offset - dimc_; - cOffsets[1] = pressure_offset; + const int dimu = residual_states[FIELD::DISP]->space().GetTrueVSize(); + const int dimc = constraints->numPressureDofs(); + SetSizes(dimu, dimc); + + //const int dimufull_ = residual_states[FIELD::DISP]->space().GetTrueVSize(); + //dimu_ = dimufull_ - fixed_tdof_list.Size() - disp_tdof_list.Size(); + + //std::unique_ptr uOffsets; + //uOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); + //std::unique_ptr cOffsets = std::make_unique(2); + + //// obtain pressure dof information + //dimc_ = constraints_->numPressureDofs(); + //HYPRE_BigInt pressure_offset = 0; + //MPI_Scan(&dimc_, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + //cOffsets[0] = pressure_offset - dimc_; + //cOffsets[1] = pressure_offset; + // set pressure and displacement dof information - SetSizes(uOffsets.get(), cOffsets.get()); + // SetSizes(uOffsets.get(), cOffsets.get()); // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); @@ -401,15 +404,6 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const return res_contribution; } -template -void TiedContactProblem::fullDisplacement(const mfem::Vector& X, mfem::Vector& u) -{ - mfem::BlockVector Xblock(y_partition); - Xblock.Set(1.0, X); - prolongation_->Mult(Xblock.GetBlock(0), u); - u.Add(1.0, uDC_); -} - template TiedContactProblem::~TiedContactProblem() { From f04b320ff40e0e45ec9be74cc26a7905ec678bcd Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 26 Nov 2025 11:31:30 -0800 Subject: [PATCH 67/82] updates, cleanup, consolidation, making homotopy examples more uniform, imperfect commit as I am not able to access certain LC resources right now, moving to another machine --- examples/contact/homotopy/two_blocks.cpp | 6 +++--- .../inertia_relief/inertia_relief_example.cpp | 16 ++++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index 8f173c441..f0b1b1d2a 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -288,17 +288,17 @@ int main(int argc, char* argv[]) auto writer = createParaviewOutput(mesh->mfemParMesh(), smith::getConstFieldPointers(states), "contact"); if (visualize) { mfem::Vector u(states[FIELD::DISP].space().GetTrueVSize()); - problem.fullDisplacement(X0, u); + u = problem.GetDisplacement(X0); states[FIELD::DISP].Set(1.0, u); writer.write(0, 0.0, smith::getConstFieldPointers(states)); if (!visualize_all_iterates) { - problem.fullDisplacement(Xf, u); + u = problem.GetDisplacement(Xf); states[FIELD::DISP].Set(1.0, u); writer.write(1, 1.0, smith::getConstFieldPointers(states)); } else { auto iterates = solver.GetIterates(); for (int i = 0; i < iterates.Size(); i++) { - problem.fullDisplacement(*iterates[i], u); + u = problem.GetDisplacement(*iterates[i]); states[FIELD::DISP].Set(1.0, u); writer.write((i + 1), static_cast(i + 1), smith::getConstFieldPointers(states)); } diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index ba97d8a14..1419449c4 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -366,28 +366,24 @@ InertialReliefProblem::InertialReliefProblem(std::vectorspace().GetTrueDofOffsets(); - dimc_ = static_cast(constraints_.size()); + int dimu = all_states_[FIELD::DISP]->space().GetTrueVSize(); + int dimc = static_cast(constraints_.size()); int myid = mfem::Mpi::WorldRank(); - cOffsets[0] = 0; - cOffsets[1] = dimc_; if (myid > 0) { - dimc_ = 0; - cOffsets[0] = cOffsets[1]; + dimc = 0; } - - dimu_ = all_states_[FIELD::DISP]->space().GetTrueVSize(); + SetSizes(dimu, dimc); MPI_Allreduce(&dimc_, &dimcglb_, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce(&dimu_, &dimuglb_, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + + constraint_cached_.SetSize(dimc_); constraint_cached_ = 0.0; residual_cached_.SetSize(dimu_); residual_cached_ = 0.0; JTvp_cached_.SetSize(dimu_); JTvp_cached_ = 0.0; - SetSizes(uOffsets, cOffsets); } // residual callback From fb6b3132101ce86fd99ecc1e9be2dc15839fb41a Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 26 Nov 2025 13:58:19 -0800 Subject: [PATCH 68/82] updating example problems, cleanup --- ContinuationSolvers | 2 +- examples/contact/homotopy/two_blocks.cpp | 25 ++-------- .../inertia_relief/inertia_relief_example.cpp | 50 +++++++++---------- 3 files changed, 30 insertions(+), 47 deletions(-) diff --git a/ContinuationSolvers b/ContinuationSolvers index 1ab6fc2b6..b7c26c473 160000 --- a/ContinuationSolvers +++ b/ContinuationSolvers @@ -1 +1 @@ -Subproject commit 1ab6fc2b6b4fd2f955bb62c7a12d404e31a0bded +Subproject commit b7c26c473ffb1aaaefc82832b6e1f625b239992c diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index f0b1b1d2a..d9a3209b2 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -328,30 +328,13 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize(); + // number of "internal" non-essential dofs + const int dimu = residual_states[FIELD::DISP]->space().GetTrueVSize() - fixed_tdof_list.Size() - disp_tdof_list.Size(); + // number of contact constraints const int dimc = constraints->numPressureDofs(); + // EqualityConstrainedHomotopyProblem utility function SetSizes(dimu, dimc); - //const int dimufull_ = residual_states[FIELD::DISP]->space().GetTrueVSize(); - //dimu_ = dimufull_ - fixed_tdof_list.Size() - disp_tdof_list.Size(); - - //std::unique_ptr uOffsets; - //uOffsets.reset(offsetsFromLocalSizes(dimu_, MPI_COMM_WORLD)); - //std::unique_ptr cOffsets = std::make_unique(2); - - //// obtain pressure dof information - //dimc_ = constraints_->numPressureDofs(); - //HYPRE_BigInt pressure_offset = 0; - //MPI_Scan(&dimc_, &pressure_offset, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - //cOffsets[0] = pressure_offset - dimc_; - //cOffsets[1] = pressure_offset; - - // set pressure and displacement dof information - // SetSizes(uOffsets.get(), cOffsets.get()); - // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); } diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index 1419449c4..44ade89f6 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -129,10 +129,6 @@ class InertialReliefProblem : public EqualityConstrainedHomotopyProblem { protected: std::unique_ptr drdu_; // Jacobian of residual std::unique_ptr dcdu_; // Jacobian of constraint - int dimu_; // dimension of displacement - int dimc_; // dimension of constraints - int dimuglb_; // global dimension of displacement - int dimcglb_; // global dimension of constraints std::vector obj_states_; // states for objective evaluation std::vector all_states_; // states for weak_form evaluation std::shared_ptr weak_form_; // weak_form @@ -366,23 +362,21 @@ InertialReliefProblem::InertialReliefProblem(std::vectorspace().GetTrueVSize(); - int dimc = static_cast(constraints_.size()); + int dim_displacement = all_states_[FIELD::DISP]->space().GetTrueVSize(); + int dim_constraints = static_cast(constraints_.size()); int myid = mfem::Mpi::WorldRank(); if (myid > 0) { - dimc = 0; + dim_displacement = 0; } - SetSizes(dimu, dimc); - MPI_Allreduce(&dimc_, &dimcglb_, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(&dimu_, &dimuglb_, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + SetSizes(dim_displacement, dim_constraints); - - constraint_cached_.SetSize(dimc_); + + constraint_cached_.SetSize(dim_constraints); constraint_cached_ = 0.0; - residual_cached_.SetSize(dimu_); + residual_cached_.SetSize(dim_displacement); residual_cached_ = 0.0; - JTvp_cached_.SetSize(dimu_); + JTvp_cached_.SetSize(dim_displacement); JTvp_cached_ = 0.0; } @@ -402,15 +396,17 @@ mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, bool new_point) const { if (new_point) { + int dim_constraints = GetMultiplierDim(); + int dim_displacement = GetDisplacementDim(); obj_states_[FIELD::DISP]->Set(1.0, u); std::vector multipliers(constraints_.size()); - for (int i = 0; i < dimc_; i++) { + for (int i = 0; i < dim_constraints; i++) { multipliers[static_cast(i)] = l(i); } const int nconstraints = static_cast(constraints_.size()); MPI_Bcast(multipliers.data(), nconstraints, MPI_DOUBLE, 0, MPI_COMM_WORLD); - mfem::Vector constraint_gradient(dimu_); + mfem::Vector constraint_gradient(dim_displacement); constraint_gradient = 0.0; JTvp_cached_ = 0.0; for (size_t i = 0; i < constraints_.size(); i++) { @@ -431,7 +427,8 @@ mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector drdu_.reset(); drdu_ = weak_form_->jacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); } - SLIC_ERROR_ROOT_IF(drdu_->Height() != dimu_ || drdu_->Width() != dimu_, "residual Jacobian of an unexpected shape"); + int dim_displacement = GetDisplacementDim(); + SLIC_ERROR_ROOT_IF(drdu_->Height() != dim_displacement || drdu_->Width() != dim_displacement, "residual Jacobian of an unexpected shape"); return drdu_.get(); } @@ -439,6 +436,7 @@ mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool new_point) const { if (new_point) { + int dim_constraints = GetMultiplierDim(); obj_states_[FIELD::DISP]->Set(1.0, u); for (size_t i = 0; i < constraints_.size(); i++) { @@ -448,7 +446,7 @@ mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool new_p double constraint_i = constraints_[i]->evaluate(time_, dt_, shape_disp_.get(), smith::getConstFieldPointers(obj_states_)); - if (dimc_ > 0) { + if (dim_constraints > 0) { constraint_cached_(idx) = constraint_i; } } @@ -459,16 +457,19 @@ mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool new_p // Jacobian of the constraint mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vector& u, bool new_point) { + int dim_constraints = GetMultiplierDim(); + int glbdim_displacement = GetGlobalDisplacementDim(); if (new_point) { obj_states_[FIELD::DISP]->Set(1.0, u); - int nentries = dimuglb_; + // dense rows + int nentries = glbdim_displacement; if (dimc_ == 0) { nentries = 0; } - mfem::SparseMatrix dcdumat(dimc_, dimuglb_, nentries); + mfem::SparseMatrix dcdumat(dim_constraints, glbdim_displacement, nentries); mfem::Array cols; - cols.SetSize(dimuglb_); - for (int i = 0; i < dimuglb_; i++) { + cols.SetSize(glbdim_displacement); + for (int i = 0; i < glbdim_displacement; i++) { cols[i] = i; } std::unique_ptr globalGradVector; @@ -476,15 +477,14 @@ mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vect const int idx = static_cast(i); const size_t i2 = static_cast(idx); SLIC_ERROR_ROOT_IF(i2 != i, "Constraint index is out of range, bad cast from size_t to int"); - mfem::HypreParVector gradVector(MPI_COMM_WORLD, dimuglb_, uOffsets_); + mfem::HypreParVector gradVector(MPI_COMM_WORLD, glbdim_displacement, uOffsets_); gradVector.Set(1.0, constraints_[i]->gradient(time_, dt_, shape_disp_.get(), smith::getConstFieldPointers(obj_states_), FIELD::DISP)); globalGradVector.reset(gradVector.GlobalVector()); - if (dimc_ > 0) { + if (dim_constraints > 0) { dcdumat.SetRow(idx, cols, *globalGradVector.get()); } } - dcdumat.Threshold(1.e-20); dcdumat.Finalize(); dcdu_.reset(GenerateHypreParMatrixFromSparseMatrix(cOffsets_, uOffsets_, &dcdumat)); } From d8cdb75934f487fe8ca0a47e8d277eaa96b433c1 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 26 Nov 2025 13:59:55 -0800 Subject: [PATCH 69/82] style --- examples/contact/homotopy/two_blocks.cpp | 5 +++-- examples/inertia_relief/inertia_relief_example.cpp | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index d9a3209b2..34492cd54 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -329,12 +329,13 @@ TiedContactProblem::TiedContactProblem(std::vectorspace().GetTrueVSize() - fixed_tdof_list.Size() - disp_tdof_list.Size(); + const int dimu = + residual_states[FIELD::DISP]->space().GetTrueVSize() - fixed_tdof_list.Size() - disp_tdof_list.Size(); // number of contact constraints const int dimc = constraints->numPressureDofs(); // EqualityConstrainedHomotopyProblem utility function SetSizes(dimu, dimc); - + // shape_disp field shape_disp_ = std::make_unique(mesh->newShapeDisplacement()); } diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index 44ade89f6..bc25808cd 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -370,8 +370,6 @@ InertialReliefProblem::InertialReliefProblem(std::vectorjacobian(time_, dt_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); } int dim_displacement = GetDisplacementDim(); - SLIC_ERROR_ROOT_IF(drdu_->Height() != dim_displacement || drdu_->Width() != dim_displacement, "residual Jacobian of an unexpected shape"); + SLIC_ERROR_ROOT_IF(drdu_->Height() != dim_displacement || drdu_->Width() != dim_displacement, + "residual Jacobian of an unexpected shape"); return drdu_.get(); } From 985b45057cc2d3a64488a3d9195c9b679c12144e Mon Sep 17 00:00:00 2001 From: thartland Date: Sun, 30 Nov 2025 14:34:49 -0800 Subject: [PATCH 70/82] spacing --- examples/contact/homotopy/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/contact/homotopy/CMakeLists.txt b/examples/contact/homotopy/CMakeLists.txt index 40c9d33b7..1209453ee 100644 --- a/examples/contact/homotopy/CMakeLists.txt +++ b/examples/contact/homotopy/CMakeLists.txt @@ -6,7 +6,7 @@ if (TRIBOL_FOUND AND STRUMPACK_DIR AND SMITH_ENABLE_CONTINUATION) blt_add_executable( NAME two_blocks_example - SOURCES two_blocks.cpp + SOURCES two_blocks.cpp OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} DEPENDS_ON smith_physics smith_mesh_utils continuation_solver ) From c91cfbdbe70a038ede811388ecb674fda5495aa0 Mon Sep 17 00:00:00 2001 From: thartland Date: Sun, 30 Nov 2025 14:43:54 -0800 Subject: [PATCH 71/82] slightly more verbose param description --- src/smith/physics/constraint.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/smith/physics/constraint.hpp b/src/smith/physics/constraint.hpp index 29d606510..1c081e91e 100644 --- a/src/smith/physics/constraint.hpp +++ b/src/smith/physics/constraint.hpp @@ -41,7 +41,7 @@ class Constraint { * @param time time * @param dt time step * @param fields vector of smith::FiniteElementState* - * @param new_point boolean indicating if this is a new point or not + * @param new_point boolean indicating if this is a new evaluation point or not * @return mfem::Vector which is the constraint evaluation */ virtual mfem::Vector evaluate(double time, double dt, const std::vector& fields, @@ -53,7 +53,7 @@ class Constraint { * @param dt time step * @param fields vector of smith::FiniteElementState* * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new point or not + * @param new_point boolean indicating if this is a new evaluation point or not * @return std::unique_ptr */ virtual std::unique_ptr jacobian(double time, double dt, @@ -67,7 +67,7 @@ class Constraint { * @param dt time step * @param fields vector of smith::FiniteElementState* * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new point or not + * @param new_point boolean indicating if this is a new evaluation point or not * @return std::unique_ptr */ virtual std::unique_ptr jacobian_tilde(double time, double dt, @@ -85,7 +85,7 @@ class Constraint { * @param fields vector of smith::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new point or not + * @param new_point boolean indicating if this is a new evaluation point or not * @return std::Vector */ virtual mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, @@ -108,7 +108,7 @@ class Constraint { * @param fields vector of smith::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new point or not + * @param new_point boolean indicating if this is a new evaluation point or not * @return std::unique_ptr */ virtual std::unique_ptr residual_contribution_jacobian( From 52bb2c1ca0f238f41634fb9c2a0385365f3e2222 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 3 Dec 2025 08:16:13 -0800 Subject: [PATCH 72/82] updating readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 62afbdab2..e43b3ff23 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,10 @@ PackageName: BLT PackageHomePage: https://github.com/LLNL/blt PackageLicenseDeclared: BSD-3-Clause +PackageName: ContinuationSolvers +PackageHomePage: https://github.com/LLNL/ContinuationSolvers +PackageLicenseDeclared: BSD-3-Clause + PackageName: MFEM PackageHomePage: https://github.com/mfem/mfem PackageLicenseDeclared: BSD-3-Clause From 4da74d1e818b62a05758c8422d20e6d8f518d8f3 Mon Sep 17 00:00:00 2001 From: thartland Date: Wed, 3 Dec 2025 09:37:19 -0800 Subject: [PATCH 73/82] bug fix inertia relief example --- examples/inertia_relief/inertia_relief_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index bc25808cd..fb02919dd 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -366,7 +366,7 @@ InertialReliefProblem::InertialReliefProblem(std::vector(constraints_.size()); int myid = mfem::Mpi::WorldRank(); if (myid > 0) { - dim_displacement = 0; + dim_constraints = 0; } SetSizes(dim_displacement, dim_constraints); From d1d4a51fef9dd0e0a88bedf930c33281b7ce2251 Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 5 Dec 2025 12:59:45 -0800 Subject: [PATCH 74/82] comment update --- examples/contact/homotopy/two_blocks.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index 34492cd54..b2692b57b 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -150,7 +150,6 @@ auto createParaviewOutput(const mfem::ParMesh& mesh, const std::vector ess_fixed_tdof_list; mfem::Array ess_disp_tdof_list; mfem::Array ess_bdr_marker(mesh->mfemParMesh().bdr_attributes.Max()); From 522c25c5597cbc00a7ab3772ebdf47f24c621c1b Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 5 Dec 2025 14:24:38 -0800 Subject: [PATCH 75/82] slight restructing, more comments --- examples/contact/homotopy/two_blocks.cpp | 27 +++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index b2692b57b..880ec0b5b 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -155,12 +155,12 @@ int main(int argc, char* argv[]) // Initialize and automatically finalize MPI and other libraries smith::ApplicationManager applicationManager(argc, argv); - int visualize = 1; - int visualize_all_iterates = 1; + int visualize = 0; + int visualize_all_iterates = 0; // command line arguments axom::CLI::App app{"Two block contact."}; app.add_option("--visualize", visualize, "solution visualization") - ->default_val("1") // Matches value set above + ->default_val("0") // Matches value set above ->check(axom::CLI::Range(0, 1)); app.set_help_flag("--help"); @@ -215,7 +215,7 @@ int main(int argc, char* argv[]) contact_states[smith::ContactFields::SHAPE] = 0.0; states[FIELD::VELO] = 0.0; states[FIELD::ACCEL] = 0.0; - params[0] = 1.0; + params[0] = 1.0; // density std::string physics_name = "solid"; @@ -223,16 +223,18 @@ int main(int argc, char* argv[]) auto solid_mechanics_weak_form = std::make_shared(physics_name, mesh, states[FIELD::DISP].space(), getSpaces(params)); + // set material parameters SolidMaterial mat; mat.K = 1.0; mat.G = 0.5; solid_mechanics_weak_form->setMaterial(smith::DependsOn<0>{}, mesh->entireBodyName(), mat); + // constant body force smith::tensor constant_force{}; for (int i = 0; i < dim; i++) { constant_force[i] = 0.0; } - constant_force[dim - 1] = 0.0; + constant_force[dim - 1] = -1.e-4; solid_mechanics_weak_form->addBodyIntegral(mesh->entireBodyName(), [constant_force](double /* t */, auto x) { return smith::tuple{constant_force, 0.0 * smith::get(x)}; @@ -252,8 +254,6 @@ int main(int argc, char* argv[]) ess_bdr_marker = 0; ess_bdr_marker[5] = 1; states[FIELD::DISP].space().GetEssentialTrueDofs(ess_bdr_marker, ess_disp_tdof_list); - mfem::Vector uDC(states[FIELD::DISP].space().GetTrueVSize()); - uDC = 0.0; auto applied_displacement = [](smith::tensor /*x*/) { smith::tensor u{}; u[0] = 0.0; @@ -262,23 +262,30 @@ int main(int argc, char* argv[]) return u; }; states[FIELD::DISP].setFromFieldFunction(applied_displacement); + mfem::Vector uDC(states[FIELD::DISP].space().GetTrueVSize()); + uDC = 0.0; uDC.Set(1.0, states[FIELD::DISP]); + // define tied contact problem TiedContactProblem problem(contact_state_ptrs, residual_state_ptrs, mesh, solid_mechanics_weak_form, contact_constraint, ess_fixed_tdof_list, ess_disp_tdof_list, uDC); - double nonlinear_absolute_tol = 1.e-6; - int nonlinear_max_iterations = 30; - int nonlinear_print_level = 1; // optimization variables auto X0 = problem.GetOptimizationVariable(); auto Xf = problem.GetOptimizationVariable(); + // set optimization parameters + double nonlinear_absolute_tol = 1.e-6; + int nonlinear_max_iterations = 30; + int nonlinear_print_level = 1; + + // setup Homotopy solver HomotopySolver solver(&problem); solver.SetTol(nonlinear_absolute_tol); solver.SetMaxIter(nonlinear_max_iterations); solver.SetPrintLevel(nonlinear_print_level); solver.EnableRegularizedNewtonMode(); solver.EnableSaveIterates(); + // solve tied contact problem via Homotopy solver solver.Mult(X0, Xf); bool converged = solver.GetConverged(); SLIC_WARNING_ROOT_IF(!converged, "Homotopy solver did not converge"); From 5092ab99b76a330c3579b4128ffc346d1ea99f6b Mon Sep 17 00:00:00 2001 From: Tucker Hartland Date: Fri, 19 Dec 2025 08:54:16 -0800 Subject: [PATCH 76/82] Update examples/contact/homotopy/CMakeLists.txt Co-authored-by: Chris White --- examples/contact/homotopy/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/contact/homotopy/CMakeLists.txt b/examples/contact/homotopy/CMakeLists.txt index 1209453ee..11c54c156 100644 --- a/examples/contact/homotopy/CMakeLists.txt +++ b/examples/contact/homotopy/CMakeLists.txt @@ -14,9 +14,9 @@ if (TRIBOL_FOUND AND STRUMPACK_DIR AND SMITH_ENABLE_CONTINUATION) COMMAND two_blocks_example NUM_MPI_TASKS 4) install( - FILES - two_blocks.cpp - DESTINATION - examples/smith/two_blocks + FILES + two_blocks.cpp + DESTINATION + examples/smith/two_blocks ) endif() From d08f8c0138c298f4d0b52187666462295e9ba8c1 Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 19 Dec 2025 09:06:51 -0800 Subject: [PATCH 77/82] new_point -> fresh_evaluation to be more descriptive but language not specifically limited to contact mechanics applications, ^T --> ^Transpose to also be more descriptive/clear/specific --- src/smith/physics/constraint.hpp | 4 +-- src/smith/physics/contact_constraint.hpp | 34 ++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/smith/physics/constraint.hpp b/src/smith/physics/constraint.hpp index 1c081e91e..cf88c33e5 100644 --- a/src/smith/physics/constraint.hpp +++ b/src/smith/physics/constraint.hpp @@ -100,8 +100,8 @@ class Constraint { return y; }; - /** @brief Virtual interface for computing Jacobians of the residual contribution from a vector of - * smith::FiniteElementState* + /** @brief Virtual interface for computing residual contribution Jacobian_tilde^(Transpose) * (Lagrange multiplier) + * from a vector of smith::FiniteElementState* * * @param time time * @param dt time step diff --git a/src/smith/physics/contact_constraint.hpp b/src/smith/physics/contact_constraint.hpp index bb36b1408..4045aa007 100644 --- a/src/smith/physics/contact_constraint.hpp +++ b/src/smith/physics/contact_constraint.hpp @@ -122,16 +122,16 @@ class ContactConstraint : public Constraint { * @param time time * @param dt time step * @param fields vector of smith::FiniteElementState* - * @param new_point boolean indicating if this is a new point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return mfem::Vector which is the constraint evaluation */ mfem::Vector evaluate(double time, double dt, const std::vector& fields, - bool new_point = true) const override + bool fresh_evaluation = true) const override { contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); - if (new_point) { + if (fresh_evaluation) { // note: Tribol does not use cycle. int cycle = 0; contact_.update(cycle, time, dt); @@ -150,17 +150,17 @@ class ContactConstraint : public Constraint { * @param dt time step * @param fields vector of smith::FiniteElementState* * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr The true Jacobian */ std::unique_ptr jacobian(double time, double dt, const std::vector& fields, - int direction, bool new_point = true) const override + int direction, bool fresh_evaluation = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); - if (new_point) { + if (fresh_evaluation) { int cycle = 0; contact_.update(cycle, time, dt); J_contact_ = contact_.mergedJacobian(); @@ -172,27 +172,27 @@ class ContactConstraint : public Constraint { return dgdu; }; - /** @brief Interface for computing residual contribution Jacobian_tilde^T multiplier from a vector of - * smith::FiniteElementState* + /** @brief Interface for computing residual contribution Jacobian_tilde^(Transpose) * (Lagrange multiplier) + * from a vector of smith::FiniteElementState* * * @param time time * @param dt time step * @param fields vector of smith::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::Vector */ mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, int direction, - bool new_point = true) const override + bool fresh_evaluation = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_GAP); int cycle = 0; - if (new_point) { + if (fresh_evaluation) { // we need to call update first to update gaps for (auto& interaction : contact_.getContactInteractions()) { interaction.evalJacobian(false); @@ -222,20 +222,20 @@ class ContactConstraint : public Constraint { * @param fields vector of smith::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ std::unique_ptr residual_contribution_jacobian(double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, int direction, - bool new_point = true) const override + bool fresh_evaluation = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); int cycle = 0; - if (new_point) { + if (fresh_evaluation) { // we need to call update first to update gaps for (auto& interaction : contact_.getContactInteractions()) { interaction.evalJacobian(false); @@ -266,18 +266,18 @@ class ContactConstraint : public Constraint { * @param dt time step * @param fields vector of smith::FiniteElementState* * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, - int direction, bool new_point = true) const override + int direction, bool fresh_evaluation = true) const override { SLIC_ERROR_IF(direction != ContactFields::DISP, "requesting a non displacement-field derivative"); contact_.setDisplacements(*fields[ContactFields::SHAPE], *fields[ContactFields::DISP]); tribol::setLagrangeMultiplierOptions(interaction_id_, tribol::ImplicitEvalMode::MORTAR_JACOBIAN); int cycle = 0; - if (new_point) { + if (fresh_evaluation) { contact_.update(cycle, time, dt); J_contact_.reset(); J_contact_ = contact_.mergedJacobian(); From 0efb68cb1e91fbb78a74122a00b9104488709ddd Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 19 Dec 2025 10:35:31 -0800 Subject: [PATCH 78/82] new_point --> fresh_evaluation --- examples/contact/homotopy/two_blocks.cpp | 33 ++++++++--------- .../inertia_relief/inertia_relief_example.cpp | 36 +++++++++---------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index 80c2752cf..9d81468bd 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -74,11 +74,11 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::shared_ptr mesh, std::shared_ptr weak_form, std::shared_ptr constraints, mfem::Array fixed_tdof_list, mfem::Array disp_tdof_list, mfem::Vector uDC); - mfem::Vector residual(const mfem::Vector& u, bool new_point) const; - mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); - mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; - mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); - mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; + mfem::Vector residual(const mfem::Vector& u, bool fresh_evaluation) const; + mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool fresh_evaluation); + mfem::Vector constraint(const mfem::Vector& u, bool fresh_evaluation) const; + mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool fresh_evaluation); + mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool fresh_evaluation) const; void fullDisplacement(const mfem::Vector& x, mfem::Vector& u); virtual ~TiedContactProblem(); }; @@ -348,7 +348,7 @@ TiedContactProblem::TiedContactProblem(std::vector -mfem::Vector TiedContactProblem::residual(const mfem::Vector& u, bool /*new_point*/) const +mfem::Vector TiedContactProblem::residual(const mfem::Vector& u, bool /*fresh_evaluation*/) const { residual_states_[FIELD::DISP]->Set(1.0, u); auto res = weak_form_->residual(time_info_, shape_disp_.get(), smith::getConstFieldPointers(residual_states_)); @@ -356,7 +356,8 @@ mfem::Vector TiedContactProblem::residual(const mfem::Vector& }; template -mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, bool /*new_point*/) +mfem::HypreParMatrix* TiedContactProblem::residualJacobian(const mfem::Vector& u, + bool /*fresh_evaluation*/) { residual_states_[FIELD::DISP]->Set(1.0, u); drdu_ = weak_form_->jacobian(time_info_, shape_disp_.get(), smith::getConstFieldPointers(residual_states_), @@ -365,33 +366,33 @@ mfem::HypreParMatrix* TiedContactProblem::residualJacobian(co } template -mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u, bool /*new_point*/) const +mfem::Vector TiedContactProblem::constraint(const mfem::Vector& u, bool /*fresh_evaluation*/) const { - bool new_point = true; + bool fresh_evaluation = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); - auto gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), new_point); + auto gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), fresh_evaluation); return gap; } template mfem::HypreParMatrix* TiedContactProblem::constraintJacobian(const mfem::Vector& u, - bool /*new_point*/) + bool /*fresh_evaluation*/) { - bool new_point = true; + bool fresh_evaluation = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); dcdu_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), smith::ContactFields::DISP, - new_point); + fresh_evaluation); return dcdu_.get(); } template mfem::Vector TiedContactProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, - bool /*new_point*/) const + bool /*fresh_evaluation*/) const { - bool new_point = true; + bool fresh_evaluation = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); auto res_contribution = constraints_->residual_contribution(time_, dt_, smith::getConstFieldPointers(contact_states_), - l, smith::ContactFields::DISP, new_point); + l, smith::ContactFields::DISP, fresh_evaluation); return res_contribution; } diff --git a/examples/inertia_relief/inertia_relief_example.cpp b/examples/inertia_relief/inertia_relief_example.cpp index e5e4bf6d2..a1a4c3964 100644 --- a/examples/inertia_relief/inertia_relief_example.cpp +++ b/examples/inertia_relief/inertia_relief_example.cpp @@ -136,9 +136,9 @@ class InertialReliefProblem : public EqualityConstrainedHomotopyProblem { std::shared_ptr weak_form_; // weak_form std::unique_ptr shape_disp_; // shape displacement std::shared_ptr mesh_; - std::vector> constraints_; // vector of constraints - smith::TimeInfo time_info_; // time info for constraint and weak_form function calls - std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; // weights for weak_form_->jacobian calls + std::vector> constraints_; // vector of constraints + smith::TimeInfo time_info_; // time info for constraint and weak_form function calls + std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; // weights for weak_form_->jacobian calls mutable mfem::Vector constraint_cached_; mutable mfem::Vector residual_cached_; mutable mfem::Vector JTvp_cached_; @@ -147,11 +147,11 @@ class InertialReliefProblem : public EqualityConstrainedHomotopyProblem { InertialReliefProblem(std::vector obj_states, std::vector all_states, std::shared_ptr mesh, std::shared_ptr weak_form, std::vector> constraints); - mfem::Vector residual(const mfem::Vector& u, bool new_point) const; - mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool new_point) const; - mfem::Vector constraint(const mfem::Vector& u, bool new_point) const; - mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool new_point); - mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool new_point); + mfem::Vector residual(const mfem::Vector& u, bool fresh_evaluation) const; + mfem::Vector constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, bool fresh_evaluation) const; + mfem::Vector constraint(const mfem::Vector& u, bool fresh_evaluation) const; + mfem::HypreParMatrix* constraintJacobian(const mfem::Vector& u, bool fresh_evaluation); + mfem::HypreParMatrix* residualJacobian(const mfem::Vector& u, bool fresh_evaluation); virtual ~InertialReliefProblem(); }; @@ -381,9 +381,9 @@ InertialReliefProblem::InertialReliefProblem(std::vectorSet(1.0, u); residual_cached_.Set( 1.0, weak_form_->residual(time_info_, shape_disp_.get(), smith::getConstFieldPointers(all_states_))); @@ -393,9 +393,9 @@ mfem::Vector InertialReliefProblem::residual(const mfem::Vector& u, bool new_poi // constraint Jacobian transpose vector product mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, const mfem::Vector& l, - bool new_point) const + bool fresh_evaluation) const { - if (new_point) { + if (fresh_evaluation) { int dim_constraints = GetMultiplierDim(); int dim_displacement = GetDisplacementDim(); obj_states_[FIELD::DISP]->Set(1.0, u); @@ -420,9 +420,9 @@ mfem::Vector InertialReliefProblem::constraintJacobianTvp(const mfem::Vector& u, } // Jacobian of the residual -mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector& u, bool new_point) +mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector& u, bool fresh_evaluation) { - if (new_point) { + if (fresh_evaluation) { obj_states_[FIELD::DISP]->Set(1.0, u); drdu_.reset(); drdu_ = weak_form_->jacobian(time_info_, shape_disp_.get(), getConstFieldPointers(all_states_), jacobian_weights_); @@ -434,9 +434,9 @@ mfem::HypreParMatrix* InertialReliefProblem::residualJacobian(const mfem::Vector } // constraint callback -mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool new_point) const +mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool fresh_evaluation) const { - if (new_point) { + if (fresh_evaluation) { int dim_constraints = GetMultiplierDim(); obj_states_[FIELD::DISP]->Set(1.0, u); @@ -456,11 +456,11 @@ mfem::Vector InertialReliefProblem::constraint(const mfem::Vector& u, bool new_p } // Jacobian of the constraint -mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vector& u, bool new_point) +mfem::HypreParMatrix* InertialReliefProblem::constraintJacobian(const mfem::Vector& u, bool fresh_evaluation) { int dim_constraints = GetMultiplierDim(); int glbdim_displacement = GetGlobalDisplacementDim(); - if (new_point) { + if (fresh_evaluation) { obj_states_[FIELD::DISP]->Set(1.0, u); // dense rows int nentries = glbdim_displacement; From 19244b80977e93ed3ba27cc6c7638e9ee2f78525 Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 19 Dec 2025 10:44:01 -0800 Subject: [PATCH 79/82] constraint --- src/smith/physics/constraint.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/smith/physics/constraint.hpp b/src/smith/physics/constraint.hpp index cf88c33e5..6d58ac879 100644 --- a/src/smith/physics/constraint.hpp +++ b/src/smith/physics/constraint.hpp @@ -41,11 +41,11 @@ class Constraint { * @param time time * @param dt time step * @param fields vector of smith::FiniteElementState* - * @param new_point boolean indicating if this is a new evaluation point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return mfem::Vector which is the constraint evaluation */ virtual mfem::Vector evaluate(double time, double dt, const std::vector& fields, - bool new_point = true) const = 0; + bool fresh_evaluation = true) const = 0; /** @brief Virtual interface for computing constraint Jacobian from a vector of smith::FiniteElementState* * @@ -53,12 +53,12 @@ class Constraint { * @param dt time step * @param fields vector of smith::FiniteElementState* * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new evaluation point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ virtual std::unique_ptr jacobian(double time, double dt, const std::vector& fields, int direction, - bool new_point = true) const = 0; + bool fresh_evaluation = true) const = 0; /** @brief Virtual interface for computing constraint Jacobian_tilde from a vector of smith::FiniteElementState* * Jacobian_tilde is an optional approximation of the true Jacobian @@ -67,17 +67,17 @@ class Constraint { * @param dt time step * @param fields vector of smith::FiniteElementState* * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new evaluation point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ virtual std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, int direction, - bool new_point = true) const + bool fresh_evaluation = true) const { - return jacobian(time, dt, fields, direction, new_point); + return jacobian(time, dt, fields, direction, fresh_evaluation); }; - /** @brief Virtual interface for computing residual contribution Jacobian_tilde^T multiplier from a vector of + /** @brief Virtual interface for computing residual contribution Jacobian_tilde^(Transpose) * (Lagrange multiplier) * smith::FiniteElementState* * * @param time time @@ -85,14 +85,14 @@ class Constraint { * @param fields vector of smith::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new evaluation point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::Vector */ virtual mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, int direction, - bool new_point = true) const + bool fresh_evaluation = true) const { - std::unique_ptr jac = jacobian_tilde(time, dt, fields, direction, new_point); + std::unique_ptr jac = jacobian_tilde(time, dt, fields, direction, fresh_evaluation); mfem::Vector y(jac->Width()); y = 0.0; SLIC_ERROR_ROOT_IF(jac->Height() != multipliers.Size(), "Incompatible matrix and vector sizes."); @@ -108,13 +108,13 @@ class Constraint { * @param fields vector of smith::FiniteElementState* * @param multipliers mfem::Vector of Lagrange multipliers * @param direction index for which field to take the gradient with respect to - * @param new_point boolean indicating if this is a new evaluation point or not + * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ virtual std::unique_ptr residual_contribution_jacobian( [[maybe_unused]] double time, [[maybe_unused]] double dt, [[maybe_unused]] const std::vector& fields, [[maybe_unused]] const mfem::Vector& multipliers, - [[maybe_unused]] int direction, [[maybe_unused]] bool new_point = true) const + [[maybe_unused]] int direction, [[maybe_unused]] bool fresh_evaluation = true) const { SLIC_ERROR_ROOT(axom::fmt::format("Base class must override residual_contribution_jacobian before usage")); std::unique_ptr res_contr_jacobian = nullptr; From 899baad37860f83c12dcd93288e197f0db51028e Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 19 Dec 2025 11:00:28 -0800 Subject: [PATCH 80/82] pushing changes prior to developing on another machine --- src/smith/physics/constraint.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/smith/physics/constraint.hpp b/src/smith/physics/constraint.hpp index 6d58ac879..14b5f6e43 100644 --- a/src/smith/physics/constraint.hpp +++ b/src/smith/physics/constraint.hpp @@ -44,7 +44,7 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return mfem::Vector which is the constraint evaluation */ - virtual mfem::Vector evaluate(double time, double dt, const std::vector& fields, + virtual mfem::Vector evaluate(TimeInfo time_info, const std::vector& fields, bool fresh_evaluation = true) const = 0; /** @brief Virtual interface for computing constraint Jacobian from a vector of smith::FiniteElementState* @@ -56,7 +56,7 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ - virtual std::unique_ptr jacobian(double time, double dt, + virtual std::unique_ptr jacobian(TimeInfo time_info, const std::vector& fields, int direction, bool fresh_evaluation = true) const = 0; @@ -70,11 +70,11 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ - virtual std::unique_ptr jacobian_tilde(double time, double dt, + virtual std::unique_ptr jacobian_tilde(TimeInfo time_info, const std::vector& fields, int direction, bool fresh_evaluation = true) const { - return jacobian(time, dt, fields, direction, fresh_evaluation); + return jacobian(time_info, fields, direction, fresh_evaluation); }; /** @brief Virtual interface for computing residual contribution Jacobian_tilde^(Transpose) * (Lagrange multiplier) @@ -88,11 +88,11 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::Vector */ - virtual mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, + virtual mfem::Vector residual_contribution(TimeInfo time_info, const std::vector& fields, const mfem::Vector& multipliers, int direction, bool fresh_evaluation = true) const { - std::unique_ptr jac = jacobian_tilde(time, dt, fields, direction, fresh_evaluation); + std::unique_ptr jac = jacobian_tilde(time_info, fields, direction, fresh_evaluation); mfem::Vector y(jac->Width()); y = 0.0; SLIC_ERROR_ROOT_IF(jac->Height() != multipliers.Size(), "Incompatible matrix and vector sizes."); From ef13bb4d3880c19c6df10023e972355f375a3966 Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 19 Dec 2025 11:31:56 -0800 Subject: [PATCH 81/82] reverting --- src/smith/physics/constraint.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/smith/physics/constraint.hpp b/src/smith/physics/constraint.hpp index 6d58ac879..14b5f6e43 100644 --- a/src/smith/physics/constraint.hpp +++ b/src/smith/physics/constraint.hpp @@ -44,7 +44,7 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return mfem::Vector which is the constraint evaluation */ - virtual mfem::Vector evaluate(double time, double dt, const std::vector& fields, + virtual mfem::Vector evaluate(TimeInfo time_info, const std::vector& fields, bool fresh_evaluation = true) const = 0; /** @brief Virtual interface for computing constraint Jacobian from a vector of smith::FiniteElementState* @@ -56,7 +56,7 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ - virtual std::unique_ptr jacobian(double time, double dt, + virtual std::unique_ptr jacobian(TimeInfo time_info, const std::vector& fields, int direction, bool fresh_evaluation = true) const = 0; @@ -70,11 +70,11 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ - virtual std::unique_ptr jacobian_tilde(double time, double dt, + virtual std::unique_ptr jacobian_tilde(TimeInfo time_info, const std::vector& fields, int direction, bool fresh_evaluation = true) const { - return jacobian(time, dt, fields, direction, fresh_evaluation); + return jacobian(time_info, fields, direction, fresh_evaluation); }; /** @brief Virtual interface for computing residual contribution Jacobian_tilde^(Transpose) * (Lagrange multiplier) @@ -88,11 +88,11 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::Vector */ - virtual mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, + virtual mfem::Vector residual_contribution(TimeInfo time_info, const std::vector& fields, const mfem::Vector& multipliers, int direction, bool fresh_evaluation = true) const { - std::unique_ptr jac = jacobian_tilde(time, dt, fields, direction, fresh_evaluation); + std::unique_ptr jac = jacobian_tilde(time_info, fields, direction, fresh_evaluation); mfem::Vector y(jac->Width()); y = 0.0; SLIC_ERROR_ROOT_IF(jac->Height() != multipliers.Size(), "Incompatible matrix and vector sizes."); From 99d4f6b967fa4190e16fba6ab6b8e5a311c7a8ca Mon Sep 17 00:00:00 2001 From: thartland Date: Fri, 19 Dec 2025 12:38:38 -0800 Subject: [PATCH 82/82] example ConstrainedHomotopyProblem now have TimeInfo variables, consistent for both inertia relief and two block contact. However, tribol and weak_form have different call strutures. Constraint conforms to Tribol and we use accessor functions time() and dt() from TimeInfo struct to tribol call structure via constraint class --- examples/contact/homotopy/two_blocks.cpp | 19 ++++++++++--------- src/smith/physics/constraint.hpp | 12 ++++++------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/examples/contact/homotopy/two_blocks.cpp b/examples/contact/homotopy/two_blocks.cpp index 9d81468bd..a37df57c2 100644 --- a/examples/contact/homotopy/two_blocks.cpp +++ b/examples/contact/homotopy/two_blocks.cpp @@ -64,9 +64,7 @@ class TiedContactProblem : public EqualityConstrainedHomotopyProblem { std::unique_ptr shape_disp_; std::shared_ptr mesh_; std::shared_ptr constraints_; - double time_ = 0.0; - double dt_ = 0.0; - smith::TimeInfo time_info_ = smith::TimeInfo(0.0, 0.0, 0); + smith::TimeInfo time_info_; std::vector jacobian_weights_ = {1.0, 0.0, 0.0, 0.0}; public: @@ -325,7 +323,8 @@ TiedContactProblem::TiedContactProblem(std::vector::constraint(const mfem::Vecto { bool fresh_evaluation = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); - auto gap = constraints_->evaluate(time_, dt_, smith::getConstFieldPointers(contact_states_), fresh_evaluation); + auto gap = constraints_->evaluate(time_info_.time(), time_info_.dt(), smith::getConstFieldPointers(contact_states_), + fresh_evaluation); return gap; } @@ -380,8 +380,8 @@ mfem::HypreParMatrix* TiedContactProblem::constraintJacobian( { bool fresh_evaluation = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); - dcdu_ = constraints_->jacobian(time_, dt_, smith::getConstFieldPointers(contact_states_), smith::ContactFields::DISP, - fresh_evaluation); + dcdu_ = constraints_->jacobian(time_info_.time(), time_info_.dt(), smith::getConstFieldPointers(contact_states_), + smith::ContactFields::DISP, fresh_evaluation); return dcdu_.get(); } @@ -391,8 +391,9 @@ mfem::Vector TiedContactProblem::constraintJacobianTvp(const { bool fresh_evaluation = true; contact_states_[smith::ContactFields::DISP]->Set(1.0, u); - auto res_contribution = constraints_->residual_contribution(time_, dt_, smith::getConstFieldPointers(contact_states_), - l, smith::ContactFields::DISP, fresh_evaluation); + auto res_contribution = constraints_->residual_contribution(time_info_.time(), time_info_.dt(), + smith::getConstFieldPointers(contact_states_), l, + smith::ContactFields::DISP, fresh_evaluation); return res_contribution; } diff --git a/src/smith/physics/constraint.hpp b/src/smith/physics/constraint.hpp index 14b5f6e43..6d58ac879 100644 --- a/src/smith/physics/constraint.hpp +++ b/src/smith/physics/constraint.hpp @@ -44,7 +44,7 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return mfem::Vector which is the constraint evaluation */ - virtual mfem::Vector evaluate(TimeInfo time_info, const std::vector& fields, + virtual mfem::Vector evaluate(double time, double dt, const std::vector& fields, bool fresh_evaluation = true) const = 0; /** @brief Virtual interface for computing constraint Jacobian from a vector of smith::FiniteElementState* @@ -56,7 +56,7 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ - virtual std::unique_ptr jacobian(TimeInfo time_info, + virtual std::unique_ptr jacobian(double time, double dt, const std::vector& fields, int direction, bool fresh_evaluation = true) const = 0; @@ -70,11 +70,11 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::unique_ptr */ - virtual std::unique_ptr jacobian_tilde(TimeInfo time_info, + virtual std::unique_ptr jacobian_tilde(double time, double dt, const std::vector& fields, int direction, bool fresh_evaluation = true) const { - return jacobian(time_info, fields, direction, fresh_evaluation); + return jacobian(time, dt, fields, direction, fresh_evaluation); }; /** @brief Virtual interface for computing residual contribution Jacobian_tilde^(Transpose) * (Lagrange multiplier) @@ -88,11 +88,11 @@ class Constraint { * @param fresh_evaluation boolean indicating if we re-evaluate or use previously cached evaluation * @return std::Vector */ - virtual mfem::Vector residual_contribution(TimeInfo time_info, const std::vector& fields, + virtual mfem::Vector residual_contribution(double time, double dt, const std::vector& fields, const mfem::Vector& multipliers, int direction, bool fresh_evaluation = true) const { - std::unique_ptr jac = jacobian_tilde(time_info, fields, direction, fresh_evaluation); + std::unique_ptr jac = jacobian_tilde(time, dt, fields, direction, fresh_evaluation); mfem::Vector y(jac->Width()); y = 0.0; SLIC_ERROR_ROOT_IF(jac->Height() != multipliers.Size(), "Incompatible matrix and vector sizes.");