Smith  0.1
Smith is an implicit thermal structural mechanics simulation code.
solid_mechanics_with_internal_vars_system.hpp
Go to the documentation of this file.
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Smith Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
12 #pragma once
13 
14 #include "smith/differentiable_numerics/field_store.hpp"
18 #include "smith/differentiable_numerics/multiphysics_time_integrator.hpp"
26 
27 namespace smith {
28 
43 template <int dim, int disp_order, typename StateSpace,
44  typename DisplacementTimeRule = QuasiStaticSecondOrderTimeIntegrationRule,
45  typename InternalVarTimeRule = BackwardEulerFirstOrderTimeIntegrationRule, typename... parameter_space>
47  static_assert(DisplacementTimeRule::num_states == 4,
48  "SolidMechanicsWithInternalVarsSystem requires a 4-state displacement rule");
49  static_assert(InternalVarTimeRule::num_states == 2,
50  "SolidMechanicsWithInternalVarsSystem requires a 2-state internal variable rule");
51 
52  // Primary weak form: residual for displacement (u).
53  // Inputs: u, u_old, v_old, a_old, alpha, alpha_old, params...
58  H1<disp_order, dim>, StateSpace, StateSpace, parameter_space...>>;
59 
60  // State weak form: residual for internal variable (alpha).
61  // Inputs: alpha, alpha_old, u, u_old, v_old, a_old, params...
64  TimeDiscretizedWeakForm<dim, StateSpace,
66  H1<disp_order, dim>, H1<disp_order, dim>, parameter_space...>>;
67 
68  // Cycle-zero weak form: test field = acceleration, inputs: u, v, a, alpha, params...
73 
74  std::shared_ptr<SolidWeakFormType> solid_weak_form;
75  std::shared_ptr<StateWeakFormType> state_weak_form;
76  std::shared_ptr<CycleZeroSolidWeakFormType> cycle_zero_solid_weak_form;
77  std::shared_ptr<DirichletBoundaryConditions> disp_bc;
78  std::shared_ptr<DirichletBoundaryConditions> state_bc;
79  std::shared_ptr<DisplacementTimeRule> disp_time_rule;
80  std::shared_ptr<InternalVarTimeRule> state_time_rule;
81 
86  std::vector<FieldState> getStateFields() const
87  {
88  return {field_store->getField(prefix("displacement_solve_state")),
89  field_store->getField(prefix("displacement")),
90  field_store->getField(prefix("velocity")),
91  field_store->getField(prefix("acceleration")),
92  field_store->getField(prefix("state_solve_state")),
93  field_store->getField(prefix("state"))};
94  }
95 
100  std::vector<FieldState> getOutputFieldStates() const
101  {
102  return {field_store->getField(prefix("displacement")), field_store->getField(prefix("velocity")),
103  field_store->getField(prefix("acceleration")), field_store->getField(prefix("state"))};
104  }
105 
110  std::vector<ReactionInfo> getReactionInfos() const
111  {
112  return {{prefix("solid_residual"), &field_store->getField(prefix("displacement")).get()->space()},
113  {prefix("state_residual"), &field_store->getField(prefix("state")).get()->space()}};
114  }
115 
121  std::unique_ptr<DifferentiablePhysics> createDifferentiablePhysics(std::string physics_name)
122  {
123  return std::make_unique<DifferentiablePhysics>(field_store->getMesh(), field_store->graph(),
124  field_store->getShapeDisp(), getStateFields(), getParameterFields(),
125  advancer, physics_name, getReactionInfos());
126  }
127 
138  template <typename MaterialType>
139  void setMaterial(const MaterialType& material, const std::string& domain_name)
140  {
141  auto captured_disp_rule = disp_time_rule;
142  auto captured_state_rule = state_time_rule;
143 
144  solid_weak_form->addBodyIntegral(domain_name, [=](auto t_info, auto /*X*/, auto u, auto u_old, auto v_old,
145  auto a_old, auto alpha, auto alpha_old, auto... params) {
146  auto [u_current, v_current, a_current] = captured_disp_rule->interpolate(t_info, u, u_old, v_old, a_old);
147  auto alpha_current = captured_state_rule->value(t_info, alpha, alpha_old);
148 
149  typename MaterialType::State state;
150  auto pk_stress = material(state, get<DERIVATIVE>(u_current), get<VALUE>(alpha_current), params...);
151 
152  return smith::tuple{get<VALUE>(a_current) * material.density, pk_stress};
153  });
154 
155  // Cycle-zero: u and v are given, solve for a; alpha at initial condition
156  cycle_zero_solid_weak_form->addBodyIntegral(
157  domain_name, [=](auto /*t_info*/, auto /*X*/, auto u, auto /*v*/, auto a, auto alpha, auto... params) {
158  auto alpha_current = alpha; // at cycle 0, use initial alpha
159  typename MaterialType::State state;
160  auto pk_stress = material(state, get<DERIVATIVE>(u), get<VALUE>(alpha_current), params...);
161 
162  return smith::tuple{get<VALUE>(a) * material.density, pk_stress};
163  });
164  }
165 
172  template <int... active_parameters, typename BodyForceType>
173  void addBodyForce(DependsOn<active_parameters...> depends_on, const std::string& domain_name,
174  BodyForceType force_function)
175  {
176  auto captured_disp_rule = disp_time_rule;
177  auto captured_state_rule = state_time_rule;
178  solid_weak_form->addBodySource(
179  depends_on, domain_name,
180  [=](auto t_info, auto X, auto u, auto u_old, auto v_old, auto a_old, auto alpha, auto alpha_old,
181  auto... params) {
182  auto [u_current, v_current, a_current] = captured_disp_rule->interpolate(t_info, u, u_old, v_old, a_old);
183  auto [alpha_current, alpha_dot] = captured_state_rule->interpolate(t_info, alpha, alpha_old);
184  return force_function(t_info.time(), X, u_current, v_current, a_current, alpha_current, alpha_dot, params...);
185  });
186 
187  addCycleZeroBodySourceImpl(
188  domain_name,
189  [=](auto t_info, auto X, auto u, auto v, auto a, auto alpha, auto... params) {
190  auto alpha_dot = 0.0 * alpha;
191  return force_function(t_info.time(), X, u, v, a, alpha, alpha_dot, params...);
192  },
193  std::make_index_sequence<4 + sizeof...(parameter_space)>{});
194  }
195 
201  template <typename BodyForceType>
202  void addBodyForce(const std::string& domain_name, BodyForceType force_function)
203  {
204  addBodyForceAllParams(domain_name, force_function, std::make_index_sequence<6 + sizeof...(parameter_space)>{});
205  }
206 
213  template <int... active_parameters, typename TractionType>
214  void addTraction(DependsOn<active_parameters...> depends_on, const std::string& domain_name,
215  TractionType traction_function)
216  {
217  auto captured_disp_rule = disp_time_rule;
218  auto captured_state_rule = state_time_rule;
219  solid_weak_form->addBoundaryFlux(
220  depends_on, domain_name,
221  [=](auto t_info, auto X, auto n, auto u, auto u_old, auto v_old, auto a_old, auto alpha, auto alpha_old,
222  auto... params) {
223  auto [u_current, v_current, a_current] = captured_disp_rule->interpolate(t_info, u, u_old, v_old, a_old);
224  auto [alpha_current, alpha_dot] = captured_state_rule->interpolate(t_info, alpha, alpha_old);
225  return traction_function(t_info.time(), X, n, u_current, v_current, a_current, alpha_current, alpha_dot,
226  params...);
227  });
228 
229  addCycleZeroBoundaryFluxImpl(
230  domain_name,
231  [=](auto t_info, auto X, auto n, auto u, auto v, auto a, auto alpha, auto... params) {
232  auto alpha_dot = 0.0 * alpha;
233  return traction_function(t_info.time(), X, n, u, v, a, alpha, alpha_dot, params...);
234  },
235  std::make_index_sequence<4 + sizeof...(parameter_space)>{});
236  }
237 
243  template <typename TractionType>
244  void addTraction(const std::string& domain_name, TractionType traction_function)
245  {
246  addTractionAllParams(domain_name, traction_function, std::make_index_sequence<6 + sizeof...(parameter_space)>{});
247  }
248 
255  template <int... active_parameters, typename PressureType>
256  void addPressure(DependsOn<active_parameters...> depends_on, const std::string& domain_name,
257  PressureType pressure_function)
258  {
259  auto captured_disp_rule = disp_time_rule;
260  auto captured_state_rule = state_time_rule;
261  solid_weak_form->addBoundaryIntegral(
262  depends_on, domain_name,
263  [=](auto t_info, auto X, auto u, auto u_old, auto v_old, auto a_old, auto alpha, auto alpha_old,
264  auto... params) {
265  auto [u_current, v_current, a_current] = captured_disp_rule->interpolate(t_info, u, u_old, v_old, a_old);
266  auto [alpha_current, alpha_dot] = captured_state_rule->interpolate(t_info, alpha, alpha_old);
267 
268  auto x_current = X + u_current;
269  auto n_deformed = cross(get<DERIVATIVE>(x_current));
270  auto n_shape_norm = norm(cross(get<DERIVATIVE>(X)));
271 
272  auto pressure = pressure_function(t_info.time(), get<VALUE>(X), u_current, v_current, a_current,
273  alpha_current, alpha_dot, get<VALUE>(params)...);
274 
275  return pressure * n_deformed * (1.0 / n_shape_norm);
276  });
277 
278  addCycleZeroBoundaryIntegralImpl(
279  domain_name,
280  [=](auto t_info, auto X, auto u, auto v, auto a, auto alpha, auto... params) {
281  auto alpha_val = get<VALUE>(alpha);
282  auto alpha_dot = 0.0 * alpha_val;
283 
284  auto x_current = X + u;
285  auto n_deformed = cross(get<DERIVATIVE>(x_current));
286  auto n_shape_norm = norm(cross(get<DERIVATIVE>(X)));
287 
288  auto pressure = pressure_function(t_info.time(), get<VALUE>(X), get<VALUE>(u), get<VALUE>(v), get<VALUE>(a),
289  alpha_val, alpha_dot, get<VALUE>(params)...);
290 
291  return pressure * n_deformed * (1.0 / n_shape_norm);
292  },
293  std::make_index_sequence<4 + sizeof...(parameter_space)>{});
294  }
295 
301  template <typename PressureType>
302  void addPressure(const std::string& domain_name, PressureType pressure_function)
303  {
304  addPressureAllParams(domain_name, pressure_function, std::make_index_sequence<6 + sizeof...(parameter_space)>{});
305  }
306 
313  template <typename EvolutionType>
314  void addStateEvolution(const std::string& domain_name, EvolutionType evolution_law)
315  {
316  auto captured_disp_rule = disp_time_rule;
317  auto captured_state_rule = state_time_rule;
318 
319  state_weak_form->addBodyIntegral(domain_name, [=](auto t_info, auto /*X*/, auto alpha, auto alpha_old, auto u,
320  auto u_old, auto v_old, auto a_old, auto... params) {
321  auto [u_current, v_current, a_current] = captured_disp_rule->interpolate(t_info, u, u_old, v_old, a_old);
322  auto [alpha_current, alpha_dot] = captured_state_rule->interpolate(t_info, alpha, alpha_old);
323 
324  auto residual_val = evolution_law(t_info, get<VALUE>(alpha_current), get<VALUE>(alpha_dot),
325  get<DERIVATIVE>(u_current), params...);
326 
327  tensor<double, dim> flux{};
328  return smith::tuple{residual_val, flux};
329  });
330  }
331 
332  private:
333  template <typename BodyForceType, std::size_t... Is>
334  void addBodyForceAllParams(const std::string& domain_name, BodyForceType force_function, std::index_sequence<Is...>)
335  {
336  addBodyForce(DependsOn<static_cast<int>(Is)...>{}, domain_name, force_function);
337  }
338 
339  template <typename TractionType, std::size_t... Is>
340  void addTractionAllParams(const std::string& domain_name, TractionType traction_function, std::index_sequence<Is...>)
341  {
342  addTraction(DependsOn<static_cast<int>(Is)...>{}, domain_name, traction_function);
343  }
344 
345  template <typename PressureType, std::size_t... Is>
346  void addPressureAllParams(const std::string& domain_name, PressureType pressure_function, std::index_sequence<Is...>)
347  {
348  addPressure(DependsOn<static_cast<int>(Is)...>{}, domain_name, pressure_function);
349  }
350 
351  // Cycle-zero helpers: use all-params DependsOn with the 5-state cycle-zero form (u, v, a, alpha, alpha_old)
352  template <typename IntegrandType, std::size_t... Is>
353  void addCycleZeroBodySourceImpl(const std::string& name, IntegrandType f, std::index_sequence<Is...>)
354  {
355  cycle_zero_solid_weak_form->addBodySource(DependsOn<static_cast<int>(Is)...>{}, name, f);
356  }
357 
358  template <typename IntegrandType, std::size_t... Is>
359  void addCycleZeroBoundaryFluxImpl(const std::string& name, IntegrandType f, std::index_sequence<Is...>)
360  {
361  cycle_zero_solid_weak_form->addBoundaryFlux(DependsOn<static_cast<int>(Is)...>{}, name, f);
362  }
363 
364  template <typename IntegrandType, std::size_t... Is>
365  void addCycleZeroBoundaryIntegralImpl(const std::string& name, IntegrandType f, std::index_sequence<Is...>)
366  {
367  cycle_zero_solid_weak_form->addBoundaryIntegral(DependsOn<static_cast<int>(Is)...>{}, name, f);
368  }
369 };
370 
382 template <int dim, int disp_order, typename StateSpace, typename DisplacementTimeRule, typename InternalVarTimeRule,
383  typename... parameter_space>
384 SolidMechanicsWithInternalVarsSystem<dim, disp_order, StateSpace, DisplacementTimeRule, InternalVarTimeRule,
385  parameter_space...>
386 buildSolidMechanicsWithInternalVarsSystem(std::shared_ptr<Mesh> mesh, std::shared_ptr<CoupledSystemSolver> solver,
387  DisplacementTimeRule disp_rule, InternalVarTimeRule state_rule,
388  std::string prepend_name = "",
389  std::shared_ptr<CoupledSystemSolver> cycle_zero_solver = nullptr,
390  FieldType<parameter_space>... parameter_types)
391 {
392  auto field_store = std::make_shared<FieldStore>(mesh, 100);
393 
394  auto prefix = [&](const std::string& name) {
395  if (prepend_name.empty()) {
396  return name;
397  }
398  return prepend_name + "_" + name;
399  };
400 
401  // Add shape displacement
402  FieldType<H1<1, dim>> shape_disp_type(prefix("shape_displacement"));
403  field_store->addShapeDisp(shape_disp_type);
404 
405  // 1. Displacement fields (4-state second-order)
406  auto disp_time_rule_ptr = std::make_shared<DisplacementTimeRule>(disp_rule);
407  FieldType<H1<disp_order, dim>> disp_type(prefix("displacement_solve_state"));
408  auto disp_bc = field_store->addIndependent(disp_type, disp_time_rule_ptr);
409  auto disp_old_type = field_store->addDependent(disp_type, FieldStore::TimeDerivative::VAL, prefix("displacement"));
410  auto velo_old_type = field_store->addDependent(disp_type, FieldStore::TimeDerivative::DOT, prefix("velocity"));
411  auto accel_old_type = field_store->addDependent(disp_type, FieldStore::TimeDerivative::DDOT, prefix("acceleration"));
412 
413  // 2. Internal variable fields (2-state first-order)
414  auto state_time_rule_ptr = std::make_shared<InternalVarTimeRule>(state_rule);
415  FieldType<StateSpace> state_type(prefix("state_solve_state"));
416  auto state_bc = field_store->addIndependent(state_type, state_time_rule_ptr);
417  auto state_old_type = field_store->addDependent(state_type, FieldStore::TimeDerivative::VAL, prefix("state"));
418 
419  // 3. Parameters
420  std::vector<FieldState> parameter_fields;
421  (field_store->addParameter(FieldType<parameter_space>(prefix("param_" + parameter_types.name))), ...);
422  (parameter_fields.push_back(field_store->getField(prefix("param_" + parameter_types.name))), ...);
423 
424  using SystemType = SolidMechanicsWithInternalVarsSystem<dim, disp_order, StateSpace, DisplacementTimeRule,
425  InternalVarTimeRule, parameter_space...>;
426 
427  // 4. Solid weak form: residual for u (inputs: u, u_old, v_old, a_old, alpha, alpha_old, params...)
428  std::string solid_res_name = prefix("solid_residual");
429  auto solid_weak_form = std::make_shared<typename SystemType::SolidWeakFormType>(
430  solid_res_name, field_store->getMesh(), field_store->getField(disp_type.name).get()->space(),
431  field_store->createSpaces(solid_res_name, disp_type.name, disp_type, disp_old_type, velo_old_type, accel_old_type,
432  state_type, state_old_type,
433  FieldType<parameter_space>(prefix("param_" + parameter_types.name))...));
434 
435  // 5. State weak form: residual for alpha (inputs: alpha, alpha_old, u, u_old, v_old, a_old, params...)
436  std::string state_res_name = prefix("state_residual");
437  auto state_weak_form = std::make_shared<typename SystemType::StateWeakFormType>(
438  state_res_name, field_store->getMesh(), field_store->getField(state_type.name).get()->space(),
439  field_store->createSpaces(state_res_name, state_type.name, state_type, state_old_type, disp_type, disp_old_type,
440  velo_old_type, accel_old_type,
441  FieldType<parameter_space>(prefix("param_" + parameter_types.name))...));
442 
443  // 6. Cycle-zero weak form: solve for acceleration (inputs: u, v, a, alpha, params...)
444  std::string cycle_zero_name = prefix("solid_reaction");
445  auto cycle_zero_solid_weak_form = std::make_shared<typename SystemType::CycleZeroSolidWeakFormType>(
446  cycle_zero_name, field_store->getMesh(), field_store->getField(accel_old_type.name).get()->space(),
447  field_store->createSpaces(cycle_zero_name, accel_old_type.name, disp_type, velo_old_type, accel_old_type,
448  state_type, FieldType<parameter_space>(prefix("param_" + parameter_types.name))...));
449 
450  if (cycle_zero_solver == nullptr) {
451  cycle_zero_solver = solver->singleBlockSolver(0);
452  }
453  SLIC_ERROR_IF(cycle_zero_solver == nullptr,
454  "Could not derive a cycle-zero solver for block 0 from the provided internal-vars solid mechanics "
455  "solver.");
456 
457  // Solver and Advancer
458  std::vector<std::shared_ptr<WeakForm>> weak_forms{solid_weak_form, state_weak_form};
459  auto advancer = std::make_shared<MultiphysicsTimeIntegrator>(field_store, weak_forms, solver,
460  cycle_zero_solid_weak_form, cycle_zero_solver);
461 
462  return SystemType{{field_store, solver, advancer, parameter_fields, prepend_name},
463  solid_weak_form,
464  state_weak_form,
465  cycle_zero_solid_weak_form,
466  disp_bc,
467  state_bc,
468  disp_time_rule_ptr,
469  state_time_rule_ptr};
470 }
471 
475 template <int dim, int disp_order, typename StateSpace, typename DisplacementTimeRule, typename InternalVarTimeRule,
476  typename... parameter_space>
477 auto buildSolidMechanicsWithInternalVarsSystem(std::shared_ptr<Mesh> mesh, std::shared_ptr<CoupledSystemSolver> solver,
478  DisplacementTimeRule disp_rule, InternalVarTimeRule state_rule,
479  std::shared_ptr<CoupledSystemSolver> cycle_zero_solver = nullptr,
480  FieldType<parameter_space>... parameter_types)
481 {
482  return buildSolidMechanicsWithInternalVarsSystem<dim, disp_order, StateSpace>(mesh, solver, disp_rule, state_rule, "",
483  cycle_zero_solver, parameter_types...);
484 }
485 
486 } // namespace smith
Defines a BasePhysics implementation backed by FieldState objects and a gretl computational graph.
Contains DirichletBoundaryConditions class for interaction with the differentiable solve interfaces.
Accelerator functionality.
Definition: smith.cpp:36
SMITH_HOST_DEVICE auto cross(const tensor< T, 3, 2 > &A)
compute the cross product of the columns of A: A(:,1) x A(:,2)
Definition: tensor.hpp:966
SolidMechanicsWithInternalVarsSystem< dim, disp_order, StateSpace, DisplacementTimeRule, InternalVarTimeRule, parameter_space... > buildSolidMechanicsWithInternalVarsSystem(std::shared_ptr< Mesh > mesh, std::shared_ptr< CoupledSystemSolver > solver, DisplacementTimeRule disp_rule, InternalVarTimeRule state_rule, std::string prepend_name="", std::shared_ptr< CoupledSystemSolver > cycle_zero_solver=nullptr, FieldType< parameter_space >... parameter_types)
Factory function to build a solid mechanics system with internal variable.
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
mfem::future::tuple< T... > tuple
Expose MFEM tuple in the Smith namespace.
Definition: tuple.hpp:241
This file contains nonlinear block solver interfaces and helpers.
Interface and implementations for advancing from one step to the next. Typically these are time integ...
@ DOT
The first time derivative.
@ DDOT
The second time derivative.
Representation of a field type with a name and an optional unknown index.
Definition: field_store.hpp:30
std::string name
Name of the field.
Definition: field_store.hpp:37
H1 elements of order p.
a struct that is used in the physics modules to clarify which template arguments are user-controlled ...
Definition: common.hpp:45
System struct for solid mechanics with an additional internal variable (L2 state).
void addTraction(DependsOn< active_parameters... > depends_on, const std::string &domain_name, TractionType traction_function)
Add a surface traction to the solid mechanics part (with DependsOn).
std::vector< FieldState > getStateFields() const
Get the list of all state fields (disp_pred, disp, vel, accel, state_pred, state).
void setMaterial(const MaterialType &material, const std::string &domain_name)
Set the material model for the solid mechanics part.
std::shared_ptr< CycleZeroSolidWeakFormType > cycle_zero_solid_weak_form
Cycle-zero weak form.
std::shared_ptr< DirichletBoundaryConditions > disp_bc
Displacement boundary conditions.
std::shared_ptr< SolidWeakFormType > solid_weak_form
Solid mechanics weak form.
void addTraction(const std::string &domain_name, TractionType traction_function)
Add a surface traction that depends on all state and parameter fields.
std::vector< ReactionInfo > getReactionInfos() const
Get information about reaction fields for this system.
std::vector< FieldState > getOutputFieldStates() const
Get the list of physical, non-solve state fields.
std::shared_ptr< DirichletBoundaryConditions > state_bc
Internal variable boundary conditions.
std::shared_ptr< StateWeakFormType > state_weak_form
Internal variable weak form.
std::unique_ptr< DifferentiablePhysics > createDifferentiablePhysics(std::string physics_name)
Create a DifferentiablePhysics object for this system.
std::shared_ptr< InternalVarTimeRule > state_time_rule
Time integration for internal variable.
std::shared_ptr< DisplacementTimeRule > disp_time_rule
Time integration for displacement.
void addBodyForce(const std::string &domain_name, BodyForceType force_function)
Add a body force that depends on all state and parameter fields.
void addBodyForce(DependsOn< active_parameters... > depends_on, const std::string &domain_name, BodyForceType force_function)
Add a body force to the solid mechanics part (with DependsOn).
void addStateEvolution(const std::string &domain_name, EvolutionType evolution_law)
Add the evolution law for the internal variable.
void addPressure(DependsOn< active_parameters... > depends_on, const std::string &domain_name, PressureType pressure_function)
Add a pressure boundary condition (follower force) (with DependsOn).
void addPressure(const std::string &domain_name, PressureType pressure_function)
Add a pressure boundary condition that depends on all state and parameter fields.
Base struct for physics systems containing common members and helper functions.
Definition: system_base.hpp:60
std::string prefix(const std::string &name) const
Helper function to prepend the physics name to a string.
Definition: system_base.hpp:78
std::shared_ptr< StateAdvancer > advancer
The state advancer.
Definition: system_base.hpp:63
std::shared_ptr< FieldStore > field_store
Field store managing the system's fields.
Definition: system_base.hpp:61
const std::vector< FieldState > & getParameterFields() const
Get the list of all parameter fields.
Definition: system_base.hpp:71
Defines the SystemBase struct for common system functionality.
Implementation of the tensor class used by Functional.
Wraps FunctionalWeakForm to provide TimeInfo (time, dt, cycle) to integrands instead of just time.
Provides templated implementations for discretizing values, velocities and accelerations from current...
Smith tuple compatibility layer over mfem::future::tuple.
Specifies interface for evaluating weak form residuals and their gradients.