Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
integral.hpp
1 // Copyright (c) 2019-2024, 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 
7 #pragma once
8 
9 #include <array>
10 #include <memory>
11 
12 #include "mfem.hpp"
13 
15 #include "serac/numerics/functional/geometric_factors.hpp"
16 #include "serac/numerics/functional/function_signature.hpp"
17 #include "serac/numerics/functional/domain_integral_kernels.hpp"
18 #include "serac/numerics/functional/boundary_integral_kernels.hpp"
19 #include "serac/numerics/functional/differentiate_wrt.hpp"
20 
21 namespace serac {
22 
24 struct Integral {
26  static constexpr std::size_t num_types = 2;
27 
37  Integral(const Domain& d, std::vector<uint32_t> trial_space_indices)
38  : domain_(d), active_trial_spaces_(trial_space_indices)
39  {
40  std::size_t num_trial_spaces = trial_space_indices.size();
41  evaluation_with_AD_.resize(num_trial_spaces);
42  jvp_.resize(num_trial_spaces);
43  element_gradient_.resize(num_trial_spaces);
44 
45  for (uint32_t i = 0; i < num_trial_spaces; i++) {
47  }
48  }
49 
65  void Mult(double t, const std::vector<mfem::BlockVector>& input_E, mfem::BlockVector& output_E,
66  uint32_t differentiation_index, bool update_state) const
67  {
68  output_E = 0.0;
69 
70  bool with_AD =
71  (functional_to_integral_index_.count(differentiation_index) > 0 && differentiation_index != NO_DIFFERENTIATION);
72  auto& kernels =
73  (with_AD) ? evaluation_with_AD_[functional_to_integral_index_.at(differentiation_index)] : evaluation_;
74  for (auto& [geometry, func] : kernels) {
75  std::vector<const double*> inputs(active_trial_spaces_.size());
76  for (std::size_t i = 0; i < active_trial_spaces_.size(); i++) {
77  inputs[i] = input_E[uint32_t(active_trial_spaces_[i])].GetBlock(geometry).Read();
78  }
79  func(t, inputs, output_E.GetBlock(geometry).ReadWrite(), update_state);
80  }
81  }
82 
93  void GradientMult(const mfem::BlockVector& input_E, mfem::BlockVector& output_E, uint32_t differentiation_index) const
94  {
95  output_E = 0.0;
96 
97  // if this integral actually depends on the specified variable
98  if (functional_to_integral_index_.count(differentiation_index) > 0) {
99  for (auto& [geometry, func] : jvp_[functional_to_integral_index_.at(differentiation_index)]) {
100  func(input_E.GetBlock(geometry).Read(), output_E.GetBlock(geometry).ReadWrite());
101  }
102  }
103  }
104 
112  void ComputeElementGradients(std::map<mfem::Geometry::Type, ExecArray<double, 3, ExecutionSpace::CPU> >& K_e,
113  uint32_t differentiation_index) const
114  {
115  // if this integral actually depends on the specified variable
116  if (functional_to_integral_index_.count(differentiation_index) > 0) {
117  for (auto& [geometry, func] : element_gradient_[functional_to_integral_index_.at(differentiation_index)]) {
118  func(view(K_e[geometry]));
119  }
120  }
121  }
122 
125 
127  using eval_func = std::function<void(double, const std::vector<const double*>&, double*, bool)>;
128 
130  std::map<mfem::Geometry::Type, eval_func> evaluation_;
131 
133  std::vector<std::map<mfem::Geometry::Type, eval_func> > evaluation_with_AD_;
134 
136  using jacobian_vector_product_func = std::function<void(const double*, double*)>;
137 
139  std::vector<std::map<mfem::Geometry::Type, jacobian_vector_product_func> > jvp_;
140 
143 
145  std::vector<std::map<mfem::Geometry::Type, grad_func> > element_gradient_;
146 
148  std::vector<uint32_t> active_trial_spaces_;
149 
164  std::map<uint32_t, uint32_t> functional_to_integral_index_;
165 
167  std::map<mfem::Geometry::Type, GeometricFactors> geometric_factors_;
168 };
169 
184 template <mfem::Geometry::Type geom, int Q, typename test, typename... trials, typename lambda_type,
185  typename qpt_data_type>
186 void generate_kernels(FunctionSignature<test(trials...)> s, Integral& integral, lambda_type&& qf,
187  std::shared_ptr<QuadratureData<qpt_data_type> > qdata)
188 {
189  integral.geometric_factors_[geom] = GeometricFactors(integral.domain_, Q, geom);
190  GeometricFactors& gf = integral.geometric_factors_[geom];
191  if (gf.num_elements == 0) return;
192 
193  const double* positions = gf.X.Read();
194  const double* jacobians = gf.J.Read();
195  const int* elements = &integral.domain_.get(geom)[0];
196  const uint32_t num_elements = uint32_t(gf.num_elements);
197  const uint32_t qpts_per_element = num_quadrature_points(geom, Q);
198 
199  std::shared_ptr<zero> dummy_derivatives;
200  integral.evaluation_[geom] = domain_integral::evaluation_kernel<NO_DIFFERENTIATION, Q, geom>(
201  s, qf, positions, jacobians, qdata, dummy_derivatives, elements, num_elements);
202 
203  constexpr std::size_t num_args = s.num_args;
204  [[maybe_unused]] static constexpr int dim = dimension_of(geom);
205  for_constexpr<num_args>([&](auto index) {
206  // allocate memory for the derivatives of the q-function at each quadrature point
207  //
208  // Note: ptrs' lifetime is managed in an unusual way! It is captured by-value in the
209  // action_of_gradient functor below to augment the reference count, and extend its lifetime to match
210  // that of the DomainIntegral that allocated it.
211  using derivative_type = decltype(domain_integral::get_derivative_type<index, dim, trials...>(qf, qpt_data_type{}));
212  auto ptr = accelerator::make_shared_array<ExecutionSpace::CPU, derivative_type>(num_elements * qpts_per_element);
213 
214  integral.evaluation_with_AD_[index][geom] = domain_integral::evaluation_kernel<index, Q, geom>(
215  s, qf, positions, jacobians, qdata, ptr, elements, num_elements);
216 
217  integral.jvp_[index][geom] =
218  domain_integral::jacobian_vector_product_kernel<index, Q, geom>(s, ptr, elements, num_elements);
219  integral.element_gradient_[index][geom] =
220  domain_integral::element_gradient_kernel<index, Q, geom>(s, ptr, elements, num_elements);
221  });
222 }
223 
238 template <typename s, int Q, int dim, typename lambda_type, typename qpt_data_type>
239 Integral MakeDomainIntegral(const Domain& domain, lambda_type&& qf,
240  std::shared_ptr<QuadratureData<qpt_data_type> > qdata,
241  std::vector<uint32_t> argument_indices)
242 {
243  FunctionSignature<s> signature;
244 
245  SLIC_ERROR_IF(domain.type_ != Domain::Type::Elements, "Error: trying to evaluate a domain integral over a boundary");
246 
247  Integral integral(domain, argument_indices);
248 
249  if constexpr (dim == 2) {
250  generate_kernels<mfem::Geometry::TRIANGLE, Q>(signature, integral, qf, qdata);
251  generate_kernels<mfem::Geometry::SQUARE, Q>(signature, integral, qf, qdata);
252  }
253 
254  if constexpr (dim == 3) {
255  generate_kernels<mfem::Geometry::TETRAHEDRON, Q>(signature, integral, qf, qdata);
256  generate_kernels<mfem::Geometry::CUBE, Q>(signature, integral, qf, qdata);
257  }
258 
259  return integral;
260 }
261 
275 template <mfem::Geometry::Type geom, int Q, typename test, typename... trials, typename lambda_type>
276 void generate_bdr_kernels(FunctionSignature<test(trials...)> s, Integral& integral, lambda_type&& qf)
277 {
278  integral.geometric_factors_[geom] = GeometricFactors(integral.domain_, Q, geom, FaceType::BOUNDARY);
279  GeometricFactors& gf = integral.geometric_factors_[geom];
280  if (gf.num_elements == 0) return;
281 
282  const double* positions = gf.X.Read();
283  const double* jacobians = gf.J.Read();
284  const uint32_t num_elements = uint32_t(gf.num_elements);
285  const uint32_t qpts_per_element = num_quadrature_points(geom, Q);
286  const int* elements = &gf.elements[0];
287 
288  std::shared_ptr<zero> dummy_derivatives;
289  integral.evaluation_[geom] = boundary_integral::evaluation_kernel<NO_DIFFERENTIATION, Q, geom>(
290  s, qf, positions, jacobians, dummy_derivatives, elements, num_elements);
291 
292  constexpr std::size_t num_args = s.num_args;
293  [[maybe_unused]] static constexpr int dim = dimension_of(geom);
294  for_constexpr<num_args>([&](auto index) {
295  // allocate memory for the derivatives of the q-function at each quadrature point
296  //
297  // Note: ptrs' lifetime is managed in an unusual way! It is captured by-value in the
298  // action_of_gradient functor below to augment the reference count, and extend its lifetime to match
299  // that of the boundaryIntegral that allocated it.
300  using derivative_type = decltype(boundary_integral::get_derivative_type<index, dim, trials...>(qf));
301  auto ptr = accelerator::make_shared_array<ExecutionSpace::CPU, derivative_type>(num_elements * qpts_per_element);
302 
303  integral.evaluation_with_AD_[index][geom] =
304  boundary_integral::evaluation_kernel<index, Q, geom>(s, qf, positions, jacobians, ptr, elements, num_elements);
305 
306  integral.jvp_[index][geom] =
307  boundary_integral::jacobian_vector_product_kernel<index, Q, geom>(s, ptr, elements, num_elements);
308  integral.element_gradient_[index][geom] =
309  boundary_integral::element_gradient_kernel<index, Q, geom>(s, ptr, elements, num_elements);
310  });
311 }
312 
327 template <typename s, int Q, int dim, typename lambda_type>
328 Integral MakeBoundaryIntegral(const Domain& domain, lambda_type&& qf, std::vector<uint32_t> argument_indices)
329 {
330  FunctionSignature<s> signature;
331 
332  SLIC_ERROR_IF(domain.type_ != Domain::Type::BoundaryElements,
333  "Error: trying to evaluate a boundary integral over a non-boundary domain of integration");
334 
335  Integral integral(domain, argument_indices);
336 
337  if constexpr (dim == 1) {
338  generate_bdr_kernels<mfem::Geometry::SEGMENT, Q>(signature, integral, qf);
339  }
340 
341  if constexpr (dim == 2) {
342  generate_bdr_kernels<mfem::Geometry::TRIANGLE, Q>(signature, integral, qf);
343  generate_bdr_kernels<mfem::Geometry::SQUARE, Q>(signature, integral, qf);
344  }
345 
346  return integral;
347 }
348 
349 } // namespace serac
This file contains the interface used for initializing/terminating any hardware accelerator-related f...
Accelerator functionality.
Definition: serac.cpp:38
Integral MakeBoundaryIntegral(const Domain &domain, lambda_type &&qf, std::vector< uint32_t > argument_indices)
function to generate kernels held by an Integral object of type "Boundary", for all element types
Definition: integral.hpp:328
constexpr int num_quadrature_points(mfem::Geometry::Type g, int Q)
return the number of quadrature points in a Gauss-Legendre rule with parameter "Q"
Definition: geometry.hpp:25
axom::Array< T, dim, detail::execution_to_memory_v< space > > ExecArray
Alias for an Array corresponding to a particular ExecutionSpace.
void generate_bdr_kernels(FunctionSignature< test(trials...)> s, Integral &integral, lambda_type &&qf)
function to generate kernels held by an Integral object of type "BoundaryDomain", with a specific ele...
Definition: integral.hpp:276
auto view(axom::Array< T, dim, space > &arr)
convenience function for creating a view of an axom::Array type
Integral MakeDomainIntegral(const Domain &domain, lambda_type &&qf, std::shared_ptr< QuadratureData< qpt_data_type > > qdata, std::vector< uint32_t > argument_indices)
function to generate kernels held by an Integral object of type "Domain", for all element types
Definition: integral.hpp:239
void generate_kernels(FunctionSignature< test(trials...)> s, Integral &integral, lambda_type &&qf, std::shared_ptr< QuadratureData< qpt_data_type > > qdata)
function to generate kernels held by an Integral object of type "Domain", with a specific element typ...
Definition: integral.hpp:186
axom::ArrayView< T, dim, detail::execution_to_memory_v< space > > ExecArrayView
Alias for an ArrayView corresponding to a particular ExecutionSpace.
constexpr int dimension_of(mfem::Geometry::Type g)
Returns the dimension of an element geometry.
Definition: geometry.hpp:49
a class for representing a geometric region that can be used for integration
Definition: domain.hpp:21
Type type_
whether the elements in this domain are on the boundary or not
Definition: domain.hpp:38
const std::vector< int > & get(mfem::Geometry::Type geom) const
get elements by geometry type
Definition: domain.hpp:115
a class that computes and stores positions and jacobians at each quadrature point
std::size_t num_elements
the number of elements in the domain
std::vector< int > elements
list of element indices that are part of the associated domain
mfem::Vector J
Jacobians of the element transformations at all quadrature points.
mfem::Vector X
Mapped (physical) coordinates of all quadrature points.
a class for representing a Integral calculations and their derivatives
Definition: integral.hpp:24
Domain domain_
information about which elements to integrate over
Definition: integral.hpp:124
std::vector< uint32_t > active_trial_spaces_
a list of the trial spaces that take part in this integrand
Definition: integral.hpp:148
std::map< uint32_t, uint32_t > functional_to_integral_index_
a way of translating between the indices used by Functional and Integral to refer to the same trial s...
Definition: integral.hpp:164
std::vector< std::map< mfem::Geometry::Type, eval_func > > evaluation_with_AD_
kernels for integral evaluation + derivative w.r.t. specified argument over each type of element
Definition: integral.hpp:133
std::map< mfem::Geometry::Type, GeometricFactors > geometric_factors_
the spatial positions and jacobians (dx_dxi) for each element type and quadrature point
Definition: integral.hpp:167
std::function< void(ExecArrayView< double, 3, ExecutionSpace::CPU >)> grad_func
signature of element gradient kernel
Definition: integral.hpp:142
static constexpr std::size_t num_types
the number of different kinds of integration domains
Definition: integral.hpp:26
std::function< void(const double *, double *)> jacobian_vector_product_func
signature of element jvp kernel
Definition: integral.hpp:136
Integral(const Domain &d, std::vector< uint32_t > trial_space_indices)
Construct an "empty" Integral object, whose kernels are to be initialized later in one of the Make***...
Definition: integral.hpp:37
std::vector< std::map< mfem::Geometry::Type, grad_func > > element_gradient_
kernels for calculation of element jacobians
Definition: integral.hpp:145
void ComputeElementGradients(std::map< mfem::Geometry::Type, ExecArray< double, 3, ExecutionSpace::CPU > > &K_e, uint32_t differentiation_index) const
evaluate the jacobian (with respect to some trial space) of this integral
Definition: integral.hpp:112
std::map< mfem::Geometry::Type, eval_func > evaluation_
kernels for integral evaluation over each type of element
Definition: integral.hpp:130
std::vector< std::map< mfem::Geometry::Type, jacobian_vector_product_func > > jvp_
kernels for jacobian-vector product of integral calculation
Definition: integral.hpp:139
void Mult(double t, const std::vector< mfem::BlockVector > &input_E, mfem::BlockVector &output_E, uint32_t differentiation_index, bool update_state) const
evaluate the integral, optionally storing q-function derivatives with respect to a specific trial spa...
Definition: integral.hpp:65
std::function< void(double, const std::vector< const double * > &, double *, bool)> eval_func
signature of integral evaluation kernel
Definition: integral.hpp:127
void GradientMult(const mfem::BlockVector &input_E, mfem::BlockVector &output_E, uint32_t differentiation_index) const
evaluate the jacobian(with respect to some trial space)-vector product of this integral
Definition: integral.hpp:93
A class for storing and access user-defined types at quadrature points.