24 template <
int spatial_dim,
typename OutputSpace,
typename inputs = Parameters<>,
25 typename input_indices = std::make_
integer_sequence<
int, inputs::n>>
35 template <
int spatial_dim,
typename OutputSpace,
typename... InputSpaces,
int... input_indices>
37 std::integer_sequence<int, input_indices...>> :
public WeakForm {
39 using SpacesT = std::vector<const mfem::ParFiniteElementSpace*>;
52 const mfem::ParFiniteElementSpace& output_mfem_space,
const SpacesT& input_mfem_spaces)
53 :
WeakForm(physics_name), mesh_(mesh)
55 std::array<
const mfem::ParFiniteElementSpace*,
sizeof...(InputSpaces)> trial_spaces;
56 std::array<
const mfem::ParFiniteElementSpace*,
sizeof...(InputSpaces) + 1> vector_residual_trial_spaces{
60 sizeof...(InputSpaces) != input_mfem_spaces.size(),
61 std::format(
"{} parameter spaces given in the template argument but {} input mfem spaces were supplied.",
62 sizeof...(InputSpaces), input_mfem_spaces.size()));
64 if constexpr (
sizeof...(InputSpaces) > 0) {
65 for_constexpr<
sizeof...(InputSpaces)>([&](
auto i) { trial_spaces[i] = input_mfem_spaces[i]; });
67 [&](
auto i) { vector_residual_trial_spaces[i + 1] = input_mfem_spaces[i]; });
70 const auto& shape_disp_space = mesh->shapeDisplacementSpace();
72 weak_form_ = std::make_unique<ShapeAwareFunctional<
ShapeDispSpace, OutputSpace(InputSpaces...)>>(
73 &shape_disp_space, &output_mfem_space, trial_spaces);
75 v_dot_weak_form_residual_ =
76 std::make_unique<ShapeAwareFunctional<
ShapeDispSpace, double(OutputSpace, InputSpaces...)>>(
77 &shape_disp_space, vector_residual_trial_spaces);
103 template <
int... active_parameters,
typename BodyIntegralType>
107 mesh_->domain(body_name));
109 v_dot_weak_form_residual_->AddDomainIntegral(
111 [integrand](
double time,
auto X,
auto V,
auto... inputs) {
112 auto orig_tuple = integrand(time, X, inputs...);
113 return smith::inner(get<VALUE>(V), get<VALUE>(orig_tuple)) +
114 smith::inner(get<DERIVATIVE>(V), get<DERIVATIVE>(orig_tuple));
116 mesh_->domain(body_name));
120 template <
typename BodyForceType>
123 addBodyIntegral(
DependsOn<>{}, body_name, body_integral);
146 template <
int... active_parameters,
typename BodyLoadType>
149 addBodyIntegral(depends_on, body_name, [load_function](
double t,
auto X,
auto... inputs) {
155 template <
int... active_parameters,
typename BodyLoadType>
158 return addBodySource(
DependsOn<>{}, body_name, load_function);
184 template <
int... active_parameters,
typename BoundaryIntegrandType>
188 mesh_->domain(boundary_name));
190 v_dot_weak_form_residual_->AddBoundaryIntegral(
192 [integrand](
double t,
auto X,
auto V,
auto... params) {
193 auto orig_surface_flux = integrand(t, X, params...);
196 mesh_->domain(boundary_name));
200 template <
typename BoundaryIntegrandType>
203 addBoundaryIntegral(
DependsOn<>{}, boundary_name, integrand);
224 template <
int... active_parameters,
typename InteriorIntegrandType>
226 InteriorIntegrandType integrand)
229 mesh_->domain(interior_name));
231 v_dot_weak_form_residual_->AddInteriorFaceIntegral(
233 [integrand](
double t,
auto X,
auto V,
auto... params) {
235 auto orig_surface_flux = integrand(t, X, params...);
236 auto [flux_pos, flux_neg] = orig_surface_flux;
239 mesh_->domain(interior_name));
243 template <
typename InteriorIntegrandType>
246 addInteriorBoundaryIntegral(
DependsOn<>{}, interior_name, integrand);
270 template <
int... active_parameters,
typename BoundaryFluxType>
272 BoundaryFluxType flux_function)
274 addBoundaryIntegral(depends_on, boundary_name, [flux_function](
double t,
auto X,
auto... inputs) {
275 auto n =
cross(get<DERIVATIVE>(X));
276 return -flux_function(t, get<VALUE>(X),
normalize(n), get<VALUE>(inputs)...);
281 template <
typename BoundaryFluxType>
284 addBoundaryFlux(
DependsOn<>{}, boundary_name, integrand);
289 [[maybe_unused]]
const std::vector<ConstQuadratureFieldPtr>& quad_fields = {})
const override
291 dt_ = time_info.
dt();
292 cycle_ = time_info.
cycle();
293 auto ret = (*weak_form_)(time_info.
time(), *shape_disp, *fields[input_indices]...);
300 const std::vector<double>& jacobian_weights,
301 [[maybe_unused]]
const std::vector<ConstQuadratureFieldPtr>& quad_fields = {})
const override
303 dt_ = time_info.
dt();
304 cycle_ = time_info.
cycle();
306 std::unique_ptr<mfem::HypreParMatrix> J;
308 auto addToJ = [&J](
double factor, std::unique_ptr<mfem::HypreParMatrix> jac_contrib) {
310 SLIC_ERROR_IF(J->N() != jac_contrib->N(),
311 "Multiple nonzero jacobian weights are being used on inconsistently sized input arguments.");
312 SLIC_ERROR_IF(J->M() != jac_contrib->M(),
313 "Multiple nonzero jacobian weights are being used on inconsistently sized input arguments.");
314 J->Add(factor, *jac_contrib);
316 J.reset(jac_contrib.release());
317 if (factor != 1.0) (*J) *= factor;
321 auto jacs = jacobianFunctions(std::make_integer_sequence<
int,
sizeof...(input_indices)>{}, time_info.
time(),
324 for (
size_t input_col = 0; input_col < jacobian_weights.size(); ++input_col) {
325 if (jacobian_weights[input_col] != 0.0) {
326 auto K = smith::get<DERIVATIVE>(jacs[input_col](time_info.
time(), shape_disp, fields));
327 addToJ(jacobian_weights[input_col], assemble(K));
336 [[maybe_unused]]
const std::vector<ConstQuadratureFieldPtr>& quad_fields,
337 [[maybe_unused]]
ConstFieldPtr v_shape_disp,
const std::vector<ConstFieldPtr>& v_fields,
338 [[maybe_unused]]
const std::vector<ConstQuadratureFieldPtr>& v_quad_fields,
341 SLIC_ERROR_IF(v_fields.size() != fields.size(),
342 "Invalid number of field sensitivities relative to the number of fields");
344 dt_ = time_info.
dt();
345 cycle_ = time_info.
cycle();
347 auto jacs = jacobianFunctions(std::make_integer_sequence<
int,
sizeof...(input_indices)>{}, time_info.
time(),
352 for (
size_t input_col = 0; input_col < fields.size(); ++input_col) {
353 if (v_fields[input_col] !=
nullptr) {
354 auto K = smith::get<DERIVATIVE>(jacs[input_col](time_info.
time(), shape_disp, fields));
355 K.AddMult(*v_fields[input_col], *jvp_reaction);
362 [[maybe_unused]]
const std::vector<ConstQuadratureFieldPtr>& quad_fields,
ConstFieldPtr v_field,
363 DualFieldPtr vjp_shape_disp_sensitivity,
const std::vector<DualFieldPtr>& vjp_sensitivities,
364 [[maybe_unused]]
const std::vector<QuadratureFieldPtr>& vjp_quad_field_sensitivities)
const override
366 SLIC_ERROR_IF(vjp_sensitivities.size() != fields.size(),
367 "Invalid number of field sensitivities relative to the number of fields");
369 dt_ = time_info.
dt();
370 cycle_ = time_info.
cycle();
371 auto vecJacs = vectorJacobianFunctions(std::make_integer_sequence<
int,
sizeof...(input_indices)>{},
372 time_info.
time(), shape_disp, v_field, fields);
374 auto shape_vjp = smith::get<DERIVATIVE>((*v_dot_weak_form_residual_)(
376 auto shape_vjp_vector = assemble(shape_vjp);
377 *vjp_shape_disp_sensitivity += *shape_vjp_vector;
380 for (
size_t input_col = 0; input_col < fields.size(); ++input_col) {
381 if (vjp_sensitivities[input_col] !=
nullptr) {
382 auto vec_jac = smith::get<DERIVATIVE>(vecJacs[input_col](time_info.
time(), shape_disp, v_field, fields));
383 auto vec_jac_mfem_vector = assemble(vec_jac);
384 *vjp_sensitivities[input_col] += *vec_jac_mfem_vector;
398 return *v_dot_weak_form_residual_;
405 const std::vector<ConstFieldPtr>& fs)
const
407 using JacFuncType = std::function<decltype((*weak_form_)(
DifferentiateWRT<1>{}, time, *shape_disp, *fs[i]...))(
409 return std::array<JacFuncType,
sizeof...(i)>{
410 [
this](
double _time,
ConstFieldPtr _shape_disp,
const std::vector<ConstFieldPtr>& _fs) {
418 const std::vector<ConstFieldPtr>& fs)
const
421 std::function<decltype((*v_dot_weak_form_residual_)(
DifferentiateWRT<1>{}, time, *shape_disp, *v, *fs[i]...))(
423 return std::array<GradFuncType,
sizeof...(i)>{
433 mutable size_t cycle_ = 0;
447 inline std::vector<const mfem::ParFiniteElementSpace*>
getSpaces(
const std::vector<smith::FiniteElementState>& states)
449 std::vector<const mfem::ParFiniteElementSpace*>
spaces;
450 for (
auto& f : states) {
451 spaces.push_back(&f.space());
Class for encapsulating the dual vector space of a finite element space (i.e. the space of linear for...
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 ...
Smith mesh class which assists in constructing the appropriate parallel mfem meshes and registering a...
Accelerator functionality.
std::vector< const mfem::ParFiniteElementSpace * > getSpaces(const std::vector< smith::FiniteElementState > &states)
Helper function to construct vector of spaces from an existing vector of FiniteElementState.
std::vector< const mfem::ParFiniteElementSpace * > spaces(const std::vector< FieldState > &states, const std::vector< FieldState > ¶ms={})
Get the spaces from the primal fields of a vector of field states.
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)
SMITH_HOST_DEVICE auto max(dual< gradient_type > a, double b)
Implementation of max for dual numbers.
constexpr SMITH_HOST_DEVICE auto inner(const dual< S > &A, const dual< T > &B)
SMITH_HOST_DEVICE auto normalize(const tensor< T, n... > &A)
Normalizes the tensor Each element is divided by the Frobenius norm of the tensor,...
FiniteElementState const * ConstFieldPtr
using
Wrapper of smith::Functional for evaluating integrals and derivatives of quantities with shape displa...
Compile-time alias for a dimension.
a struct that is used in the physics modules to clarify which template arguments are user-controlled ...
struct storing time and timestep information
double dt() const
accessor for dt
size_t cycle() const
accessor for cycle
double time() const
accessor for the current time
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.