https://mooseframework.inl.gov
ParameterMesh.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "LineSegment.h"
11 #include "MooseError.h"
12 #include "ParameterMesh.h"
13 
14 #include "libmesh/cell_hex8.h"
15 #include "libmesh/cell_hex.h"
16 #include "libmesh/edge_edge2.h"
17 #include "libmesh/enum_elem_type.h"
18 #include "libmesh/enum_point_locator_type.h"
19 #include "libmesh/int_range.h"
20 #include "libmesh/dof_map.h"
21 
22 #include "libmesh/elem.h"
23 #include "libmesh/face_quad.h"
24 #include "libmesh/face_quad4.h"
25 #include "libmesh/fe_compute_data.h"
26 #include "libmesh/fe_interface.h"
27 #include "libmesh/id_types.h"
28 #include "libmesh/int_range.h"
29 #include "libmesh/libmesh_common.h"
30 #include "libmesh/numeric_vector.h"
31 #include "libmesh/explicit_system.h"
32 #include "libmesh/plane.h"
33 #include "libmesh/enum_to_string.h"
34 #include <memory>
35 #include "libmesh/quadrature_gauss.h"
36 #include "libmesh/fe_base.h"
37 
38 using namespace libMesh;
39 
41  const std::string & exodus_mesh,
42  const bool find_closest,
43  const unsigned int kdtree_candidates)
44  : _communicator(MPI_COMM_SELF),
45  _mesh(_communicator),
46  _find_closest(find_closest),
47  _kdtree_candidates(kdtree_candidates),
48  _param_var_id(0),
49  _dof_map(nullptr),
50  _fe_type(param_type)
51 {
52  _mesh.allow_renumbering(false);
54  _exodusII_io = std::make_unique<ExodusII_IO>(_mesh);
55  _exodusII_io->read(exodus_mesh);
56  _mesh.read(exodus_mesh);
57  // Create system to store parameter values
58  _eq = std::make_unique<EquationSystems>(_mesh);
59  _sys = &_eq->add_system<ExplicitSystem>("_parameter_mesh_sys");
60  _sys->add_variable("_parameter_mesh_var", param_type);
61 
62  // Create point locator
63  _point_locator = PointLocatorBase::build(TREE_LOCAL_ELEMENTS, _mesh);
64  _point_locator->enable_out_of_mesh_mode();
65 
66  // Initialize the equations systems
67  _eq->init();
68 
69  // getting number of parameter dofs for size() function
70  const unsigned short int var_id = _sys->variable_number("_parameter_mesh_var");
71  std::set<dof_id_type> var_indices;
72  _sys->local_dof_indices(var_id, var_indices);
73  _param_dofs = var_indices.size();
74 
75  if (_find_closest)
76  {
77  for (const auto & elem : _mesh.element_ptr_range())
78  if (elem->default_order() != FIRST)
79  mooseError("Closet point projection currently does not support second order elements.");
80  }
81 
82  // Initialize node-based KDTree optimization
83  _mesh_nodes.clear();
84  _node_to_elements.clear();
85 
86  // Extract all node coordinates
87  for (const auto & node : _mesh.node_ptr_range())
88  _mesh_nodes.push_back(*node);
89 
90  // Build node-to-elements connectivity map
91  for (const auto & elem : _mesh.element_ptr_range())
92  {
93  for (const auto n : make_range(elem->n_nodes()))
94  {
95  dof_id_type node_id = elem->node_id(n);
96  _node_to_elements[node_id].insert(elem);
97  }
98  }
99 
100  // Create KDTree from node coordinates
101  if (!_mesh_nodes.empty())
102  _node_kdtree = std::make_unique<KDTree>(_mesh_nodes, 10);
103  // Update cached values for gradient computations
104  const_cast<unsigned short int &>(_param_var_id) = var_id;
105  const_cast<const DofMap *&>(_dof_map) = &_sys->get_dof_map();
107 }
108 
109 void
111  std::vector<dof_id_type> & dof_indices,
112  std::vector<Real> & weights) const
113 {
114  Point test_point = (_find_closest ? projectToMesh(pt) : pt);
115 
116  const Elem * elem = (*_point_locator)(test_point);
117  if (!elem)
118  mooseError("No element was found to contain point ", test_point);
119 
120  // Get the dof_indices for our element
121  // variable id is hard coded to _param_var_id
122  // this is probably the only variable in the ParameterMesh system used by ParameterMeshFunction
123  _dof_map->dof_indices(elem, dof_indices, _param_var_id);
124 
125  // Map the physical co-ordinates to the reference co-ordinates
126  Point coor = FEMap::inverse_map(elem->dim(), elem, test_point);
127  // get the shape function value via the FEInterface
128  FEComputeData fe_data(*_eq, coor);
129  FEInterface::compute_data(elem->dim(), _fe_type, elem, fe_data);
130  // Set weights to the value of the shape functions
131  weights = fe_data.shape;
132 
133  if (dof_indices.size() != weights.size())
134  mooseError("Internal error: weights and DoF indices do not have the same size.");
135 }
136 
137 void
139  std::vector<dof_id_type> & dof_indices,
140  std::vector<RealGradient> & weights) const
141 {
142  if (!_sys->has_variable("_parameter_mesh_var"))
143  mooseError("Internal error: System being read does not contain _parameter_mesh_var.");
144  Point test_point = (_find_closest ? projectToMesh(pt) : pt);
145  // Locate the element the point is in
146  const Elem * elem = (*_point_locator)(test_point);
147 
148  // Get the dof_indices for our element
149  // variable id is hard coded to _param_var_id
150  // this is probably the only variable in the ParameterMesh system used by ParameterMeshFunction
151  _dof_map->dof_indices(elem, dof_indices, _param_var_id);
152 
153  // Map the physical co-ordinates to the reference co-ordinates
154  Point coor = FEMap::inverse_map(elem->dim(), elem, test_point);
155  // get the shape function value via the FEInterface
156  FEComputeData fe_data(*_eq, coor);
157  fe_data.enable_derivative();
158  FEInterface::compute_data(elem->dim(), _fe_type, elem, fe_data);
159  // Set weights to the value of the shape functions
160  weights = fe_data.dshape;
161 
162  if (dof_indices.size() != weights.size())
163  mooseError("Internal error: weights and DoF indices do not have the same size.");
164 }
165 
166 Point
168 {
169  // quick path: p already inside an element
170  if ((*_point_locator)(p))
171  return p;
172 
173  // Lambda to find closest point from elements using squared distance for efficiency
174  auto findClosestElement = [&p, this](const auto & elements) -> Point
175  {
176  Real best_d2 = std::numeric_limits<Real>::max();
177  Point best_point = p;
178 
179  for (const auto * elem : elements)
180  {
181  Point trial = closestPoint(*elem, p);
182  Real d2 = (trial - p).norm_sq();
183  if (d2 < best_d2)
184  {
185  best_d2 = d2;
186  best_point = trial;
187  }
188  }
189 
190  if (best_d2 == std::numeric_limits<Real>::max())
191  mooseError("project_to_mesh failed - no candidate elements.");
192  return best_point;
193  };
194 
195  // Use KDTree optimization if available
196  if (_node_kdtree && !_mesh_nodes.empty())
197  {
198  // Find K nearest nodes using KDTree
199  std::vector<std::size_t> nearest_node_indices;
200  _node_kdtree->neighborSearch(p, _kdtree_candidates, nearest_node_indices);
201 
202  // Collect all elements connected to these nodes
203  std::set<const Elem *> candidate_elements;
204  for (auto node_idx : nearest_node_indices)
205  {
206  // Get the actual node from the mesh using the index
207  if (node_idx < _mesh.n_nodes())
208  {
209  const Node * node = _mesh.node_ptr(node_idx);
210  dof_id_type node_id = node->id();
211  auto it = _node_to_elements.find(node_id);
212  if (it != _node_to_elements.end())
213  {
214  const auto & connected_elems = it->second;
215  candidate_elements.insert(connected_elems.begin(), connected_elems.end());
216  }
217  }
218  }
219 
220  // Convert set to vector for consistent type
221  std::vector<const Elem *> candidate_vector(candidate_elements.begin(),
222  candidate_elements.end());
223  return findClosestElement(candidate_vector);
224  }
225  else
226  {
227  // Fallback to original O(n) method if KDTree not available
228  std::vector<const Elem *> all_elements;
229  for (const auto & elem : _mesh.element_ptr_range())
230  all_elements.push_back(elem);
231 
232  return findClosestElement(all_elements);
233  }
234 }
235 
236 Point
237 ParameterMesh::closestPoint(const Elem & elem, const Point & p) const
238 {
239  mooseAssert(!elem.contains_point(p),
240  "Points inside of elements shouldn't need to find closestPoint.");
241 
242  // Lambda to find closest point from range without storing temporary vectors
243  auto findClosest = [&p](auto range, auto point_func) -> Point
244  {
245  Real min_distance = std::numeric_limits<Real>::max();
246  Point min_point = p;
247 
248  for (const auto & item : range)
249  {
250  Point candidate = point_func(item);
251  Real distance = (candidate - p).norm();
252  if (distance < min_distance)
253  {
254  min_distance = distance;
255  min_point = candidate;
256  }
257  }
258  return min_point;
259  };
260 
261  switch (elem.type())
262  {
263  case EDGE2:
264  {
265  LineSegment ls(*(elem.node_ptr(0)), *(elem.node_ptr(1)));
266  return ls.closest_point(p);
267  }
268 
269  case TRI3:
270  {
271  Point a = *(elem.node_ptr(0));
272  Point b = *(elem.node_ptr(1));
273  Point c = *(elem.node_ptr(2));
274  Plane pl(a, b, c);
275  Point trial = pl.closest_point(p);
276  if (elem.contains_point(trial))
277  return trial;
278 
279  return findClosest(make_range(elem.n_edges()),
280  [&](dof_id_type i) { return closestPoint(*elem.build_edge_ptr(i), p); });
281  }
282  case QUAD4:
283  {
284  Point a = *(elem.node_ptr(0));
285  Point b = *(elem.node_ptr(1));
286  Point c = *(elem.node_ptr(2));
287  Point d = *(elem.node_ptr(3));
288  Plane pl1(a, b, c);
289  Plane pl2(b, c, d);
290  Point trial1 = pl1.closest_point(p);
291  Point trial2 = pl2.closest_point(p);
292  if (!trial1.absolute_fuzzy_equals(trial2, TOLERANCE * TOLERANCE))
293  mooseError("Quad4 element is not coplanar");
294 
295  if (elem.contains_point(trial1))
296  return trial1;
297 
298  return findClosest(make_range(elem.n_edges()),
299  [&](dof_id_type i) { return closestPoint(*elem.build_edge_ptr(i), p); });
300  }
301 
302  default:
303  {
304  if (elem.dim() == 3)
305  {
306  return findClosest(make_range(elem.n_sides()),
307  [&](dof_id_type i) { return closestPoint(*elem.build_side_ptr(i), p); });
308  }
309  else
310  {
311  mooseError("Unsupported element type ",
312  Utility::enum_to_string(elem.type()),
313  " for projection of parameter mesh.");
314  }
315  }
316  }
317 }
318 
319 template <typename T>
320 T
321 ParameterMesh::computeRegularizationLoop(const std::vector<Real> & parameter_values,
322  RegularizationType reg_type) const
323 {
324  if (parameter_values.size() != _param_dofs)
325  mooseError("Parameter values size (",
326  parameter_values.size(),
327  ") does not match mesh DOFs (",
328  _param_dofs,
329  ")");
330 
331  T result;
332  if constexpr (std::is_same_v<T, Real>)
333  result = 0.0;
334  else if constexpr (std::is_same_v<T, std::vector<Real>>)
335  result.resize(_param_dofs, 0.0);
336 
337  // Iterate over all elements in the mesh
338  for (const auto & elem : _mesh.element_ptr_range())
339  {
340  // Get DOF indices for this element
341  std::vector<dof_id_type> dof_indices;
342  _dof_map->dof_indices(elem, dof_indices, _param_var_id);
343 
344  // Get quadrature rule for this element
345  const unsigned int dim = elem->dim();
347 
348  // Create finite element objects
349  std::unique_ptr<FEBase> fe(FEBase::build(dim, _fe_type));
350  fe->attach_quadrature_rule(&qrule);
351 
352  // Request shape functions and derivatives before reinit
353  const std::vector<Real> & JxW = fe->get_JxW();
354  const std::vector<std::vector<Real>> & phi = fe->get_phi();
355  const std::vector<std::vector<RealGradient>> & dphi = fe->get_dphi();
356 
357  // Reinitialize for current element
358  fe->reinit(elem);
359 
360  for (const auto qp : make_range(qrule.n_points()))
361  {
362  if constexpr (std::is_same_v<T, Real>)
363  result +=
364  computeRegularizationQp(parameter_values, phi, dphi, qp, dof_indices, JxW, reg_type);
365  else if constexpr (std::is_same_v<T, std::vector<Real>>)
367  parameter_values, phi, dphi, qp, dof_indices, JxW, reg_type, result);
368  }
369  }
370 
371  return result;
372 }
373 
374 Real
375 ParameterMesh::computeRegularizationObjective(const std::vector<Real> & parameter_values,
376  RegularizationType reg_type) const
377 {
378  return computeRegularizationLoop<Real>(parameter_values, reg_type);
379 }
380 
381 std::vector<Real>
382 ParameterMesh::computeRegularizationGradient(const std::vector<Real> & parameter_values,
383  RegularizationType reg_type) const
384 {
385  return computeRegularizationLoop<std::vector<Real>>(parameter_values, reg_type);
386 }
387 
388 Real
389 ParameterMesh::computeRegularizationQp(const std::vector<Real> & parameter_values,
390  const std::vector<std::vector<Real>> & /*phi*/,
391  const std::vector<std::vector<RealGradient>> & dphi,
392  const unsigned int qp,
393  const std::vector<dof_id_type> & dof_indices,
394  const std::vector<Real> & JxW,
395  RegularizationType reg_type) const
396 {
397  Real objective_contribution = 0.0;
398 
399  // Switch on regularization type
400  switch (reg_type)
401  {
403  {
404  // Compute parameter gradient at this quadrature point
405  RealGradient param_grad;
406  for (const auto i : index_range(dof_indices))
407  param_grad += parameter_values[dof_indices[i]] * dphi[i][qp];
408 
409  // Add L2 norm squared of gradient for regularization
410  objective_contribution = param_grad.norm_sq() * JxW[qp];
411  break;
412  }
413  default:
414  mooseError("Unknown Regularization Type");
415  }
416 
417  return objective_contribution;
418 }
419 
420 void
421 ParameterMesh::computeRegularizationGradientQp(const std::vector<Real> & parameter_values,
422  const std::vector<std::vector<Real>> & /*phi*/,
423  const std::vector<std::vector<RealGradient>> & dphi,
424  const unsigned int qp,
425  const std::vector<dof_id_type> & dof_indices,
426  const std::vector<Real> & JxW,
427  RegularizationType reg_type,
428  std::vector<Real> & gradient) const
429 {
430  // Switch on regularization type
431  switch (reg_type)
432  {
434  {
435  // Compute parameter gradient at this quadrature point
436  RealGradient param_grad;
437  for (const auto i : index_range(dof_indices))
438  param_grad += parameter_values[dof_indices[i]] * dphi[i][qp];
439 
440  // Compute gradient contribution: 2 * grad(p) * dphi_j
441  for (const auto j : index_range(dof_indices))
442  gradient[dof_indices[j]] += 2.0 * param_grad * dphi[j][qp] * JxW[qp];
443  break;
444  }
445 
446  default:
447  mooseError("Unknown Regularization Type");
448  }
449 }
virtual dof_id_type n_nodes() const override final
const unsigned short int _param_var_id
void local_dof_indices(const unsigned int var, std::set< dof_id_type > &var_indices) const
libMesh::ReplicatedMesh _mesh
RegularizationType
Enumerations for regularization computations.
Definition: ParameterMesh.h:52
void allow_renumbering(bool allow)
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
void mooseError(Args &&... args)
unsigned int dim
std::unique_ptr< libMesh::ExodusII_IO > _exodusII_io
FIRST
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
ParameterMesh(const libMesh::FEType &param_type, const std::string &exodus_mesh, const bool find_closest=false, const unsigned int kdtree_candidates=5)
Definition: ParameterMesh.C:40
const bool _find_closest
Find closest projection points.
Order default_quadrature_order() const
std::unique_ptr< libMesh::PointLocatorBase > _point_locator
Point closest_point(const Point &p) const
T computeRegularizationLoop(const std::vector< Real > &parameter_values, RegularizationType reg_type) const
Template method containing the element loop for regularization computations.
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
Real distance(const Point &p)
TRI3
unsigned int variable_number(std::string_view var) const
QUAD4
bool has_variable(std::string_view var) const
auto norm_sq() const -> decltype(std::norm(Real()))
libMesh::System * _sys
virtual bool contains_point(const Point &p, Real tol=TOLERANCE) const
Real computeRegularizationQp(const std::vector< Real > &parameter_values, const std::vector< std::vector< Real >> &phi, const std::vector< std::vector< RealGradient >> &dphi, const unsigned int qp, const std::vector< dof_id_type > &dof_indices, const std::vector< Real > &JxW, RegularizationType reg_type) const
Compute regularization objective for a single quadrature point This is the main function users should...
dof_id_type id() const
virtual Point closest_point(const Point &p) const override
std::unordered_map< dof_id_type, std::set< const libMesh::Elem * > > _node_to_elements
const FEType & variable_type(const unsigned int i) const
std::vector< Real > computeRegularizationGradient(const std::vector< Real > &parameter_values, RegularizationType reg_type) const
Computes regularization gradient for a given regularization type.
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
unsigned int _kdtree_candidates
Real computeRegularizationObjective(const std::vector< Real > &parameter_values, RegularizationType reg_type) const
Computes regularization objective value for a given regularization type.
virtual unsigned int n_edges() const=0
std::unique_ptr< libMesh::EquationSystems > _eq
auto norm(const T &a) -> decltype(std::abs(a))
bool absolute_fuzzy_equals(const TypeVector< Real > &rhs, Real tol=TOLERANCE) const
Point closestPoint(const Elem &elem, const Point &p) const
Find closest point on the element to the given point.
std::unique_ptr< KDTree > _node_kdtree
void computeRegularizationGradientQp(const std::vector< Real > &parameter_values, const std::vector< std::vector< Real >> &phi, const std::vector< std::vector< RealGradient >> &dphi, const unsigned int qp, const std::vector< dof_id_type > &dof_indices, const std::vector< Real > &JxW, RegularizationType reg_type, std::vector< Real > &gradient) const
Compute regularization gradient for a single quadrature point This is the main function users should ...
virtual unsigned int n_sides() const=0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
EDGE2
virtual unsigned short dim() const=0
const Node * node_ptr(const unsigned int i) const
auto norm_sq(const T &a) -> decltype(std::norm(a))
dof_id_type _param_dofs
virtual const Node * node_ptr(const dof_id_type i) const override final
IntRange< T > make_range(T beg, T end)
TREE_LOCAL_ELEMENTS
const libMesh::DofMap * _dof_map
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
std::vector< Point > _mesh_nodes
Node-based KDTree optimization.
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false) override
virtual std::unique_ptr< Elem > build_edge_ptr(const unsigned int i)=0
void getIndexAndWeight(const Point &pt, std::vector< dof_id_type > &dof_indices, std::vector< Real > &weights) const
Interpolate parameters onto the computational mesh getIndexAndWeight is only used by ParameterMeshFun...
const DofMap & get_dof_map() const
virtual ElemType type() const=0
Point projectToMesh(const Point &p) const
Returns the point on the parameter mesh that is projected from the test point.
auto index_range(const T &sizable)
uint8_t dof_id_type
const libMesh::FEType _fe_type