21 #include <unordered_map>
27 #include "smith/smith_config.hpp"
31 #include "smith/physics/boundary_conditions/components.hpp"
42 #include "smith/numerics/functional/differentiate_wrt.hpp"
44 #include "smith/numerics/functional/geometry.hpp"
48 #include "smith/numerics/petsc_solvers.hpp"
56 namespace solid_mechanics {
62 void adjoint_integrate(
double dt_n,
double dt_np1, mfem::HypreParMatrix* m_mat, mfem::HypreParMatrix* k_mat,
63 mfem::HypreParVector& disp_adjoint_load_vector, mfem::HypreParVector& velo_adjoint_load_vector,
64 mfem::HypreParVector& accel_adjoint_load_vector, mfem::HypreParVector& adjoint_displacement_,
65 mfem::HypreParVector& implicit_sensitivity_displacement_start_of_step_,
66 mfem::HypreParVector& implicit_sensitivity_velocity_start_of_step_,
67 mfem::HypreParVector& adjoint_essential_, BoundaryConditionManager& bcs_,
68 mfem::Solver& lin_solver);
77 .preconditioner = smith::ordering == mfem::Ordering::byVDIM
80 .relative_tol = 1.0e-6,
81 .absolute_tol = 1.0e-16,
82 .max_iterations = 500,
86 #ifdef MFEM_USE_STRUMPACK
98 .relative_tol = 1.0e-4,
99 .absolute_tol = 1.0e-8,
100 .max_iterations = 10,
112 template <
int order,
int dim,
typename parameters = Parameters<>,
113 typename parameter_indices = std::make_
integer_sequence<
int, parameters::n>>
126 template <
int order,
int dim,
typename... parameter_space,
int... parameter_indices>
131 static constexpr
int VALUE = 0, DERIVATIVE = 1;
132 static constexpr
int SHAPE = 0;
133 static constexpr
auto I = Identity<dim>();
138 static constexpr
auto NUM_STATE_VARS = 2;
142 template <
typename T>
165 std::shared_ptr<smith::Mesh> smith_mesh, std::vector<std::string> parameter_names = {},
int cycle = 0,
166 double time = 0.0,
bool checkpoint_to_disk =
false,
bool use_warm_start =
true)
167 :
SolidMechanics(std::make_unique<EquationSolver>(nonlinear_opts, lin_opts, smith_mesh->getComm()),
168 timestepping_opts, physics_name, smith_mesh, parameter_names, cycle, time, checkpoint_to_disk,
191 const std::string& physics_name, std::shared_ptr<smith::Mesh> smith_mesh,
192 std::vector<std::string> parameter_names = {},
int cycle = 0,
double time = 0.0,
193 bool checkpoint_to_disk =
false,
bool use_warm_start =
true)
194 :
BasePhysics(physics_name, smith_mesh, cycle, time, checkpoint_to_disk),
197 velocity_(
StateManager::newState(H1<order, dim>{}, detail::addPrefix(physics_name,
"velocity"), mesh_->tag())),
201 H1<order, dim>{}, detail::addPrefix(physics_name,
"adjoint_displacement"), mesh_->tag())),
202 displacement_adjoint_load_(displacement_.space(), detail::addPrefix(physics_name,
"displacement_adjoint_load")),
203 velocity_adjoint_load_(displacement_.space(), detail::addPrefix(physics_name,
"velocity_adjoint_load")),
204 acceleration_adjoint_load_(displacement_.space(), detail::addPrefix(physics_name,
"acceleration_adjoint_load")),
205 implicit_sensitivity_displacement_start_of_step_(displacement_.space(),
"total_deriv_wrt_displacement."),
206 implicit_sensitivity_velocity_start_of_step_(displacement_.space(),
"total_deriv_wrt_velocity."),
207 reactions_(
StateManager::newDual(H1<order, dim>{}, detail::addPrefix(physics_name,
"reactions"), mesh_->tag())),
208 reactions_adjoint_bcs_(reactions_.space(),
"reactions_shape_sensitivity"),
209 nonlin_solver_(std::move(solver)),
210 ode2_(displacement_.space().TrueVSize(),
211 {.time = time_, .c0 = c0_, .c1 = c1_, .u = u_, .du_dt = v_, .d2u_dt2 = acceleration_}, *nonlin_solver_,
213 use_warm_start_(use_warm_start)
216 SLIC_ERROR_ROOT_IF(mfemParMesh().Dimension() != dim,
217 axom::fmt::format(
"Compile time dimension, {0}, and runtime mesh dimension, {1}, mismatch", dim,
218 mfemParMesh().Dimension()));
220 SLIC_ERROR_ROOT_IF(!nonlin_solver_,
221 "EquationSolver argument is nullptr in SolidMechanics constructor. It is possible that it was "
222 "previously moved.");
226 ode2_.SetTimestepper(timestepping_opts.
timestepper);
228 is_quasistatic_ =
false;
230 is_quasistatic_ =
true;
233 states_.push_back(&displacement_);
234 if (!is_quasistatic_) {
235 states_.push_back(&velocity_);
236 states_.push_back(&acceleration_);
239 adjoints_.push_back(&adjoint_displacement_);
240 duals_.push_back(&reactions_);
241 dual_adjoints_.push_back(&reactions_adjoint_bcs_);
244 const mfem::ParFiniteElementSpace* test_space = &displacement_.space();
245 const mfem::ParFiniteElementSpace* shape_space = &mesh_->shapeDisplacementSpace();
247 std::array<
const mfem::ParFiniteElementSpace*, NUM_STATE_VARS +
sizeof...(parameter_space)> trial_spaces;
248 trial_spaces[0] = &displacement_.space();
249 trial_spaces[1] = &displacement_.space();
252 sizeof...(parameter_space) != parameter_names.size(),
253 axom::fmt::format(
"{} parameter spaces given in the template argument but {} parameter names were supplied.",
254 sizeof...(parameter_space), parameter_names.size()));
256 if constexpr (
sizeof...(parameter_space) > 0) {
257 tuple<parameter_space...> types{};
259 parameters_.emplace_back(mfemParMesh(), get<i>(types), detail::addPrefix(name_, parameter_names[i]));
260 trial_spaces[i + NUM_STATE_VARS] = &(parameters_[i].state->space());
264 residual_ = std::make_unique<ShapeAwareFunctional<shape_trial, test(trial, trial, parameter_space...)>>(
265 shape_space, test_space, trial_spaces);
269 auto* amg_prec =
dynamic_cast<mfem::HypreBoomerAMG*
>(&nonlin_solver_->preconditioner());
278 amg_prec->SetSystemsOptions(displacement_.space().GetVDim(), smith::ordering == mfem::Ordering::byNODES);
281 int true_size = velocity_.space().TrueVSize();
283 u_.SetSize(true_size);
284 v_.SetSize(true_size);
285 du_.SetSize(true_size);
286 predicted_displacement_.SetSize(true_size);
288 initializeSolidMechanicsStates();
303 time_end_step_ = 0.0;
309 adjoint_displacement_ = 0.0;
310 displacement_adjoint_load_ = 0.0;
311 velocity_adjoint_load_ = 0.0;
312 acceleration_adjoint_load_ = 0.0;
314 implicit_sensitivity_displacement_start_of_step_ = 0.0;
315 implicit_sensitivity_velocity_start_of_step_ = 0.0;
318 reactions_adjoint_bcs_ = 0.0;
323 predicted_displacement_ = 0.0;
330 initializeSolidMechanicsStates();
332 if (checkpoint_to_disk_) {
335 checkpoint_states_.clear();
336 auto state_names = stateNames();
337 for (
const auto& state_name : state_names) {
338 checkpoint_states_[state_name].push_back(state(state_name));
351 template <
typename T>
387 template <
typename AppliedDisplacementFunction>
391 for (
int i = 0; i < dim; ++i) {
392 if (components[
size_t(i)]) {
393 auto mfem_coefficient_function = [applied_displacement, i](
const mfem::Vector& X_mfem,
double t) {
394 auto X = make_tensor<dim>([&X_mfem](
int k) {
return X_mfem[k]; });
395 return applied_displacement(X, t)[i];
398 component_disp_bdr_coef_ = std::make_shared<mfem::FunctionCoefficient>(mfem_coefficient_function);
400 auto dof_list = domain.
dof_list(&displacement_.space());
403 displacement_.space().DofsToVDofs(i, dof_list);
405 bcs_.addEssential(dof_list, component_disp_bdr_coef_, displacement_.space(), i);
422 setDisplacementBCs(zero_vector_function, domain, components);
448 template <
typename AppliedDisplacementFunction>
452 auto mfem_vector_coefficient_function = [applied_displacement](
const mfem::Vector& X_mfem,
double t,
453 mfem::Vector& u_mfem) {
454 auto X = make_tensor<dim>([&X_mfem](
int k) {
return X_mfem[k]; });
455 auto u = applied_displacement(X, t);
456 for (
int i = 0; i < dim; i++) {
460 disp_bdr_coef_ = std::make_shared<mfem::VectorFunctionCoefficient>(dim, mfem_vector_coefficient_function);
461 bcs_.addEssentialByTrueDofs(true_dofs, disp_bdr_coef_, displacement_.space());
467 if (state_name ==
"displacement") {
468 return displacement_;
469 }
else if (state_name ==
"velocity") {
471 }
else if (state_name ==
"acceleration") {
472 return acceleration_;
475 SLIC_ERROR_ROOT(axom::fmt::format(
"State '{}' requested from solid mechanics module '{}', but it doesn't exist",
477 return displacement_;
491 if (state_name ==
"displacement") {
492 displacement_ = state;
493 if (!checkpoint_to_disk_) {
494 checkpoint_states_[
"displacement"][
static_cast<size_t>(cycle_)] = displacement_;
497 }
else if (state_name ==
"velocity") {
499 if (!checkpoint_to_disk_) {
500 checkpoint_states_[
"velocity"][
static_cast<size_t>(cycle_)] = velocity_;
505 SLIC_ERROR_ROOT(axom::fmt::format(
506 "setState for state named '{}' requested from solid mechanics module '{}', but it doesn't exist", state_name,
513 if (is_quasistatic_) {
514 return std::vector<std::string>{
"displacement"};
516 return std::vector<std::string>{
"displacement",
"velocity",
"acceleration"};
541 template <
int... active_parameters,
typename callable>
543 const std::optional<Domain>& optional_domain = std::nullopt)
552 std::vector<std::string>
adjointNames()
const override {
return std::vector<std::string>{{
"displacement"}}; }
557 if (state_name ==
"displacement") {
558 return adjoint_displacement_;
561 SLIC_ERROR_ROOT(axom::fmt::format(
"adjoint '{}' requested from solid mechanics module '{}', but it doesn't exist",
563 return adjoint_displacement_;
567 std::vector<std::string>
dualNames()
const override {
return std::vector<std::string>{{
"reactions"}}; }
572 if (dual_name ==
"reactions") {
576 SLIC_ERROR_ROOT(axom::fmt::format(
"dual '{}' requested from solid mechanics module '{}', but it doesn't exist",
584 if (dual_name ==
"reactions") {
585 return reactions_adjoint_bcs_;
588 SLIC_ERROR_ROOT(axom::fmt::format(
589 "dualAdjoint '{}' requested from solid mechanics module '{}', but it doesn't exist", dual_name, name_));
590 return reactions_adjoint_bcs_;
596 if (dual_name ==
"reactions") {
597 auto checkpointed_sol = getCheckpointedStates(cycle);
601 if (is_quasistatic_) {
602 reactions = (*residual_)(time_, shapeDisplacement(), checkpointed_sol.at(
"displacement"), acceleration_,
603 *parameters_[parameter_indices].state...);
605 reactions = (*residual_)(time_, shapeDisplacement(), checkpointed_sol.at(
"displacement"),
606 checkpointed_sol.at(
"acceleration"), *parameters_[parameter_indices].state...);
613 axom::fmt::format(
"loadCheckpointedDual '{}' requested from solid mechanics module '{}', but it doesn't exist",
648 template <
int... active_parameters,
typename callable,
typename StateType =
Nothing>
652 residual_->AddDomainIntegral(
Dimension<dim>{},
DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, qfunction,
660 template <
typename Material>
661 struct MaterialStressFunctor {
683 template <
typename X,
typename State,
typename Displacement,
typename Acceleration,
typename... Params>
685 Params... params)
const
687 auto du_dX = get<DERIVATIVE>(displacement);
688 auto d2u_dt2 = get<VALUE>(acceleration);
690 auto stress = material_(state, du_dX, params...);
692 return smith::tuple{material_.density * d2u_dt2, stress};
721 template <
int... active_parameters,
typename MaterialType,
typename StateType =
Empty>
725 static_assert(std::is_same_v<StateType, Empty> || std::is_same_v<StateType, typename MaterialType::State>,
726 "invalid quadrature data provided in setMaterial()");
727 MaterialStressFunctor<MaterialType> material_functor(material);
728 residual_->AddDomainIntegral(
731 active_parameters + NUM_STATE_VARS...>{},
735 std::move(material_functor), domain, qdata);
739 template <
typename MaterialType,
typename StateType = Empty>
743 setMaterial(
DependsOn<>{}, material, domain, qdata);
752 template <
typename Material>
753 struct RateDependentMaterialStressFunctor {
780 template <
typename X,
typename State,
typename Displacement,
typename Acceleration,
typename... Params>
782 Params... params)
const
784 auto du_dX = get<DERIVATIVE>(displacement);
785 auto d2u_dt2 = get<VALUE>(acceleration);
787 auto stress = material_(state, *time_increment_, du_dX, params...);
789 return smith::tuple{material_.density * d2u_dt2, stress};
796 template <
int... active_parameters,
typename RateDependentMaterialType,
typename StateType =
Empty>
801 std::is_same_v<StateType, Empty> || std::is_same_v<StateType, typename RateDependentMaterialType::State>,
802 "invalid quadrature data provided in setMaterial()");
803 RateDependentMaterialStressFunctor<RateDependentMaterialType> material_functor(material, &dt_);
804 residual_->AddDomainIntegral(
807 active_parameters + NUM_STATE_VARS...>{},
811 std::move(material_functor), domain, qdata);
815 template <
typename RateDependentMaterialType,
typename StateType = Empty>
819 setRateDependentMaterial(
DependsOn<>{}, material, domain, qdata);
834 template <
typename AppliedDisplacementFunction>
837 displacement_.setFromFieldFunction(applied_displacement);
855 template <
typename AppliedVelocityFunction>
858 velocity_.setFromFieldFunction(applied_velocity);
868 template <
typename BodyForceType>
869 struct BodyForceIntegrand {
889 template <
typename T,
typename Position,
typename Displacement,
typename Acceleration,
typename... Params>
892 return smith::tuple{-1.0 * body_force_(get<VALUE>(position), t, params...),
zero{}};
915 template <
int... active_parameters,
typename BodyForceType>
919 BodyForceIntegrand<BodyForceType>(body_force), domain);
923 template <
typename BodyForceType>
952 template <
int... active_parameters,
typename TractionType>
955 residual_->AddBoundaryIntegral(
957 [traction_function](
double t,
auto X,
auto ,
auto ,
auto... params) {
958 auto n =
cross(get<DERIVATIVE>(X));
960 return -1.0 * traction_function(get<VALUE>(X),
normalize(n), t, params...);
966 template <
typename TractionType>
969 setTraction(
DependsOn<>{}, traction_function, domain);
995 template <
int... active_parameters,
typename PressureType>
998 residual_->AddBoundaryIntegral(
1000 [pressure_function](
double t,
auto X,
auto displacement,
auto ,
auto... params) {
1002 auto x = X + displacement;
1004 auto n =
cross(get<DERIVATIVE>(x));
1018 return pressure_function(get<VALUE>(X), t, params...) * (n /
norm(
cross(get<DERIVATIVE>(X))));
1024 template <
typename PressureType>
1027 setPressure(
DependsOn<>{}, pressure_function, domain);
1035 return std::make_unique<mfem_ext::StdFunctionOperator>(
1036 displacement_.space().TrueVSize(),
1039 [
this](
const mfem::Vector& u, mfem::Vector& r) {
1040 SMITH_MARK_FUNCTION;
1041 const mfem::Vector res =
1042 (*residual_)(time_, shapeDisplacement(), u, acceleration_, *parameters_[parameter_indices].state...);
1048 r.SetSubVector(bcs_.allEssentialTrueDofs(), 0.0);
1052 [
this](
const mfem::Vector& u) -> mfem::Operator& {
1053 SMITH_MARK_FUNCTION;
1054 auto [r, drdu] = (*residual_)(time_, shapeDisplacement(), differentiate_wrt(u), acceleration_,
1055 *parameters_[parameter_indices].state...);
1057 J_ = assemble(drdu);
1059 J_e_ = bcs_.eliminateAllEssentialDofsFromMatrix(*J_);
1075 std::pair<const mfem::HypreParMatrix&, const mfem::HypreParMatrix&>
stiffnessMatrix()
const
1077 SLIC_ERROR_ROOT_IF(!J_ || !J_e_,
"Stiffness matrix has not yet been assembled.");
1079 return {*J_, *J_e_};
1085 if (is_quasistatic_) {
1086 residual_with_bcs_ = buildQuasistaticOperator();
1091 residual_with_bcs_ = std::make_unique<mfem_ext::StdFunctionOperator>(
1092 displacement_.space().TrueVSize(),
1094 [
this](
const mfem::Vector& d2u_dt2, mfem::Vector& r) {
1095 add(1.0, u_, c0_, d2u_dt2, predicted_displacement_);
1096 const mfem::Vector res = (*residual_)(time_, shapeDisplacement(), predicted_displacement_, d2u_dt2,
1097 *parameters_[parameter_indices].state...);
1103 r.SetSubVector(bcs_.allEssentialTrueDofs(), 0.0);
1106 [
this](
const mfem::Vector& d2u_dt2) -> mfem::Operator& {
1107 add(1.0, u_, c0_, d2u_dt2, predicted_displacement_);
1110 auto K = smith::get<DERIVATIVE>((*residual_)(time_, shapeDisplacement(),
1111 differentiate_wrt(predicted_displacement_), d2u_dt2,
1112 *parameters_[parameter_indices].state...));
1113 std::unique_ptr<mfem::HypreParMatrix> k_mat(assemble(K));
1116 auto M = smith::get<DERIVATIVE>((*residual_)(time_, shapeDisplacement(), predicted_displacement_,
1117 differentiate_wrt(d2u_dt2),
1118 *parameters_[parameter_indices].state...));
1119 std::unique_ptr<mfem::HypreParMatrix> m_mat(assemble(M));
1123 J_.reset(mfem::Add(1.0, *m_mat, c0_, *k_mat));
1125 J_e_ = bcs_.eliminateAllEssentialDofsFromMatrix(*J_);
1131 #ifdef SMITH_USE_PETSC
1132 auto* space_dep_pc =
1133 dynamic_cast<smith::mfem_ext::PetscPreconditionerSpaceDependent*
>(&nonlin_solver_->preconditioner());
1137 space_dep_pc->SetFESpace(&displacement_.space());
1141 nonlin_solver_->setOperator(*residual_with_bcs_);
1143 if (checkpoint_to_disk_) {
1144 outputStateToDisk();
1146 checkpoint_states_.clear();
1147 auto state_names = stateNames();
1148 for (
const auto& state_name : state_names) {
1149 checkpoint_states_[state_name].push_back(state(state_name));
1157 for (
const auto& essential : bcs_.essentials()) {
1158 field.SetSubVector(essential.getTrueDofList(), 0.0);
1166 SLIC_ERROR_ROOT_IF(!residual_,
"completeSetup() must be called prior to advanceTimestep(dt) in SolidMechanics.");
1170 for (
auto& parameter : parameters_) {
1171 *parameter.previous_state = *parameter.state;
1175 if (is_quasistatic_) {
1176 quasiStaticSolve(dt);
1184 double time_tmp = time_;
1186 ode2_.Step(displacement_, velocity_, time_tmp, dt);
1191 if (checkpoint_to_disk_) {
1192 outputStateToDisk();
1194 for (
const auto& state_name : stateNames()) {
1195 checkpoint_states_[state_name].push_back(state(state_name));
1203 residual_->updateQdata(
true);
1205 reactions_ = (*residual_)(time_, shapeDisplacement(), displacement_, acceleration_,
1206 *parameters_[parameter_indices].state...);
1208 residual_->updateQdata(
false);
1211 if (cycle_ > max_cycle_) {
1212 timesteps_.push_back(dt);
1213 max_cycle_ = cycle_;
1232 virtual void setAdjointLoad(std::unordered_map<std::string, const smith::FiniteElementDual&> loads)
override
1234 SLIC_ERROR_ROOT_IF(loads.size() == 0,
"Adjoint load container size must be greater than 0 in the solid mechanics.");
1236 auto disp_adjoint_load = loads.find(
"displacement");
1238 SLIC_ERROR_ROOT_IF(disp_adjoint_load == loads.end(),
"Adjoint load for \"displacement\" not found.");
1240 if (disp_adjoint_load != loads.end()) {
1241 displacement_adjoint_load_ = disp_adjoint_load->second;
1243 displacement_adjoint_load_ *= -1.0;
1246 auto velo_adjoint_load = loads.find(
"velocity");
1248 if (velo_adjoint_load != loads.end()) {
1249 velocity_adjoint_load_ = velo_adjoint_load->second;
1251 velocity_adjoint_load_ *= -1.0;
1254 auto accel_adjoint_load = loads.find(
"acceleration");
1256 if (accel_adjoint_load != loads.end()) {
1257 acceleration_adjoint_load_ = accel_adjoint_load->second;
1259 acceleration_adjoint_load_ *= -1.0;
1263 virtual void setDualAdjointBcs(std::unordered_map<std::string, const smith::FiniteElementState&> bcs)
override
1265 SLIC_ERROR_ROOT_IF(bcs.size() == 0,
"Adjoint load container size must be greater than 0 in the solid mechanics.");
1267 auto reaction_adjoint_load = bcs.find(
"reactions");
1269 SLIC_ERROR_ROOT_IF(reaction_adjoint_load == bcs.end(),
"Adjoint load for \"reaction\" not found.");
1271 if (reaction_adjoint_load != bcs.end()) {
1272 reactions_adjoint_bcs_ = reaction_adjoint_load->second;
1280 SLIC_ERROR_ROOT_IF(cycle_ <= min_cycle_,
1281 "Maximum number of adjoint timesteps exceeded! The number of adjoint timesteps must equal the "
1282 "number of forward timesteps");
1286 double dt_np1_to_np2 = getCheckpointedTimestep(cycle_ + 1);
1287 const double dt_n_to_np1 = getCheckpointedTimestep(cycle_);
1289 auto end_step_solution = getCheckpointedStates(cycle_ + 1);
1291 displacement_ = end_step_solution.at(
"displacement");
1293 if (is_quasistatic_) {
1294 quasiStaticAdjointSolve(dt_n_to_np1);
1297 "Only Newmark implemented for transient adjoint solid mechanics.");
1301 velocity_ = end_step_solution.at(
"velocity");
1302 acceleration_ = end_step_solution.at(
"acceleration");
1305 auto K = smith::get<DERIVATIVE>((*residual_)(time_, shapeDisplacement(),
differentiate_wrt(displacement_),
1306 acceleration_, *parameters_[parameter_indices].state...));
1307 std::unique_ptr<mfem::HypreParMatrix> k_mat(assemble(K));
1310 auto M = smith::get<DERIVATIVE>((*residual_)(time_, shapeDisplacement(), displacement_,
1312 *parameters_[parameter_indices].state...));
1313 std::unique_ptr<mfem::HypreParMatrix> m_mat(assemble(M));
1315 auto& lin_solver = nonlin_solver_->linearSolver();
1316 solid_mechanics::detail::adjoint_integrate(
1317 dt_n_to_np1, dt_np1_to_np2, m_mat.get(), k_mat.get(), displacement_adjoint_load_, velocity_adjoint_load_,
1318 acceleration_adjoint_load_, adjoint_displacement_, implicit_sensitivity_displacement_start_of_step_,
1319 implicit_sensitivity_velocity_start_of_step_, reactions_adjoint_bcs_, bcs_, lin_solver);
1322 time_end_step_ = time_;
1323 time_ -= dt_n_to_np1;
1329 SLIC_ASSERT_MSG(parameter_field <
sizeof...(parameter_indices),
1330 axom::fmt::format(
"Invalid parameter index '{}' requested for sensitivity."));
1332 auto drdparam = smith::get<DERIVATIVE>(d_residual_d_[parameter_field](time_end_step_));
1333 auto drdparam_mat = assemble(drdparam);
1335 drdparam_mat->MultTranspose(adjoint_displacement_, *parameters_[parameter_field].sensitivity);
1337 return *parameters_[parameter_field].sensitivity;
1344 smith::get<DERIVATIVE>((*residual_)(time_end_step_,
differentiate_wrt(shapeDisplacement()), displacement_,
1345 acceleration_, *parameters_[parameter_indices].state...));
1347 auto drdshape_mat = assemble(drdshape);
1349 drdshape_mat->MultTranspose(adjoint_displacement_, shape_displacement_dual_);
1351 return shapeDisplacementSensitivity();
1358 return {{
"displacement", implicit_sensitivity_displacement_start_of_step_},
1359 {
"velocity", implicit_sensitivity_velocity_start_of_step_}};
1448 std::unique_ptr<mfem::HypreParMatrix>
J_;
1452 std::unique_ptr<mfem::HypreParMatrix>
J_e_;
1488 std::array<std::function<decltype((*residual_)(
DifferentiateWRT<1>{}, 0.0, shape_displacement_, displacement_,
1489 acceleration_, *parameters_[parameter_indices].state...))(double)>,
1490 sizeof...(parameter_indices)>
1491 d_residual_d_ = {[&](
double _t) {
1493 displacement_, acceleration_, *parameters_[parameter_indices].state...);
1501 warmStartDisplacement(dt);
1506 nonlin_solver_->solve(displacement_);
1512 auto [_, drdu] = (*residual_)(time_, shapeDisplacement(),
differentiate_wrt(displacement_), acceleration_,
1513 *parameters_[parameter_indices].state...);
1515 J_ = assemble(drdu);
1517 auto J_T = std::unique_ptr<mfem::HypreParMatrix>(J_->Transpose());
1520 J_e_ = bcs_.eliminateAllEssentialDofsFromMatrix(*J_T);
1522 auto& constrained_dofs = bcs_.allEssentialTrueDofs();
1524 mfem::EliminateBC(*J_T, *J_e_, constrained_dofs, reactions_adjoint_bcs_, displacement_adjoint_load_);
1525 for (
int i = 0; i < constrained_dofs.Size(); i++) {
1526 int j = constrained_dofs[i];
1527 displacement_adjoint_load_[j] = reactions_adjoint_bcs_[j];
1530 auto& lin_solver = nonlin_solver_->linearSolver();
1531 lin_solver.SetOperator(*J_T);
1532 lin_solver.Mult(displacement_adjoint_load_, adjoint_displacement_);
1535 nonlin_solver_->setOperator(*residual_with_bcs_);
1548 std::optional<int> component = {})
const
1551 mfem::ParGridFunction nodal_positions(
1552 const_cast<mfem::ParFiniteElementSpace*
>(&displacement_.
space()));
1553 mfemParMesh().GetNodes(nodal_positions);
1555 const int num_nodes = nodal_positions.Size() / dim;
1556 mfem::Array<int> constrained_dofs;
1558 for (
int i = 0; i < num_nodes; i++) {
1561 int idof = mfem::Ordering::Map<smith::ordering>(nodal_positions.FESpace()->GetNDofs(),
1562 nodal_positions.FESpace()->GetVDim(), i, 0);
1563 if (nodal_positions.ParFESpace()->GetLocalTDofNumber(idof) >= 0) {
1564 mfem::Vector node_coords(dim);
1565 mfem::Array<int> node_dofs;
1566 for (
int d = 0; d < dim; d++) {
1568 int local_vector_dof = mfem::Ordering::Map<smith::ordering>(nodal_positions.FESpace()->GetNDofs(),
1569 nodal_positions.FESpace()->GetVDim(), i, d);
1572 node_coords(d) = nodal_positions(local_vector_dof);
1575 bool is_active_component =
true;
1577 if (*component != d) {
1578 is_active_component =
false;
1582 if (is_active_component) {
1584 node_dofs.Append(nodal_positions.ParFESpace()->GetLocalTDofNumber(local_vector_dof));
1589 if (is_node_constrained(node_coords)) {
1590 constrained_dofs.Append(node_dofs);
1594 node_dofs.DeleteAll();
1597 return constrained_dofs;
1638 for (
auto& bc : bcs_.essentials()) {
1640 bc.setDofs(du_, time_ + dt);
1643 auto& constrained_dofs = bcs_.allEssentialTrueDofs();
1644 for (
int i = 0; i < constrained_dofs.Size(); i++) {
1645 int j = constrained_dofs[i];
1646 du_[j] -= displacement_(j);
1649 if (use_warm_start_) {
1651 auto r = (*residual_)(time_ + dt, shapeDisplacement(), displacement_, acceleration_,
1652 *parameters_[parameter_indices].state...);
1655 auto [_, drdu] = (*residual_)(time_, shapeDisplacement(),
differentiate_wrt(displacement_), acceleration_,
1656 *parameters_[parameter_indices].previous_state...);
1658 J_ = assemble(drdu);
1660 J_e_ = bcs_.eliminateAllEssentialDofsFromMatrix(*J_);
1664 mfem::EliminateBC(*J_, *J_e_, constrained_dofs, du_, r);
1665 for (
int i = 0; i < constrained_dofs.Size(); i++) {
1666 int j = constrained_dofs[i];
1670 auto& lin_solver = nonlin_solver_->linearSolver();
1672 lin_solver.SetOperator(*J_);
1674 lin_solver.Mult(r, du_);
1677 displacement_ += du_;
This file contains the interface used for initializing/terminating any hardware accelerator-related f...
#define SMITH_HOST_DEVICE
Macro that evaluates to __host__ __device__ when compiling with nvcc and does nothing on a host compi...
The base interface class for a generic PDE solver.
This file contains the declaration of the boundary condition manager class.
This is the abstract base class for a generic forward solver.
void initializeBasePhysicsStates(int cycle, double time)
Protected, non-virtual method to reset physics states to zero. This does not reset design parameters ...
A set to flag components of a vector field.
Class for encapsulating the dual vector space of a finite element space (i.e. the space of linear for...
Class for encapsulating the critical MFEM components of a primal finite element field.
Class for encapsulating the data associated with a vector derived from a MFEM finite element space....
mfem::ParFiniteElementSpace & space()
Returns a non-owning reference to the internal FESpace.
void setTraction(TractionType traction_function, Domain &domain)
This is an overloaded member function, provided for convenience. It differs from the above function o...
virtual void quasiStaticAdjointSolve(double)
Solve the Quasi-static adjoint linear.
void warmStartDisplacement(double dt)
Sets the Dirichlet BCs for the current time and computes an initial guess for parameters and displace...
const smith::FiniteElementState & acceleration() const
Get the acceleration state.
const FiniteElementState & state(const std::string &state_name) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::shared_ptr< mfem::VectorCoefficient > disp_bdr_coef_
Coefficient containing the essential boundary values.
const smith::FiniteElementState & velocity() const
Get the velocity state.
const FiniteElementState & adjoint(const std::string &state_name) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
mfem::Vector v_
used to communicate the ODE solver's predicted velocity to the residual operator
FiniteElementState velocity_
The velocity finite element state.
FiniteElementDual displacement_adjoint_load_
The adjoint load (RHS) for the displacement adjoint system solve (downstream -dQOI/d displacement)
void setVelocity(const FiniteElementState &temp)
This is an overloaded member function, provided for convenience. It differs from the above function o...
const smith::FiniteElementDual & reactions() const
getter for nodal forces (before zeroing-out essential dofs)
std::unique_ptr< mfem::HypreParMatrix > J_e_
std::pair< const mfem::HypreParMatrix &, const mfem::HypreParMatrix & > stiffnessMatrix() const
Return the assembled stiffness matrix.
FiniteElementDual implicit_sensitivity_displacement_start_of_step_
The total/implicit sensitivity of the qoi with respect to the start of the previous timestep's displa...
void completeSetup() override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::unique_ptr< EquationSolver > nonlin_solver_
the specific methods and tolerances specified to solve the nonlinear residual equations
const FiniteElementDual & dual(const std::string &dual_name) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setPressure(PressureType pressure_function, Domain &domain)
This is an overloaded member function, provided for convenience. It differs from the above function o...
mfem_ext::SecondOrderODE ode2_
the ordinary differential equation that describes how to solve for the second time derivative of disp...
void zeroEssentials(FiniteElementVector &field) const
Set field to zero wherever their are essential boundary conditions applies.
void initializeSolidMechanicsStates()
Non virtual method to reset thermal states to zero. This does not reset design parameters or shape.
qdata_type< T > createQuadratureDataBuffer(T initial_state, const std::optional< Domain > &optional_domain=std::nullopt)
Create a shared ptr to a quadrature data buffer for the given material type.
mfem::Array< int > calculateConstrainedDofs(std::function< bool(const mfem::Vector &)> is_node_constrained, std::optional< int > component={}) const
Calculate a list of constrained dofs in the true displacement vector from a function that returns tru...
void addBodyForce(DependsOn< active_parameters... >, BodyForceType body_force, Domain &domain)
Set the body forcefunction.
FiniteElementState reactions_adjoint_bcs_
sensitivity of qoi with respect to reaction forces
mfem::Vector du_
vector used to store the change in essential bcs between timesteps
const FiniteElementDual & computeTimestepShapeSensitivity() override
This is an overloaded member function, provided for convenience. It differs from the above function o...
const FiniteElementState & dualAdjoint(const std::string &dual_name) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setVelocity(AppliedVelocityFunction applied_velocity)
Set the underlying finite element state to a prescribed velocity field.
virtual void quasiStaticSolve(double dt)
Solve the Quasi-static Newton system.
virtual void setDualAdjointBcs(std::unordered_map< std::string, const smith::FiniteElementState & > bcs) override
Set the dual loads (dirichlet values) for the adjoint reverse timestep solve This must be called afte...
std::unique_ptr< ShapeAwareFunctional< shape_trial, test(trial, trial, parameter_space...)> > residual_
smith::Functional that is used to calculate the residual and its derivatives
double c1_
coefficient used to calculate predicted velocity: dudt_p := dudt + c1 * d2u_dt2
FiniteElementDual acceleration_adjoint_load_
The adjoint load (RHS) for the adjoint system solve (downstream -dQOI/d acceleration)
const std::unordered_map< std::string, const smith::FiniteElementDual & > computeInitialConditionSensitivity() const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setMaterial(DependsOn< active_parameters... >, const MaterialType &material, Domain &domain, qdata_type< StateType > qdata=EmptyQData)
Set the material stress response and mass properties for the physics module.
std::unique_ptr< mfem_ext::StdFunctionOperator > residual_with_bcs_
mfem::Operator that calculates the residual after applying essential boundary conditions
mfem::Vector predicted_displacement_
an intermediate variable used to store the predicted end-step displacement
double time_end_step_
End of step time used in reverse mode so that the time can be decremented on reverse steps.
SolidMechanics(std::unique_ptr< smith::EquationSolver > solver, const smith::TimesteppingOptions timestepping_opts, const std::string &physics_name, std::shared_ptr< smith::Mesh > smith_mesh, std::vector< std::string > parameter_names={}, int cycle=0, double time=0.0, bool checkpoint_to_disk=false, bool use_warm_start=true)
Construct a new SolidMechanics object.
void setFixedBCs(Domain &domain, Components components=Component::ALL)
Shortcut to set selected components of displacements to zero for all time.
FiniteElementState acceleration_
The acceleration finite element state.
std::shared_ptr< QuadratureData< T > > qdata_type
a container holding quadrature point data of the specified type
FiniteElementDual implicit_sensitivity_velocity_start_of_step_
The total/implicit sensitivity of the qoi with respect to the start of the previous timestep's veloci...
void setRateDependentMaterial(const RateDependentMaterialType &material, Domain &domain, std::shared_ptr< QuadratureData< StateType >> qdata=EmptyQData)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setDisplacement(const FiniteElementState &temp)
This is an overloaded member function, provided for convenience. It differs from the above function o...
FiniteElementDual reactions_
nodal reaction forces
std::vector< std::string > stateNames() const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setDisplacement(AppliedDisplacementFunction applied_displacement)
Set the underlying finite element state to a prescribed displacement field.
void setMaterial(const MaterialType &material, Domain &domain, std::shared_ptr< QuadratureData< StateType >> qdata=EmptyQData)
This is an overloaded member function, provided for convenience. It differs from the above function o...
virtual std::unique_ptr< mfem_ext::StdFunctionOperator > buildQuasistaticOperator()
Build the quasi-static operator corresponding to the total Lagrangian formulation.
mfem::Vector u_
used to communicate the ODE solver's predicted displacement to the residual operator
void advanceTimestep(double dt) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setDisplacementBCs(AppliedDisplacementFunction applied_displacement, Domain &domain, Components components=Component::ALL)
Set essential displacement boundary conditions on selected components.
void resetStates(int cycle=0, double time=0.0) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::shared_ptr< mfem::Coefficient > component_disp_bdr_coef_
Coefficient containing the essential boundary values.
FiniteElementState displacement_
The displacement finite element state.
FiniteElementDual velocity_adjoint_load_
The adjoint load (RHS) for the velocity adjoint system solve (downstream -dQOI/d velocity)
void setTraction(DependsOn< active_parameters... >, TractionType traction_function, Domain &domain)
Set the traction boundary condition.
double c0_
coefficient used to calculate predicted displacement: u_p := u + c0 * d2u_dt2
SolidMechanics(const NonlinearSolverOptions nonlinear_opts, const LinearSolverOptions lin_opts, const smith::TimesteppingOptions timestepping_opts, const std::string &physics_name, std::shared_ptr< smith::Mesh > smith_mesh, std::vector< std::string > parameter_names={}, int cycle=0, double time=0.0, bool checkpoint_to_disk=false, bool use_warm_start=true)
Construct a new SolidMechanics object.
virtual ~SolidMechanics()
Destroy the SolidMechanics Functional object.
FiniteElementDual loadCheckpointedDual(const std::string &dual_name, int cycle) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void addCustomBoundaryIntegral(DependsOn< active_parameters... >, callable qfunction, const std::optional< Domain > &optional_domain=std::nullopt)
register a custom boundary integral calculation as part of the residual
std::unique_ptr< mfem::HypreParMatrix > J_
Assembled sparse matrix for the Jacobian df/du (11 block if using Lagrange multiplier contact)
void setDisplacementBCsByDofList(AppliedDisplacementFunction applied_displacement, const mfem::Array< int > true_dofs)
Set the displacement essential boundary conditions on a set of true degrees of freedom.
std::vector< std::string > dualNames() const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
FiniteElementState adjoint_displacement_
The displacement finite element adjoint state.
bool use_warm_start_
A flag denoting whether to compute the warm start for improved robustness.
virtual void setAdjointLoad(std::unordered_map< std::string, const smith::FiniteElementDual & > loads) override
Set the loads for the adjoint reverse timestep solve.
void addBodyForce(BodyForceType body_force, Domain &domain)
This is an overloaded member function, provided for convenience. It differs from the above function o...
FiniteElementDual computeTimestepSensitivity(size_t parameter_field) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::vector< std::string > adjointNames() const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
const smith::FiniteElementState & displacement() const
Get the displacement state.
void reverseAdjointTimestep() override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setState(const std::string &state_name, const FiniteElementState &state) override
Set the primal solution field (displacement, velocity) for the underlying solid mechanics solver.
void addCustomDomainIntegral(DependsOn< active_parameters... >, callable qfunction, Domain &domain, qdata_type< StateType > qdata=NoQData)
register a custom domain integral calculation as part of the residual
void setPressure(DependsOn< active_parameters... >, PressureType pressure_function, Domain &domain)
Apply a pressure-type follower load.
void setRateDependentMaterial(DependsOn< active_parameters... >, const RateDependentMaterialType &material, Domain &domain, qdata_type< StateType > qdata=EmptyQData)
static FiniteElementDual newDual(FunctionSpace space, const std::string &dual_name, const std::string &mesh_tag)
Factory method for creating a new FEDual object.
static std::shared_ptr< QuadratureData< T > > newQuadratureDataBuffer(const std::string &mesh_tag, const Domain &domain, int order, int dim, T initial_state)
Create a shared ptr to a quadrature data buffer for the given material type.
static FiniteElementState newState(FunctionSpace space, const std::string &state_name, const std::string &mesh_tag)
Factory method for creating a new FEState object.
SecondOrderODE is a class wrapping mfem::SecondOrderTimeDependentOperator so that the user can use st...
A file defining some enums and structs that are used by the different physics modules.
many of the functions in this file amount to extracting element indices from an mesh_t like
This file contains the declaration of an equation solver wrapper.
This contains a class that represents the dual of a finite element vector space, i....
This file contains the declaration of structure that manages the MFEM objects that make up the state ...
This file contains the declaration of structure that manages vectors derived from an MFEM finite elem...
Implementation of the quadrature-function-based functional enabling rapid development of FEM formulat...
Smith mesh class which assists in constructing the appropriate parallel mfem meshes and registering a...
const TimesteppingOptions default_quasistatic_options
default quasistatic timestepping options for solid mechanics
const TimesteppingOptions default_timestepping_options
default implicit dynamic timestepping options for solid mechanics
const LinearSolverOptions default_linear_options
default method and tolerances for solving the systems of linear equations that show up in implicit so...
const NonlinearSolverOptions default_nonlinear_options
default iteration limits, tolerances and verbosity for solving the systems of nonlinear equations tha...
const LinearSolverOptions direct_linear_options
the default direct solver option for solving the linear stiffness equations
Accelerator functionality.
tuple(T...) -> tuple< T... >
Class template argument deduction rule for tuples.
std::shared_ptr< QuadratureData< Nothing > > NoQData
a single instance of a QuadratureData container of Nothings, since they are all interchangeable
constexpr SMITH_HOST_DEVICE auto norm(const isotropic_tensor< T, m, m > &I)
compute the Frobenius norm (sqrt(tr(dot(transpose(I), I)))) of an isotropic tensor
Domain EntireBoundary(const mesh_t &mesh)
constructs a domain from all the boundary elements in a mesh
std::shared_ptr< QuadratureData< Empty > > EmptyQData
a single instance of a QuadratureData container of Emptys, since they are all interchangeable
auto differentiate_wrt(const mfem::Vector &v)
this function is intended to only be used in combination with smith::Functional::operator(),...
Domain EntireDomain(const mesh_t &mesh)
constructs a domain from all the elements in a mesh
auto cross(const tensor< T, 3, 2 > &A)
compute the cross product of the columns of A: A(:,1) x A(:,2)
SMITH_HOST_DEVICE auto normalize(const tensor< T, n... > &A)
Normalizes the tensor Each element is divided by the Frobenius norm of the tensor,...
Wrapper classes for using MFEM's ODE solver objects.
Various helper functions and macros for profiling using Caliper.
#define SMITH_MARK_FUNCTION
This file contains the declaration of the structures that manage quadrature point data.
Wrapper of smith::Functional for evaluating integrals and derivatives of quantities with shape displa...
The material and load types for the solid functional physics module.
This file contains enumerations and record types for physics solver configuration.
This file contains the declaration of the StateManager class.
Classes for defining an mfem::Operator with std::functions.
Compile-time alias for a dimension.
a class for representing a geometric region that can be used for integration
mfem::Array< int > dof_list(const fes_t *fes) const
get mfem degree of freedom list for a given FiniteElementSpace
see Nothing for a complete description of this class and when to use it
Parameters for an iterative linear solution scheme.
LinearSolver linear_solver
Linear solver selection.
Nonlinear solution scheme parameters.
NonlinearSolver nonlin_solver
Nonlinear solver selection.
these classes are a little confusing. These two special types represent the similar (but different) c...
a struct that is used in the physics modules to clarify which template arguments are user-controlled ...
A class for storing and access user-defined types at quadrature points.
auto SMITH_HOST_DEVICE operator()(T t, Position position, Displacement, Acceleration, Params... params) const
Body force call.
BodyForceIntegrand(BodyForceType body_force)
Constructor for the functor.
BodyForceType body_force_
Body force model.
auto SMITH_HOST_DEVICE operator()(double, X, State &state, Displacement displacement, Acceleration acceleration, Params... params) const
Material stress response call.
const double * time_increment_
Current time step.
Material material_
Material model.
RateDependentMaterialStressFunctor(Material material, const double *dt)
Constructor for the functor.
auto SMITH_HOST_DEVICE operator()(double, X, State &state, Displacement displacement, Acceleration acceleration, Params... params) const
Material stress response call.
MaterialStressFunctor(Material material)
Constructor for the functor.
Material material_
Material model.
A timestep and boundary condition enforcement method for a dynamic solver.
TimestepMethod timestepper
The timestepping method to be applied.
DirichletEnforcementMethod enforcement_method
The essential boundary enforcement method to use.
This is a class that mimics most of std::tuple's interface, except that it is usable in CUDA kernels ...
A sentinel struct for eliding no-op tensor operations.
Implementation of the tensor class used by Functional.
Implements a std::tuple-like object that works in CUDA kernels.