Smith  0.1
Smith is an implicit thermal structural mechanics simulation code.
functional_weak_form.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 
14 #pragma once
15 
17 #include "smith/physics/mesh.hpp"
21 
22 namespace smith {
23 
24 template <int spatial_dim, typename OutputSpace, typename inputs = Parameters<>,
25  typename input_indices = std::make_integer_sequence<int, inputs::n>>
27 
35 template <int spatial_dim, typename OutputSpace, typename... InputSpaces, int... input_indices>
36 class FunctionalWeakForm<spatial_dim, OutputSpace, Parameters<InputSpaces...>,
37  std::integer_sequence<int, input_indices...>> : public WeakForm {
38  public:
39  using SpacesT = std::vector<const mfem::ParFiniteElementSpace*>;
40 
42 
51  FunctionalWeakForm(std::string physics_name, std::shared_ptr<Mesh> mesh,
52  const mfem::ParFiniteElementSpace& output_mfem_space, const SpacesT& input_mfem_spaces)
53  : WeakForm(physics_name), mesh_(mesh)
54  {
55  std::array<const mfem::ParFiniteElementSpace*, sizeof...(InputSpaces)> trial_spaces;
56  std::array<const mfem::ParFiniteElementSpace*, sizeof...(InputSpaces) + 1> vector_residual_trial_spaces{
57  &output_mfem_space};
58 
59  SLIC_ERROR_ROOT_IF(
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()));
63 
64  // Validate output space
65  validateSpace<OutputSpace>(output_mfem_space, "output");
66 
67  // Validate input spaces
68  if constexpr (sizeof...(InputSpaces) > 0) {
69  // Validate each input space using a helper
70  validateInputSpaces<0>(input_mfem_spaces);
71 
72  for_constexpr<sizeof...(InputSpaces)>([&](auto i) { trial_spaces[i] = input_mfem_spaces[i]; });
73  for_constexpr<sizeof...(InputSpaces)>(
74  [&](auto i) { vector_residual_trial_spaces[i + 1] = input_mfem_spaces[i]; });
75  }
76 
77  const auto& shape_disp_space = mesh->shapeDisplacementSpace();
78 
79  weak_form_ = std::make_unique<ShapeAwareFunctional<ShapeDispSpace, OutputSpace(InputSpaces...)>>(
80  &shape_disp_space, &output_mfem_space, trial_spaces);
81 
82  v_dot_weak_form_residual_ =
83  std::make_unique<ShapeAwareFunctional<ShapeDispSpace, double(OutputSpace, InputSpaces...)>>(
84  &shape_disp_space, vector_residual_trial_spaces);
85  }
86 
108  template <typename BodyIntegralType, int... all_params>
109  void addBodyIntegralImpl(std::string body_name, BodyIntegralType integrand, std::integer_sequence<int, all_params...>)
110  {
111  const double* dt = &dt_;
112  const size_t* cycle = &cycle_;
113  const TimeInfo::EvaluationMode* mode = &mode_;
114  weak_form_->AddDomainIntegral(
115  Dimension<spatial_dim>{}, DependsOn<all_params...>{},
116  [dt, cycle, mode, integrand](double time, auto X, auto... inputs) {
117  return integrand(TimeInfo(time, *dt, *cycle, *mode), X, inputs...);
118  },
119  mesh_->domain(body_name));
120 
121  v_dot_weak_form_residual_->AddDomainIntegral(
122  Dimension<spatial_dim>{}, DependsOn<0, 1 + all_params...>{},
123  [dt, cycle, mode, integrand](double time, auto X, auto V, auto... inputs) {
124  auto orig_tuple = integrand(TimeInfo(time, *dt, *cycle, *mode), X, inputs...);
125  return smith::inner(get<VALUE>(V), get<VALUE>(orig_tuple)) +
126  smith::inner(get<DERIVATIVE>(V), get<DERIVATIVE>(orig_tuple));
127  },
128  mesh_->domain(body_name));
129  }
130 
132  template <int... active_parameters, typename BodyIntegralType>
133  void addBodyIntegral(DependsOn<active_parameters...>, std::string body_name, BodyIntegralType integrand)
134  {
135  addBodyIntegralImpl(body_name, integrand, std::integer_sequence<int, active_parameters...>{});
136  }
137 
139  template <typename BodyIntegralType>
140  void addBodyIntegral(std::string body_name, BodyIntegralType integrand)
141  {
142  addBodyIntegralImpl(body_name, integrand, std::make_integer_sequence<int, sizeof...(InputSpaces)>{});
143  }
144 
162  template <int... active_parameters, typename BodyLoadType>
164  void addBodySource(DependsOn<active_parameters...> depends_on, std::string body_name, BodyLoadType load_function)
165  {
166  addBodyIntegral(depends_on, body_name, [load_function](const TimeInfo& t_info, auto X, auto... inputs) {
167  return smith::tuple{-load_function(t_info, get<VALUE>(X), get<VALUE>(inputs)...), smith::zero{}};
168  });
169  }
170 
172  template <typename BodyLoadType>
173  void addBodySource(std::string body_name, BodyLoadType load_function)
174  {
175  addBodyIntegral(body_name, [load_function](const TimeInfo& t_info, auto X, auto... inputs) {
176  return smith::tuple{-load_function(t_info, get<VALUE>(X), get<VALUE>(inputs)...), smith::zero{}};
177  });
178  }
179 
201  template <typename BoundaryIntegrandType, int... all_params>
202  void addBoundaryIntegralImpl(std::string boundary_name, BoundaryIntegrandType integrand,
203  std::integer_sequence<int, all_params...>)
204  {
205  const double* dt = &dt_;
206  const size_t* cycle = &cycle_;
207  const TimeInfo::EvaluationMode* mode = &mode_;
208  weak_form_->AddBoundaryIntegral(
209  Dimension<spatial_dim - 1>{}, DependsOn<all_params...>{},
210  [dt, cycle, mode, integrand](double time, auto X, auto... params) {
211  return integrand(TimeInfo(time, *dt, *cycle, *mode), X, params...);
212  },
213  mesh_->domain(boundary_name));
214 
215  v_dot_weak_form_residual_->AddBoundaryIntegral(
216  Dimension<spatial_dim - 1>{}, DependsOn<0, 1 + all_params...>{},
217  [dt, cycle, mode, integrand](double time, auto X, auto V, auto... params) {
218  auto orig_surface_flux = integrand(TimeInfo(time, *dt, *cycle, *mode), X, params...);
219  return smith::inner(get<VALUE>(V), orig_surface_flux);
220  },
221  mesh_->domain(boundary_name));
222  }
223 
225  template <int... active_parameters, typename BoundaryIntegrandType>
226  void addBoundaryIntegral(DependsOn<active_parameters...>, std::string boundary_name, BoundaryIntegrandType integrand)
227  {
228  addBoundaryIntegralImpl(boundary_name, integrand, std::integer_sequence<int, active_parameters...>{});
229  }
230 
232  template <typename BoundaryIntegrandType>
233  void addBoundaryIntegral(std::string boundary_name, const BoundaryIntegrandType& integrand)
234  {
235  addBoundaryIntegralImpl(boundary_name, integrand, std::make_integer_sequence<int, sizeof...(InputSpaces)>{});
236  }
237 
254  template <typename InteriorIntegrandType, int... all_params>
255  void addInteriorBoundaryIntegralImpl(std::string interior_name, InteriorIntegrandType integrand,
256  std::integer_sequence<int, all_params...>)
257  {
258  const double* dt = &dt_;
259  const size_t* cycle = &cycle_;
260  const TimeInfo::EvaluationMode* mode = &mode_;
261  weak_form_->AddInteriorFaceIntegral(
262  Dimension<spatial_dim - 1>{}, DependsOn<all_params...>{},
263  [dt, cycle, mode, integrand](double time, auto X, auto... params) {
264  return integrand(TimeInfo(time, *dt, *cycle, *mode), X, params...);
265  },
266  mesh_->domain(interior_name));
267 
268  v_dot_weak_form_residual_->AddInteriorFaceIntegral(
269  Dimension<spatial_dim - 1>{}, DependsOn<0, 1 + all_params...>{},
270  [dt, cycle, mode, integrand](double time, auto X, auto V, auto... params) {
271  auto [V1, V2] = V;
272  auto orig_surface_flux = integrand(TimeInfo(time, *dt, *cycle, *mode), X, params...);
273  auto [flux_pos, flux_neg] = orig_surface_flux;
274  return smith::inner(V1, flux_pos) + smith::inner(V2, flux_neg);
275  },
276  mesh_->domain(interior_name));
277  }
278 
280  template <int... active_parameters, typename InteriorIntegrandType>
282  InteriorIntegrandType integrand)
283  {
284  addInteriorBoundaryIntegralImpl(interior_name, integrand, std::integer_sequence<int, active_parameters...>{});
285  }
286 
288  template <typename InteriorIntegrandType>
289  void addInteriorBoundaryIntegral(std::string interior_name, const InteriorIntegrandType& integrand)
290  {
291  addInteriorBoundaryIntegralImpl(interior_name, integrand,
292  std::make_integer_sequence<int, sizeof...(InputSpaces)>{});
293  }
294 
313  template <int... active_parameters, typename BoundaryFluxType>
315  void addBoundaryFlux(DependsOn<active_parameters...> depends_on, std::string boundary_name,
316  BoundaryFluxType flux_function)
317  {
318  addBoundaryIntegral(depends_on, boundary_name, [flux_function](const TimeInfo& t_info, auto X, auto... inputs) {
319  auto n = cross(get<DERIVATIVE>(X));
320  return -flux_function(t_info, get<VALUE>(X), normalize(n), get<VALUE>(inputs)...);
321  });
322  }
323 
325  template <typename BoundaryFluxType>
326  void addBoundaryFlux(std::string boundary_name, BoundaryFluxType flux_function)
327  {
328  addBoundaryIntegral(boundary_name, [flux_function](const TimeInfo& t_info, auto X, auto... inputs) {
329  auto n = cross(get<DERIVATIVE>(X));
330  return -flux_function(t_info, get<VALUE>(X), normalize(n), get<VALUE>(inputs)...);
331  });
332  }
333 
335  mfem::Vector residual(TimeInfo time_info, ConstFieldPtr shape_disp, const std::vector<ConstFieldPtr>& fields,
336  [[maybe_unused]] const std::vector<ConstQuadratureFieldPtr>& quad_fields = {}) const override
337  {
338  validateFields(fields, "residual");
339  dt_ = time_info.dt();
340  cycle_ = time_info.cycle();
341  mode_ = time_info.mode();
342  auto ret = (*weak_form_)(time_info.time(), *shape_disp, *fields[input_indices]...);
343  return ret;
344  }
345 
347  std::unique_ptr<mfem::HypreParMatrix> jacobian(
348  TimeInfo time_info, ConstFieldPtr shape_disp, const std::vector<ConstFieldPtr>& fields,
349  const std::vector<double>& jacobian_weights,
350  [[maybe_unused]] const std::vector<ConstQuadratureFieldPtr>& quad_fields = {}) const override
351  {
352  validateFields(fields, "jacobian");
353  dt_ = time_info.dt();
354  cycle_ = time_info.cycle();
355  mode_ = time_info.mode();
356 
357  std::unique_ptr<mfem::HypreParMatrix> J;
358 
359  auto addToJ = [&J](double factor, std::unique_ptr<mfem::HypreParMatrix> jac_contrib) {
360  if (J) {
361  SLIC_ERROR_IF(J->N() != jac_contrib->N(),
362  "Multiple nonzero jacobian weights are being used on inconsistently sized input arguments.");
363  SLIC_ERROR_IF(J->M() != jac_contrib->M(),
364  "Multiple nonzero jacobian weights are being used on inconsistently sized input arguments.");
365  J->Add(factor, *jac_contrib);
366  } else {
367  J.reset(jac_contrib.release());
368  if (factor != 1.0) (*J) *= factor;
369  }
370  };
371 
372  auto jacs = jacobianFunctions(std::make_integer_sequence<int, sizeof...(input_indices)>{}, time_info.time(),
373  shape_disp, fields);
374 
375  for (size_t input_col = 0; input_col < jacobian_weights.size(); ++input_col) {
376  if (jacobian_weights[input_col] != 0.0) {
377  auto K = smith::get<DERIVATIVE>(jacs[input_col](time_info.time(), shape_disp, fields));
378  addToJ(jacobian_weights[input_col], assemble(K));
379  }
380  }
381 
382  return J;
383  }
384 
386  void jvp(TimeInfo time_info, ConstFieldPtr shape_disp, const std::vector<ConstFieldPtr>& fields,
387  [[maybe_unused]] const std::vector<ConstQuadratureFieldPtr>& quad_fields,
388  [[maybe_unused]] ConstFieldPtr v_shape_disp, const std::vector<ConstFieldPtr>& v_fields,
389  [[maybe_unused]] const std::vector<ConstQuadratureFieldPtr>& v_quad_fields,
390  DualFieldPtr jvp_reaction) const override
391  {
392  validateFields(fields, "jvp");
393  SLIC_ERROR_IF(v_fields.size() != fields.size(),
394  "Invalid number of field sensitivities relative to the number of fields");
395 
396  dt_ = time_info.dt();
397  cycle_ = time_info.cycle();
398  mode_ = time_info.mode();
399 
400  auto jacs = jacobianFunctions(std::make_integer_sequence<int, sizeof...(input_indices)>{}, time_info.time(),
401  shape_disp, fields);
402 
403  *jvp_reaction = 0.0;
404 
405  for (size_t input_col = 0; input_col < fields.size(); ++input_col) {
406  if (v_fields[input_col] != nullptr) {
407  auto K = smith::get<DERIVATIVE>(jacs[input_col](time_info.time(), shape_disp, fields));
408  K.AddMult(*v_fields[input_col], *jvp_reaction);
409  }
410  }
411  }
412 
414  void vjp(TimeInfo time_info, ConstFieldPtr shape_disp, const std::vector<ConstFieldPtr>& fields,
415  [[maybe_unused]] const std::vector<ConstQuadratureFieldPtr>& quad_fields, ConstFieldPtr v_field,
416  DualFieldPtr vjp_shape_disp_sensitivity, const std::vector<DualFieldPtr>& vjp_sensitivities,
417  [[maybe_unused]] const std::vector<QuadratureFieldPtr>& vjp_quad_field_sensitivities) const override
418  {
419  validateFields(fields, "vjp");
420  SLIC_ERROR_IF(vjp_sensitivities.size() != fields.size(),
421  "Invalid number of field sensitivities relative to the number of fields");
422 
423  dt_ = time_info.dt();
424  cycle_ = time_info.cycle();
425  mode_ = time_info.mode();
426 
427  auto vecJacs = vectorJacobianFunctions(std::make_integer_sequence<int, sizeof...(input_indices)>{},
428  time_info.time(), shape_disp, v_field, fields);
429  {
430  auto shape_vjp = smith::get<DERIVATIVE>((*v_dot_weak_form_residual_)(
431  DifferentiateWRT<0>{}, time_info.time(), *shape_disp, *v_field, *fields[input_indices]...));
432  auto shape_vjp_vector = assemble(shape_vjp);
433  *vjp_shape_disp_sensitivity += *shape_vjp_vector;
434  }
435 
436  for (size_t input_col = 0; input_col < fields.size(); ++input_col) {
437  if (vjp_sensitivities[input_col] != nullptr) {
438  auto vec_jac = smith::get<DERIVATIVE>(vecJacs[input_col](time_info.time(), shape_disp, v_field, fields));
439  auto vec_jac_mfem_vector = assemble(vec_jac);
440  *vjp_sensitivities[input_col] += *vec_jac_mfem_vector;
441  }
442  }
443  }
444 
447  ShapeAwareFunctional<ShapeDispSpace, OutputSpace(InputSpaces...)>& getShapeAwareResidual() { return *weak_form_; }
448 
452  ShapeAwareFunctional<ShapeDispSpace, double(OutputSpace, InputSpaces...)>& getShapeAwareVectorTimesResidual()
453  {
454  return *v_dot_weak_form_residual_;
455  }
456 
457  protected:
458  template <typename BodyIntegralType, int... all_params>
460  void addBodyIntegralWithAllParams(std::string body_name, BodyIntegralType integrand,
461  std::integer_sequence<int, all_params...>)
462  {
463  addBodyIntegralImpl(body_name, integrand, std::integer_sequence<int, all_params...>{});
464  }
465 
466  template <typename BodyLoadType, int... all_params>
468  void addBodySourceWithAllParams(std::string body_name, BodyLoadType load_function,
469  std::integer_sequence<int, all_params...>)
470  {
471  (void)std::integer_sequence<int, all_params...>{};
472  addBodySource(body_name, load_function);
473  }
474 
475  template <typename BoundaryIntegrandType, int... all_params>
477  void addBoundaryIntegralWithAllParams(std::string boundary_name, BoundaryIntegrandType integrand,
478  std::integer_sequence<int, all_params...>)
479  {
480  addBoundaryIntegralImpl(boundary_name, integrand, std::integer_sequence<int, all_params...>{});
481  }
482 
483  template <typename BoundaryFluxType, int... all_params>
485  void addBoundaryFluxWithAllParams(std::string boundary_name, BoundaryFluxType flux_function,
486  std::integer_sequence<int, all_params...>)
487  {
488  (void)std::integer_sequence<int, all_params...>{};
489  addBoundaryFlux(boundary_name, flux_function);
490  }
491 
492  template <typename InteriorIntegrandType, int... all_params>
494  void addInteriorBoundaryIntegralWithAllParams(std::string interior_name, InteriorIntegrandType integrand,
495  std::integer_sequence<int, all_params...>)
496  {
497  addInteriorBoundaryIntegralImpl(interior_name, integrand, std::integer_sequence<int, all_params...>{});
498  }
499 
501  template <size_t I>
502  void validateInputSpaces(const SpacesT& input_mfem_spaces) const
503  {
504  if constexpr (I < sizeof...(InputSpaces)) {
505  using Space = typename std::tuple_element<I, std::tuple<InputSpaces...>>::type;
506  validateSpace<Space>(*input_mfem_spaces[I], axom::fmt::format("input[{}]", I));
507  validateInputSpaces<I + 1>(input_mfem_spaces);
508  }
509  }
510 
512  template <size_t I>
513  void validateFieldsRecursive(const std::vector<ConstFieldPtr>& fields, const std::string& method_name) const
514  {
515  if constexpr (I < sizeof...(InputSpaces)) {
516  using Space = typename std::tuple_element<I, std::tuple<InputSpaces...>>::type;
517  validateSpace<Space>(fields[I]->space(),
518  axom::fmt::format("{}(): field[{}] ('{}')", method_name, I, fields[I]->name()));
519  validateFieldsRecursive<I + 1>(fields, method_name);
520  }
521  }
522 
524  void validateFields(const std::vector<ConstFieldPtr>& fields, const std::string& method_name) const
525  {
526  SLIC_ERROR_ROOT_IF(fields.size() != sizeof...(InputSpaces),
527  axom::fmt::format("{}(): fields.size()={} but weak form expects {} InputSpaces", method_name,
528  fields.size(), sizeof...(InputSpaces)));
529 
530  if constexpr (sizeof...(InputSpaces) > 0) {
531  validateFieldsRecursive<0>(fields, method_name);
532  }
533  }
534 
536  template <typename Space>
537  static void validateSpace(const mfem::ParFiniteElementSpace& mfem_space, const std::string& space_name)
538  {
539  const auto* fec = mfem_space.FEColl();
540 
541  // Note: We check using the name string rather than dynamic_cast because MFEM
542  // has multiple FECollection types that represent H1 and L2 spaces
543  if constexpr (Space::family == Family::H1) {
544  std::string fec_name = fec->Name();
545  bool is_h1 =
546  (fec_name.find("H1") != std::string::npos || fec_name.find("ND_") != std::string::npos || // Nedelec elements
547  fec_name.find("Linear") != std::string::npos);
548  SLIC_ERROR_ROOT_IF(!is_h1, axom::fmt::format("Space '{}': Template specifies H1 family but mfem space uses '{}'",
549  space_name, fec_name));
550  } else if constexpr (Space::family == Family::L2) {
551  std::string fec_name = fec->Name();
552  bool is_l2 = (fec_name.find("L2") != std::string::npos || fec_name.find("DG") != std::string::npos ||
553  fec_name.find("Const") != std::string::npos);
554  SLIC_ERROR_ROOT_IF(
555  !is_l2, axom::fmt::format("Space '{}': Template specifies L2/DG family but mfem space uses '{}'", space_name,
556  fec_name));
557  }
558 
559  // Validate order
560  SLIC_ERROR_ROOT_IF(fec->GetOrder() != Space::order,
561  axom::fmt::format("Space '{}': Template specifies order {} but mfem space has order {}",
562  space_name, Space::order, fec->GetOrder()));
563 
564  // Validate vector dimension
565  SLIC_ERROR_ROOT_IF(
566  mfem_space.GetVDim() != Space::components,
567  axom::fmt::format("Space '{}': Template specifies {} components but mfem space has {} components (VDim)",
568  space_name, Space::components, mfem_space.GetVDim()));
569  }
570 
572  template <int... i>
573  auto jacobianFunctions(std::integer_sequence<int, i...>, double time, ConstFieldPtr shape_disp,
574  const std::vector<ConstFieldPtr>& fs) const
575  {
576  using JacFuncType = std::function<decltype((*weak_form_)(DifferentiateWRT<1>{}, time, *shape_disp, *fs[i]...))(
577  double, ConstFieldPtr, const std::vector<ConstFieldPtr>&)>;
578  return std::array<JacFuncType, sizeof...(i)>{
579  [this](double _time, ConstFieldPtr _shape_disp, const std::vector<ConstFieldPtr>& _fs) {
580  return (*weak_form_)(DifferentiateWRT<i + 1>{}, _time, *_shape_disp, *_fs[i]...);
581  }...};
582  }
583 
585  template <int... i>
586  auto vectorJacobianFunctions(std::integer_sequence<int, i...>, double time, ConstFieldPtr shape_disp, ConstFieldPtr v,
587  const std::vector<ConstFieldPtr>& fs) const
588  {
589  using GradFuncType =
590  std::function<decltype((*v_dot_weak_form_residual_)(DifferentiateWRT<1>{}, time, *shape_disp, *v, *fs[i]...))(
591  double, ConstFieldPtr, ConstFieldPtr, const std::vector<ConstFieldPtr>&)>;
592  return std::array<GradFuncType, sizeof...(i)>{
593  [this](double _time, ConstFieldPtr _shape_disp, ConstFieldPtr _v, const std::vector<ConstFieldPtr>& _fs) {
594  return (*v_dot_weak_form_residual_)(DifferentiateWRT<i + 2>{}, _time, *_shape_disp, *_v, *_fs[i]...);
595  }...};
596  }
597 
599  mutable double dt_ = std::numeric_limits<double>::max();
600 
602  mutable size_t cycle_ = 0;
603 
606 
608  std::shared_ptr<Mesh> mesh_;
609 
611  std::unique_ptr<ShapeAwareFunctional<ShapeDispSpace, OutputSpace(InputSpaces...)>> weak_form_;
612 
614  std::unique_ptr<ShapeAwareFunctional<ShapeDispSpace, double(OutputSpace, InputSpaces...)>> v_dot_weak_form_residual_;
615 };
616 
619 inline std::vector<const mfem::ParFiniteElementSpace*> getSpaces(const std::vector<smith::FiniteElementState>& states)
620 {
621  std::vector<const mfem::ParFiniteElementSpace*> spaces;
622  for (auto& f : states) {
623  spaces.push_back(&f.space());
624  }
625  return spaces;
626 }
627 
628 } // namespace smith
Class for encapsulating the dual vector space of a finite element space (i.e. the space of linear for...
void addBoundaryFlux(std::string boundary_name, BoundaryFluxType flux_function)
Add a boundary flux depending on all input fields.
void jvp(TimeInfo time_info, ConstFieldPtr shape_disp, const std::vector< ConstFieldPtr > &fields, [[maybe_unused]] const std::vector< ConstQuadratureFieldPtr > &quad_fields, [[maybe_unused]] ConstFieldPtr v_shape_disp, const std::vector< ConstFieldPtr > &v_fields, [[maybe_unused]] const std::vector< ConstQuadratureFieldPtr > &v_quad_fields, DualFieldPtr jvp_reaction) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
auto vectorJacobianFunctions(std::integer_sequence< int, i... >, double time, ConstFieldPtr shape_disp, ConstFieldPtr v, const std::vector< ConstFieldPtr > &fs) const
Utility to get array of jvp functions, one for each input field in fs.
void addBodySource(std::string body_name, BodyLoadType load_function)
Add a body source depending on all input fields.
void validateFields(const std::vector< ConstFieldPtr > &fields, const std::string &method_name) const
Validate that fields vector matches the templated input spaces.
std::unique_ptr< ShapeAwareFunctional< ShapeDispSpace, double(OutputSpace, InputSpaces...)> > v_dot_weak_form_residual_
functional residual times and arbitrary vector v (same space as residual) evaluator,...
std::unique_ptr< ShapeAwareFunctional< ShapeDispSpace, OutputSpace(InputSpaces...)> > weak_form_
functional residual evaluator, shape aware
void addInteriorBoundaryIntegral(std::string interior_name, const InteriorIntegrandType &integrand)
Add an interior boundary integral to the weak form.
ShapeAwareFunctional< ShapeDispSpace, OutputSpace(InputSpaces...)> & getShapeAwareResidual()
Accessor to get a reference to the underlying ShapeAwareFunctional in case more direct access is need...
void validateFieldsRecursive(const std::vector< ConstFieldPtr > &fields, const std::string &method_name) const
Helper to validate fields passed to residual/jacobian/vjp/jvp.
FunctionalWeakForm(std::string physics_name, std::shared_ptr< Mesh > mesh, const mfem::ParFiniteElementSpace &output_mfem_space, const SpacesT &input_mfem_spaces)
Construct a new FunctionalWeakForm object.
void addBodySourceWithAllParams(std::string body_name, BodyLoadType load_function, std::integer_sequence< int, all_params... >)
Forwards body source with dependency list covering all input parameters.
void addBoundaryIntegral(DependsOn< active_parameters... >, std::string boundary_name, BoundaryIntegrandType integrand)
Add a boundary integral depending only on selected input fields.
auto jacobianFunctions(std::integer_sequence< int, i... >, double time, ConstFieldPtr shape_disp, const std::vector< ConstFieldPtr > &fs) const
Utility to get array of jacobian functions, one for each input field in fs.
void addBodyIntegralImpl(std::string body_name, BodyIntegralType integrand, std::integer_sequence< int, all_params... >)
Add a body integral contribution to the residual.
void addBoundaryIntegral(std::string boundary_name, const BoundaryIntegrandType &integrand)
Add a boundary integral to the weak form.
void addBoundaryIntegralImpl(std::string boundary_name, BoundaryIntegrandType integrand, std::integer_sequence< int, all_params... >)
Add a boundary integral term to the weak form.
std::unique_ptr< mfem::HypreParMatrix > jacobian(TimeInfo time_info, ConstFieldPtr shape_disp, const std::vector< ConstFieldPtr > &fields, const std::vector< double > &jacobian_weights, [[maybe_unused]] const std::vector< ConstQuadratureFieldPtr > &quad_fields={}) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void addInteriorBoundaryIntegralImpl(std::string interior_name, InteriorIntegrandType integrand, std::integer_sequence< int, all_params... >)
Add a interior boundary integral term to the weak form.
void addBodySource(DependsOn< active_parameters... > depends_on, std::string body_name, BodyLoadType load_function)
Add a body source (body load) to the weak form.
void addBoundaryFluxWithAllParams(std::string boundary_name, BoundaryFluxType flux_function, std::integer_sequence< int, all_params... >)
Forwards boundary flux with dependency list covering all input parameters.
void addBoundaryFlux(DependsOn< active_parameters... > depends_on, std::string boundary_name, BoundaryFluxType flux_function)
Add a boundary flux term to the weak form.
void addBodyIntegralWithAllParams(std::string body_name, BodyIntegralType integrand, std::integer_sequence< int, all_params... >)
Forwards body integrand with dependency list covering all input parameters.
mfem::Vector residual(TimeInfo time_info, ConstFieldPtr shape_disp, const std::vector< ConstFieldPtr > &fields, [[maybe_unused]] const std::vector< ConstQuadratureFieldPtr > &quad_fields={}) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void addBodyIntegral(std::string body_name, BodyIntegralType integrand)
Add a body integral to the weak form.
void addInteriorBoundaryIntegral(DependsOn< active_parameters... >, std::string interior_name, InteriorIntegrandType integrand)
Add an interior boundary integral depending only on selected input fields.
void vjp(TimeInfo time_info, ConstFieldPtr shape_disp, const std::vector< ConstFieldPtr > &fields, [[maybe_unused]] const std::vector< ConstQuadratureFieldPtr > &quad_fields, ConstFieldPtr v_field, DualFieldPtr vjp_shape_disp_sensitivity, const std::vector< DualFieldPtr > &vjp_sensitivities, [[maybe_unused]] const std::vector< QuadratureFieldPtr > &vjp_quad_field_sensitivities) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void addInteriorBoundaryIntegralWithAllParams(std::string interior_name, InteriorIntegrandType integrand, std::integer_sequence< int, all_params... >)
Forwards interior-boundary integrand with dependency list covering all input parameters.
static void validateSpace(const mfem::ParFiniteElementSpace &mfem_space, const std::string &space_name)
Validate that an mfem space matches the template space parameters.
void addBoundaryIntegralWithAllParams(std::string boundary_name, BoundaryIntegrandType integrand, std::integer_sequence< int, all_params... >)
Forwards boundary integrand with dependency list covering all input parameters.
void addBodyIntegral(DependsOn< active_parameters... >, std::string body_name, BodyIntegralType integrand)
Add a body integral depending only on selected input fields.
void validateInputSpaces(const SpacesT &input_mfem_spaces) const
Helper to validate input spaces recursively (for constructor)
ShapeAwareFunctional< ShapeDispSpace, double(OutputSpace, InputSpaces...)> & getShapeAwareVectorTimesResidual()
Accessor to get a reference to the underlying ShapeAwareFunctional vector-residual in case more direc...
Abstract WeakForm class.
Definition: weak_form.hpp:36
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...
constexpr SMITH_HOST_DEVICE void for_constexpr(const lambda &f)
multidimensional loop tool that evaluates the lambda body inside the innermost loop.
Accelerator functionality.
Definition: smith.cpp:36
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 > &params={})
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)
Definition: tensor.hpp:966
SMITH_HOST_DEVICE auto max(dual< gradient_type > a, double b)
Implementation of max for dual numbers.
Definition: dual.hpp:229
mfem::future::tuple< T... > tuple
Expose MFEM tuple in the Smith namespace.
Definition: tuple.hpp:241
constexpr SMITH_HOST_DEVICE auto inner(const dual< S > &A, const dual< T > &B)
Definition: dual.hpp:281
mfem::future::tuple_element< I, T > tuple_element
Alias for the MFEM tuple element trait.
Definition: tuple.hpp:258
mfem::ParFiniteElementSpace & space(FieldState field)
Get the space from the primal field of a field states.
SMITH_HOST_DEVICE auto normalize(const tensor< T, n... > &A)
Normalizes the tensor Each element is divided by the Frobenius norm of the tensor,...
Definition: tensor.hpp:1122
FiniteElementState const * ConstFieldPtr
using
Definition: field_types.hpp:36
Wrapper of smith::Functional for evaluating integrals and derivatives of quantities with shape displa...
Compile-time alias for a dimension.
Definition: geometry.hpp:17
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:59
struct storing time and timestep information
Definition: common.hpp:18
double dt() const
accessor for dt
Definition: common.hpp:36
size_t cycle() const
accessor for cycle
Definition: common.hpp:39
EvaluationMode
Evaluation mode for the current step.
Definition: common.hpp:21
@ Regular
Normal evaluation step.
double time() const
accessor for the current time
Definition: common.hpp:33
EvaluationMode mode() const
accessor for residual evaluation mode.
Definition: common.hpp:45
A sentinel struct for eliding no-op tensor operations.
Definition: tensor.hpp:122
Specifies interface for evaluating weak form residuals and their gradients.