Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
shape_aware_functional.hpp
Go to the documentation of this file.
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Serac Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
14 #pragma once
15 
18 
19 namespace serac {
20 
21 namespace detail {
22 
30 template <int dim, typename shape_type>
32  public:
39  : J_(Identity<dim>() + get<DERIVATIVE>(p)), detJ_(det(J_)), inv_J_(inv(J_)), inv_JT_(transpose(inv_J_))
40  {
41  }
42 
54  template <typename trial_type, typename space_type>
55  SERAC_HOST_DEVICE auto modify_trial_argument(space_type /* space */, const trial_type& u) const
56  {
57  if constexpr (space_type{}.family == Family::H1 || space_type{}.family == Family::L2) {
58  // Our updated spatial coordinate is x' = x + p. We want to calculate
59  // du/dx' = du/dx * dx/dx'
60  // = du/dx * (dx'/dx)^-1
61  // = du_dx * (J)^-1
62  auto trial_derivative = dot(get<DERIVATIVE>(u), inv_J_);
63 
64  return serac::tuple{get<VALUE>(u), trial_derivative};
65  }
66 
67  if constexpr (space_type{}.family == Family::HCURL) {
68  auto modified_val = dot(get<VALUE>(u), inv_J_);
69  if constexpr (dim == 2) {
70  auto modified_derivative = get<DERIVATIVE>(u) / detJ_;
71  return serac::tuple{modified_val, modified_derivative};
72  }
73  if constexpr (dim == 3) {
74  auto modified_derivative = dot(get<DERIVATIVE>(u), transpose(J_));
75  return serac::tuple{modified_val, modified_derivative};
76  }
77  }
78  }
79 
91  template <typename test_space, typename q_func_type>
92  SERAC_HOST_DEVICE auto modify_shape_aware_qf_return(test_space /*test*/, const q_func_type& v)
93  {
94  if constexpr (std::is_same_v<test_space, double>) {
95  return detJ_ * v;
96  } else {
97  if constexpr (test_space{}.family == Family::H1 || test_space{}.family == Family::L2) {
98  auto modified_flux = dot(get<FLUX>(v), inv_JT_) * detJ_;
99  auto modified_source = get<SOURCE>(v) * detJ_;
100 
101  return serac::tuple{modified_source, modified_flux};
102  }
103  if constexpr (test_space{}.family == Family::HCURL) {
104  auto modified_source = dot(get<SOURCE>(v), inv_JT_) * detJ_;
105  if constexpr (dim == 3) {
106  auto modified_flux = dot(get<FLUX>(v), J_);
107  return serac::tuple{modified_source, modified_flux};
108  } else {
109  return serac::tuple{modified_source, get<FLUX>(v)};
110  }
111  }
112  }
113  }
114 
115  private:
117  using jacobian_type = std::remove_reference_t<decltype(get<DERIVATIVE>(std::declval<shape_type>()))>;
118  using detJ_type = decltype(det(std::declval<jacobian_type>()));
119  using inv_J_type = decltype(inv(std::declval<jacobian_type>()));
120  using inv_JT_type = decltype(inv(transpose(std::declval<jacobian_type>())));
122 
124  jacobian_type J_;
125 
127  detJ_type detJ_;
128 
130  inv_J_type inv_J_;
131 
133  inv_JT_type inv_JT_;
134 };
135 
148 template <typename position_type, typename shape_type>
149 SERAC_HOST_DEVICE auto compute_boundary_area_correction(const position_type& X, const shape_type& shape)
150 {
151  auto x_prime = X + shape;
152 
153  auto n = cross(get<DERIVATIVE>(x_prime));
154 
155  // serac::Functional's boundary integrals multiply the q-function output by
156  // norm(cross(dX_dxi)) at that quadrature point, but if we impose a shape displacement
157  // then that weight needs to be corrected. The new weight should be
158  // norm(cross(dX_dxi + dp_dxi)), so we multiply by the ratio w_new / w_old
159  // to get
160  // q * area_correction * w_old
161  // = q * (w_new / w_old) * w_old
162  // = q * w_new
163 
164  auto area_correction = norm(n) / norm(cross(get<DERIVATIVE>(X)));
165 
166  return area_correction;
167 }
168 
193 template <typename lambda, typename coord_type, typename shape_type, typename space_types, typename trial_types,
194  typename correction_type, int... i>
195 SERAC_HOST_DEVICE auto apply_shape_aware_qf_helper(const lambda& qf, double t, const coord_type& position,
196  const shape_type& shape, const space_types& space_tuple,
197  const trial_types& arg_tuple, const correction_type& correction,
198  std::integer_sequence<int, i...>)
199 {
201  "Argument and finite element space tuples are not the same size.");
202 
203  auto x = serac::tuple{get<VALUE>(position) + get<VALUE>(shape),
204 
205  // x := X + u,
206  // so, dx/dxi = dX/dxi + du/dxi
207  // = dX/dxi + du/dX * dX/dxi
208  get<DERIVATIVE>(position) + get<DERIVATIVE>(shape) * get<DERIVATIVE>(position)};
209 
210  return qf(t, x, correction.modify_trial_argument(serac::get<i>(space_tuple), serac::get<i>(arg_tuple))...);
211 }
212 
239 template <typename lambda, typename coord_type, typename state_type, typename shape_type, typename space_types,
240  typename trial_types, typename correction_type, int... i>
241 SERAC_HOST_DEVICE auto apply_shape_aware_qf_helper_with_state(const lambda& qf, double t, const coord_type& position,
242  state_type& state, const shape_type& shape,
243  const space_types& space_tuple,
244  const trial_types& arg_tuple,
245  const correction_type& correction,
246  std::integer_sequence<int, i...>)
247 {
249  "Argument and finite element space tuples are not the same size.");
250 
251  auto x = serac::tuple{get<VALUE>(position) + get<VALUE>(shape),
252 
253  // x := X + u,
254  // so, dx/dxi = dX/dxi + du/dxi
255  // = dX/dxi + du/dX * dX/dxi
256  get<DERIVATIVE>(position) + get<DERIVATIVE>(shape) * get<DERIVATIVE>(position)};
257 
258  return qf(t, x, state, correction.modify_trial_argument(serac::get<i>(space_tuple), serac::get<i>(arg_tuple))...);
259 }
260 
261 } // namespace detail
262 
264 template <typename T1, typename T2, ExecutionSpace exec = serac::ExecutionSpace::CPU>
265 class ShapeAwareFunctional;
267 
285 template <typename test, typename shape, typename... trials, ExecutionSpace exec>
286 class ShapeAwareFunctional<shape, test(trials...), exec> {
288  static constexpr tuple<trials...> trial_spaces{};
289 
291  static constexpr test test_space{};
292 
294  static constexpr shape shape_space{};
295 
297  static constexpr uint32_t num_trial_spaces = sizeof...(trials);
298 
299  public:
310  template <typename test_type = test, typename = std::enable_if_t<!std::is_same_v<double, test_type>>>
311  ShapeAwareFunctional(const mfem::ParFiniteElementSpace* shape_fes, const mfem::ParFiniteElementSpace* test_fes,
312  std::array<const mfem::ParFiniteElementSpace*, num_trial_spaces> trial_fes)
313  {
314  static_assert(shape_space.family == Family::H1, "Only H1 spaces allowed for shape displacements");
315 
316  std::array<const mfem::ParFiniteElementSpace*, num_trial_spaces + 1> prepended_spaces;
317 
318  prepended_spaces[0] = shape_fes;
319 
320  for (uint32_t i = 0; i < num_trial_spaces; ++i) {
321  prepended_spaces[1 + i] = trial_fes[i];
322  }
323 
324  functional_ = std::make_unique<Functional<test(shape, trials...), exec>>(test_fes, prepended_spaces);
325  }
326 
336  template <typename test_type = test, typename = std::enable_if_t<std::is_same_v<double, test_type>>>
337  ShapeAwareFunctional(const mfem::ParFiniteElementSpace* shape_fes,
338  std::array<const mfem::ParFiniteElementSpace*, num_trial_spaces> trial_fes)
339  {
340  static_assert(shape_space.family == Family::H1, "Only H1 spaces allowed for shape displacements");
341 
342  std::array<const mfem::ParFiniteElementSpace*, num_trial_spaces + 1> prepended_spaces;
343 
344  prepended_spaces[0] = shape_fes;
345 
346  for (uint32_t i = 0; i < num_trial_spaces; ++i) {
347  prepended_spaces[1 + i] = trial_fes[i];
348  }
349 
350  functional_ = std::make_unique<Functional<double(shape, trials...), exec>>(prepended_spaces);
351  }
352 
357  template <typename Integrand, int dim, int... args>
358  struct ShapeAwareIntegrandWrapper {
360  ShapeAwareIntegrandWrapper(Integrand integrand) : integrand_(integrand) {}
362  Integrand integrand_;
375  template <typename PositionType, typename ShapeValueType, typename... QFuncArgs>
376  SERAC_HOST_DEVICE auto operator()(double time, PositionType x, ShapeValueType shape_val,
377  QFuncArgs... qfunc_args) const
378  {
379  auto qfunc_tuple = make_tuple(qfunc_args...);
380  auto reduced_trial_space_tuple = make_tuple(get<args>(trial_spaces)...);
381 
382  detail::ShapeCorrection shape_correction(Dimension<dim>{}, shape_val);
383  // TODO(CUDA): When this is compiled to device code, the below make_integer_sequence will
384  // need to change to a camp integer sequence.
385  auto unmodified_qf_return = detail::apply_shape_aware_qf_helper(
386  integrand_, time, x, shape_val, reduced_trial_space_tuple, qfunc_tuple, shape_correction,
387  std::make_integer_sequence<int, sizeof...(qfunc_args)>{});
388  return shape_correction.modify_shape_aware_qf_return(test_space, unmodified_qf_return);
389  }
390  };
391 
396  template <typename Integrand, int dim, int... args>
397  struct ShapeAwareIntegrandWrapperWithState {
399  ShapeAwareIntegrandWrapperWithState(Integrand integrand) : integrand_(integrand) {}
401  Integrand integrand_;
416  template <typename PositionType, typename StateType, typename ShapeValueType, typename... QFuncArgs>
417  SERAC_HOST_DEVICE auto operator()(double time, PositionType x, StateType& state, ShapeValueType shape_val,
418  QFuncArgs... qfunc_args) const
419  {
420  auto qfunc_tuple = make_tuple(qfunc_args...);
421  auto reduced_trial_space_tuple = make_tuple(get<args>(trial_spaces)...);
422 
423  detail::ShapeCorrection shape_correction(Dimension<dim>{}, shape_val);
424  // TODO(CUDA): When this is compiled to device code, the below make_integer_sequence will
425  // need to change to a camp integer sequence.
426  auto unmodified_qf_return = detail::apply_shape_aware_qf_helper_with_state(
427  integrand_, time, x, state, shape_val, reduced_trial_space_tuple, qfunc_tuple, shape_correction,
428  std::make_integer_sequence<int, sizeof...(qfunc_args)>{});
429  return shape_correction.modify_shape_aware_qf_return(test_space, unmodified_qf_return);
430  }
431  };
432 
448  template <int dim, int... args, typename lambda, typename qpt_data_type = Nothing>
449  void AddDomainIntegral(Dimension<dim>, DependsOn<args...>, const lambda& integrand, Domain& domain,
450  std::shared_ptr<QuadratureData<qpt_data_type>> qdata = NoQData)
451  {
452  if constexpr (std::is_same_v<qpt_data_type, Nothing>) {
453  functional_->AddDomainIntegral(Dimension<dim>{}, DependsOn<0, (args + 1)...>{},
454  ShapeAwareIntegrandWrapper<lambda, dim, args...>(integrand), domain, qdata);
455  } else {
456  functional_->AddDomainIntegral(Dimension<dim>{}, DependsOn<0, (args + 1)...>{},
457  ShapeAwareIntegrandWrapperWithState<lambda, dim, args...>(integrand), domain,
458  qdata);
459  }
460  }
461 
466  template <typename test_type, typename Integrand, int dim, int... args>
467  struct ShapeAwareInteriorBoundaryIntegrandWrapper {
469  ShapeAwareInteriorBoundaryIntegrandWrapper(Integrand integrand) : integrand_(integrand) {}
471  Integrand integrand_;
484  template <typename PositionType, typename ShapeValueType, typename... QFuncArgs>
485  SERAC_HOST_DEVICE auto operator()(double time, PositionType x, ShapeValueType shape_val,
486  QFuncArgs... qfunc_args) const
487  {
488  auto unmodified_qf_returns = integrand_(time, x + shape_val, qfunc_args...);
489  auto correction = detail::compute_boundary_area_correction(x, shape_val);
490  return serac::tuple{correction * serac::get<0>(unmodified_qf_returns),
491  correction * serac::get<1>(unmodified_qf_returns)};
492  }
493  };
494 
499  template <typename Integrand, int dim, int... args>
500  struct ShapeAwareInteriorBoundaryIntegrandWrapper<double, Integrand, dim, args...> {
502  ShapeAwareInteriorBoundaryIntegrandWrapper(Integrand integrand) : integrand_(integrand) {}
504  Integrand integrand_;
517  template <typename PositionType, typename ShapeValueType, typename... QFuncArgs>
518  SERAC_HOST_DEVICE auto operator()(double time, PositionType x, ShapeValueType shape_val,
519  QFuncArgs... qfunc_args) const
520  {
521  auto unmodified_qf_returns = integrand_(time, x + shape_val, qfunc_args...);
522  auto correction = detail::compute_boundary_area_correction(x, shape_val);
523  return correction * unmodified_qf_returns;
524  }
525  };
526 
541  template <int dim, int... args, typename lambda>
542  void AddInteriorFaceIntegral(Dimension<dim>, DependsOn<args...>, const lambda& integrand, Domain& domain)
543  {
544  functional_->AddInteriorFaceIntegral(
545  Dimension<dim>{}, DependsOn<0, (args + 1)...>{},
546  ShapeAwareInteriorBoundaryIntegrandWrapper<test, lambda, dim, args...>(integrand), domain);
547  }
548 
553  template <typename Integrand, int dim, int... args>
554  struct ShapeAwareBoundaryIntegrandWrapper {
556  ShapeAwareBoundaryIntegrandWrapper(Integrand integrand) : integrand_(integrand) {}
558  Integrand integrand_;
571  template <typename PositionType, typename ShapeValueType, typename... QFuncArgs>
572  SERAC_HOST_DEVICE auto operator()(double time, PositionType x, ShapeValueType shape_val,
573  QFuncArgs... qfunc_args) const
574  {
575  auto unmodified_qf_return = integrand_(time, x + shape_val, qfunc_args...);
576  return unmodified_qf_return * detail::compute_boundary_area_correction(x, shape_val);
577  }
578  };
579 
594  template <int dim, int... args, typename lambda>
595  void AddBoundaryIntegral(Dimension<dim>, DependsOn<args...>, const lambda& integrand, Domain& domain)
596  {
597  functional_->AddBoundaryIntegral(Dimension<dim>{}, DependsOn<0, (args + 1)...>{},
598  ShapeAwareBoundaryIntegrandWrapper<lambda, dim, args...>(integrand), domain);
599  }
600 
616  template <uint32_t wrt, typename... T>
617  auto operator()(DifferentiateWRT<wrt>, double t, const T&... args)
618  {
619  return (*functional_)(DifferentiateWRT<wrt>{}, t, args...);
620  }
621 
635  template <typename... T>
636  auto operator()(double t, const T&... args)
637  {
638  return (*functional_)(t, args...);
639  }
640 
649  void updateQdata(bool update_flag) { functional_->updateQdata(update_flag); }
650 
651  private:
653  std::unique_ptr<Functional<test(shape, trials...), exec>> functional_;
654 };
655 
656 } // namespace serac
This file contains the interface used for initializing/terminating any hardware accelerator-related f...
#define SERAC_HOST_DEVICE
Macro that evaluates to __host__ __device__ when compiling with nvcc and does nothing on a host compi...
Definition: accelerator.hpp:38
auto operator()(double t, const T &... args)
This function lets the user evaluate the serac::ShapeAwareFunctional with the given trial space value...
auto operator()(DifferentiateWRT< wrt >, double t, const T &... args)
This function lets the user evaluate the serac::ShapeAwareFunctional with the given trial space value...
void AddBoundaryIntegral(Dimension< dim >, DependsOn< args... >, const lambda &integrand, Domain &domain)
Adds a boundary integral term to the weak formulationx of the PDE.
void AddDomainIntegral(Dimension< dim >, DependsOn< args... >, const lambda &integrand, Domain &domain, std::shared_ptr< QuadratureData< qpt_data_type >> qdata=NoQData)
Adds a domain integral term to the weak formulation of the PDE.
ShapeAwareFunctional(const mfem::ParFiniteElementSpace *shape_fes, const mfem::ParFiniteElementSpace *test_fes, std::array< const mfem::ParFiniteElementSpace *, num_trial_spaces > trial_fes)
Constructs using mfem::ParFiniteElementSpace objects corresponding to the test/trial spaces.
void updateQdata(bool update_flag)
A flag to update the quadrature data for this operator following the computation.
ShapeAwareFunctional(const mfem::ParFiniteElementSpace *shape_fes, std::array< const mfem::ParFiniteElementSpace *, num_trial_spaces > trial_fes)
Constructs a QOI functional using mfem::ParFiniteElementSpace objects corresponding to the trial spac...
void AddInteriorFaceIntegral(Dimension< dim >, DependsOn< args... >, const lambda &integrand, Domain &domain)
Adds an interior face integral term to the weak formulation of the PDE.
Implementation of the quadrature-function-based functional enabling rapid development of FEM formulat...
Accelerator functionality.
Definition: serac.cpp:36
constexpr SERAC_HOST_DEVICE auto dot(const isotropic_tensor< S, m, m > &I, const tensor< T, m, n... > &A)
dot product between an isotropic and (nonisotropic) tensor
std::shared_ptr< QuadratureData< Nothing > > NoQData
a single instance of a QuadratureData container of Nothings, since they are all interchangeable
constexpr T & get(variant< T0, T1 > &v)
Returns the variant member of specified type.
Definition: variant.hpp:338
SERAC_HOST_DEVICE tuple< T... > make_tuple(const T &... args)
helper function for combining a list of values into a tuple
Definition: tuple.hpp:267
constexpr SERAC_HOST_DEVICE auto det(const isotropic_tensor< T, m, m > &I)
compute the determinant of an isotropic tensor
constexpr SERAC_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
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:961
constexpr SERAC_HOST_DEVICE auto inv(const isotropic_tensor< T, m, m > &I)
return the inverse of an isotropic tensor
constexpr SERAC_HOST_DEVICE auto transpose(const isotropic_tensor< T, m, m > &I)
return the transpose of an isotropic tensor
ExecutionSpace
enum used for signalling whether or not to perform certain calculations on the CPU or GPU
Definition: accelerator.hpp:73
constexpr SERAC_HOST_DEVICE isotropic_tensor< double, m, m > Identity()
return the identity matrix of the specified size
SERAC_HOST_DEVICE auto apply_shape_aware_qf_helper_with_state(const lambda &qf, double t, const coord_type &position, state_type &state, const shape_type &shape, const space_types &space_tuple, const trial_types &arg_tuple, const correction_type &correction, std::integer_sequence< int, i... >)
A helper function to modify all of the trial function input derivatives according to the given shape ...
SERAC_HOST_DEVICE auto apply_shape_aware_qf_helper(const lambda &qf, double t, const coord_type &position, const shape_type &shape, const space_types &space_tuple, const trial_types &arg_tuple, const correction_type &correction, std::integer_sequence< int, i... >)
A helper function to modify all of the trial function input derivatives according to the given shape ...
SERAC_HOST_DEVICE auto compute_boundary_area_correction(const position_type &X, const shape_type &shape)
Compute the boundary area correction term for boundary integrals with a shape displacement field.
Compile-time alias for a dimension.
Definition: geometry.hpp:17
a class for representing a geometric region that can be used for integration
Definition: domain.hpp:33
these classes are a little confusing. These two special types represent the similar (but different) c...
A class for storing and access user-defined types at quadrature points.
SERAC_HOST_DEVICE auto operator()(double time, PositionType x, ShapeValueType shape_val, QFuncArgs... qfunc_args) const
integrand call
SERAC_HOST_DEVICE auto operator()(double time, PositionType x, ShapeValueType shape_val, QFuncArgs... qfunc_args) const
integrand call
SERAC_HOST_DEVICE auto operator()(double time, PositionType x, ShapeValueType shape_val, QFuncArgs... qfunc_args) const
integrand call
SERAC_HOST_DEVICE auto operator()(double time, PositionType x, StateType &state, ShapeValueType shape_val, QFuncArgs... qfunc_args) const
integrand call
SERAC_HOST_DEVICE auto operator()(double time, PositionType x, ShapeValueType shape_val, QFuncArgs... qfunc_args) const
integrand call
A helper struct that contains the appropriate parent-to-physical and physical-to-parent transformatio...
SERAC_HOST_DEVICE auto modify_trial_argument(space_type, const trial_type &u) const
Modify the trial argument using the correct physical to reference to shape-adjusted transformation fo...
SERAC_HOST_DEVICE ShapeCorrection(Dimension< dim >, shape_type p)
Construct a new Shape Correction object with the appropriate transformations for a shape field.
SERAC_HOST_DEVICE auto modify_shape_aware_qf_return(test_space, const q_func_type &v)
Modify the quadrature function return value using the correct shape-adjusted to reference transformat...
This is a class that mimics most of std::tuple's interface, except that it is usable in CUDA kernels ...
Definition: tuple.hpp:28