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/dof_map.h"
20 
21 #include "libmesh/elem.h"
22 #include "libmesh/face_quad.h"
23 #include "libmesh/face_quad4.h"
24 #include "libmesh/fe_compute_data.h"
25 #include "libmesh/fe_interface.h"
26 #include "libmesh/id_types.h"
27 #include "libmesh/int_range.h"
28 #include "libmesh/libmesh_common.h"
29 #include "libmesh/numeric_vector.h"
30 #include "libmesh/explicit_system.h"
31 #include "libmesh/plane.h"
32 #include "libmesh/enum_to_string.h"
33 #include <memory>
34 
35 using namespace libMesh;
36 
38  const std::string & exodus_mesh,
39  const std::vector<std::string> & var_names,
40  const bool find_closest,
41  const unsigned int kdtree_candidates)
42  : _communicator(MPI_COMM_SELF),
43  _mesh(_communicator),
44  _find_closest(find_closest),
45  _kdtree_candidates(kdtree_candidates)
46 {
47  _mesh.allow_renumbering(false);
49  _exodusII_io = std::make_unique<ExodusII_IO>(_mesh);
50  _exodusII_io->read(exodus_mesh);
51  _mesh.read(exodus_mesh);
52  // Create system to store parameter values
53  _eq = std::make_unique<EquationSystems>(_mesh);
54  _sys = &_eq->add_system<ExplicitSystem>("_parameter_mesh_sys");
55  _sys->add_variable("_parameter_mesh_var", param_type);
56 
57  // Create point locator
58  _point_locator = PointLocatorBase::build(TREE_LOCAL_ELEMENTS, _mesh);
59  _point_locator->enable_out_of_mesh_mode();
60 
61  if (!var_names.empty())
62  {
63  // Make Exodus vars for equation system
64  const std::vector<std::string> & all_nodal(_exodusII_io->get_nodal_var_names());
65  const std::vector<std::string> & all_elemental(_exodusII_io->get_elem_var_names());
66 
67  std::vector<std::string> nodal_variables;
68  std::vector<std::string> elemental_variables;
69  for (const auto & var_name : var_names)
70  {
71  if (std::find(all_nodal.begin(), all_nodal.end(), var_name) != all_nodal.end())
72  nodal_variables.push_back(var_name);
73  if (std::find(all_elemental.begin(), all_elemental.end(), var_name) != all_elemental.end())
74  elemental_variables.push_back(var_name);
75  }
76  if ((elemental_variables.size() + nodal_variables.size()) != var_names.size())
77  {
78  std::string out("\n Parameter Group Variables Requested: ");
79  for (const auto & var : var_names)
80  out += var + " ";
81  out += "\n Exodus Nodal Variables: ";
82  for (const auto & var : all_nodal)
83  out += var + " ";
84  out += "\n Exodus Elemental Variables: ";
85  for (const auto & var : all_elemental)
86  out += var + " ";
87  mooseError("Exodus file did not contain all of the parameter names being intitialized.", out);
88  }
89 
90  if (!nodal_variables.empty() && !elemental_variables.empty())
91  {
92  std::string out("\n Parameter Group Nodal Variables: ");
93  for (const auto & var : nodal_variables)
94  out += var + " ";
95  out += "\n Parameter Group Elemental Variables: ";
96  for (const auto & var : elemental_variables)
97  out += var + " ";
98  mooseError("Parameter group contains nodal and elemental variables, this is "
99  "not allowed. ",
100  out);
101  }
102  // Add the parameter group ics and bounds to the system
103  // All parameters in a group will be the same type, only one of these loops will do anything
104  for (const auto & var_name : nodal_variables)
105  _sys->add_variable(var_name, param_type);
106  for (const auto & var_name : elemental_variables)
107  _sys->add_variable(var_name, param_type);
108  }
109  // Initialize the equations systems
110  _eq->init();
111 
112  const unsigned short int var_id = _sys->variable_number("_parameter_mesh_var");
113  std::set<dof_id_type> var_indices;
114  _sys->local_dof_indices(var_id, var_indices);
115  _param_dofs = var_indices.size();
116 
117  if (_find_closest)
118  {
119  for (const auto & elem : _mesh.element_ptr_range())
120  if (elem->default_order() != FIRST)
121  mooseError("Closet point projection currently does not support second order elements.");
122  }
123 
124  // Initialize node-based KDTree optimization
125  _mesh_nodes.clear();
126  _node_to_elements.clear();
127 
128  // Extract all node coordinates
129  for (const auto & node : _mesh.node_ptr_range())
130  _mesh_nodes.push_back(*node);
131 
132  // Build node-to-elements connectivity map
133  for (const auto & elem : _mesh.element_ptr_range())
134  {
135  for (const auto n : make_range(elem->n_nodes()))
136  {
137  dof_id_type node_id = elem->node_id(n);
138  _node_to_elements[node_id].insert(elem);
139  }
140  }
141 
142  // Create KDTree from node coordinates
143  if (!_mesh_nodes.empty())
144  _node_kdtree = std::make_unique<KDTree>(_mesh_nodes, 10);
145 }
146 
147 void
149  std::vector<dof_id_type> & dof_indices,
150  std::vector<Real> & weights) const
151 {
152  Point test_point = (_find_closest ? projectToMesh(pt) : pt);
153 
154  const Elem * elem = (*_point_locator)(test_point);
155  if (!elem)
156  mooseError("No element was found to contain point ", test_point);
157 
158  // Get the in the dof_indices for our element
159  // variable name is hard coded to _parameter_mesh_var
160  // this is probably the only variable in the ParameterMesh system used by ParameterMeshFunction
161  const unsigned short int var = _sys->variable_number("_parameter_mesh_var");
162  const DofMap & dof_map = _sys->get_dof_map();
163  dof_map.dof_indices(elem, dof_indices, var);
164 
165  // Map the physical co-ordinates to the reference co-ordinates
166  Point coor = FEMap::inverse_map(elem->dim(), elem, test_point);
167  // get the shape function value via the FEInterface
168  FEType fe_type = dof_map.variable_type(var);
169  FEComputeData fe_data(*_eq, coor);
170  FEInterface::compute_data(elem->dim(), fe_type, elem, fe_data);
171  // Set weights to the value of the shape functions
172  weights = fe_data.shape;
173 
174  if (dof_indices.size() != weights.size())
175  mooseError("Internal error: weights and DoF indices do not have the same size.");
176 }
177 
178 void
180  std::vector<dof_id_type> & dof_indices,
181  std::vector<RealGradient> & weights) const
182 {
183  if (!_sys->has_variable("_parameter_mesh_var"))
184  mooseError("Internal error: System being read does not contain _parameter_mesh_var.");
185  Point test_point = (_find_closest ? projectToMesh(pt) : pt);
186  // Locate the element the point is in
187  const Elem * elem = (*_point_locator)(test_point);
188 
189  // Get the in the dof_indices for our element
190  // variable name is hard coded to _parameter_mesh_var
191  // this is probably the only variable in the ParameterMesh system used by ParameterMeshFunction
192  const unsigned short int var = _sys->variable_number("_parameter_mesh_var");
193  const DofMap & dof_map = _sys->get_dof_map();
194  dof_map.dof_indices(elem, dof_indices, var);
195 
196  // Map the physical co-ordinates to the reference co-ordinates
197  Point coor = FEMap::inverse_map(elem->dim(), elem, test_point);
198  // get the shape function value via the FEInterface
199  FEType fe_type = dof_map.variable_type(var);
200  FEComputeData fe_data(*_eq, coor);
201  fe_data.enable_derivative();
202  FEInterface::compute_data(elem->dim(), fe_type, elem, fe_data);
203  // Set weights to the value of the shape functions
204  weights = fe_data.dshape;
205 
206  if (dof_indices.size() != weights.size())
207  mooseError("Internal error: weights and DoF indices do not have the same size.");
208 }
209 
210 std::vector<Real>
211 ParameterMesh::getParameterValues(std::string var_name, unsigned int time_step) const
212 {
213  if (!_sys->has_variable(var_name))
214  mooseError("Exodus file being read does not contain ", var_name, ".");
215  // get the exodus variable and put it into the equation system.
216  unsigned int step_to_read = _exodusII_io->get_num_time_steps();
217  if (time_step <= step_to_read)
218  step_to_read = time_step;
219  else if (time_step != std::numeric_limits<unsigned int>::max())
220  mooseError("Invalid value passed as \"time_step\". Expected a valid integer "
221  "less than ",
222  _exodusII_io->get_num_time_steps(),
223  ", received ",
224  time_step);
225 
226  // determine what kind of variable you are trying to read from mesh
227  const std::vector<std::string> & all_nodal(_exodusII_io->get_nodal_var_names());
228  const std::vector<std::string> & all_elemental(_exodusII_io->get_elem_var_names());
229  if (std::find(all_nodal.begin(), all_nodal.end(), var_name) != all_nodal.end())
230  _exodusII_io->copy_nodal_solution(*_sys, var_name, var_name, step_to_read);
231  else if (std::find(all_elemental.begin(), all_elemental.end(), var_name) != all_elemental.end())
232  _exodusII_io->copy_elemental_solution(*_sys, var_name, var_name, step_to_read);
233 
234  // Update the equations systems
235  _sys->update();
236 
237  const unsigned short int var_id = _sys->variable_number(var_name);
238  std::set<dof_id_type> var_indices;
239  _sys->local_dof_indices(var_id, var_indices); // Everything is local so this is fine
240  std::vector<dof_id_type> var_indices_vector(var_indices.begin(), var_indices.end());
241 
242  std::vector<Real> parameter_values;
243  _sys->solution->localize(parameter_values, var_indices_vector);
244  return parameter_values;
245 }
246 
247 Point
249 {
250  // quick path: p already inside an element
251  if ((*_point_locator)(p))
252  return p;
253 
254  // Lambda to find closest point from elements using squared distance for efficiency
255  auto findClosestElement = [&p, this](const auto & elements) -> Point
256  {
257  Real best_d2 = std::numeric_limits<Real>::max();
258  Point best_point = p;
259 
260  for (const auto * elem : elements)
261  {
262  Point trial = closestPoint(*elem, p);
263  Real d2 = (trial - p).norm_sq();
264  if (d2 < best_d2)
265  {
266  best_d2 = d2;
267  best_point = trial;
268  }
269  }
270 
271  if (best_d2 == std::numeric_limits<Real>::max())
272  mooseError("project_to_mesh failed - no candidate elements.");
273  return best_point;
274  };
275 
276  // Use KDTree optimization if available
277  if (_node_kdtree && !_mesh_nodes.empty())
278  {
279  // Find K nearest nodes using KDTree
280  std::vector<std::size_t> nearest_node_indices;
281  _node_kdtree->neighborSearch(p, _kdtree_candidates, nearest_node_indices);
282 
283  // Collect all elements connected to these nodes
284  std::set<const Elem *> candidate_elements;
285  for (auto node_idx : nearest_node_indices)
286  {
287  // Get the actual node from the mesh using the index
288  if (node_idx < _mesh.n_nodes())
289  {
290  const Node * node = _mesh.node_ptr(node_idx);
291  dof_id_type node_id = node->id();
292  auto it = _node_to_elements.find(node_id);
293  if (it != _node_to_elements.end())
294  {
295  const auto & connected_elems = it->second;
296  candidate_elements.insert(connected_elems.begin(), connected_elems.end());
297  }
298  }
299  }
300 
301  // Convert set to vector for consistent type
302  std::vector<const Elem *> candidate_vector(candidate_elements.begin(),
303  candidate_elements.end());
304  return findClosestElement(candidate_vector);
305  }
306  else
307  {
308  // Fallback to original O(n) method if KDTree not available
309  std::vector<const Elem *> all_elements;
310  for (const auto & elem : _mesh.element_ptr_range())
311  all_elements.push_back(elem);
312 
313  return findClosestElement(all_elements);
314  }
315 }
316 
317 Point
318 ParameterMesh::closestPoint(const Elem & elem, const Point & p) const
319 {
320  mooseAssert(!elem.contains_point(p),
321  "Points inside of elements shouldn't need to find closestPoint.");
322 
323  // Lambda to find closest point from range without storing temporary vectors
324  auto findClosest = [&p](auto range, auto point_func) -> Point
325  {
326  Real min_distance = std::numeric_limits<Real>::max();
327  Point min_point = p;
328 
329  for (const auto & item : range)
330  {
331  Point candidate = point_func(item);
332  Real distance = (candidate - p).norm();
333  if (distance < min_distance)
334  {
335  min_distance = distance;
336  min_point = candidate;
337  }
338  }
339  return min_point;
340  };
341 
342  switch (elem.type())
343  {
344  case EDGE2:
345  {
346  LineSegment ls(*(elem.node_ptr(0)), *(elem.node_ptr(1)));
347  return ls.closest_point(p);
348  }
349 
350  case TRI3:
351  {
352  Point a = *(elem.node_ptr(0));
353  Point b = *(elem.node_ptr(1));
354  Point c = *(elem.node_ptr(2));
355  Plane pl(a, b, c);
356  Point trial = pl.closest_point(p);
357  if (elem.contains_point(trial))
358  return trial;
359 
360  return findClosest(make_range(elem.n_edges()),
361  [&](dof_id_type i) { return closestPoint(*elem.build_edge_ptr(i), p); });
362  }
363  case QUAD4:
364  {
365  Point a = *(elem.node_ptr(0));
366  Point b = *(elem.node_ptr(1));
367  Point c = *(elem.node_ptr(2));
368  Point d = *(elem.node_ptr(3));
369  Plane pl1(a, b, c);
370  Plane pl2(b, c, d);
371  Point trial1 = pl1.closest_point(p);
372  Point trial2 = pl2.closest_point(p);
373  if (!trial1.absolute_fuzzy_equals(trial2, TOLERANCE * TOLERANCE))
374  mooseError("Quad4 element is not coplanar");
375 
376  if (elem.contains_point(trial1))
377  return trial1;
378 
379  return findClosest(make_range(elem.n_edges()),
380  [&](dof_id_type i) { return closestPoint(*elem.build_edge_ptr(i), p); });
381  }
382 
383  default:
384  {
385  if (elem.dim() == 3)
386  {
387  return findClosest(make_range(elem.n_sides()),
388  [&](dof_id_type i) { return closestPoint(*elem.build_side_ptr(i), p); });
389  }
390  else
391  {
392  mooseError("Unsupported element type ",
393  Utility::enum_to_string(elem.type()),
394  " for projection of parameter mesh.");
395  }
396  }
397  }
398 }
virtual dof_id_type n_nodes() const override final
void local_dof_indices(const unsigned int var, std::set< dof_id_type > &var_indices) const
libMesh::ReplicatedMesh _mesh
Definition: ParameterMesh.h:86
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)
std::unique_ptr< libMesh::ExodusII_IO > _exodusII_io
Definition: ParameterMesh.h:92
FIRST
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
const FEType & variable_type(const unsigned int c) const
const bool _find_closest
Find closest projection points.
Definition: ParameterMesh.h:88
std::unique_ptr< libMesh::PointLocatorBase > _point_locator
Definition: ParameterMesh.h:91
Point closest_point(const Point &p) const
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
libMesh::System * _sys
Definition: ParameterMesh.h:90
virtual bool contains_point(const Point &p, Real tol=TOLERANCE) const
dof_id_type id() const
virtual Point closest_point(const Point &p) const override
std::unique_ptr< NumericVector< Number > > solution
std::unordered_map< dof_id_type, std::set< const libMesh::Elem * > > _node_to_elements
Definition: ParameterMesh.h:99
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
virtual unsigned int n_edges() const=0
std::unique_ptr< libMesh::EquationSystems > _eq
Definition: ParameterMesh.h:89
auto norm(const T &a) -> decltype(std::abs(a))
ParameterMesh(const libMesh::FEType &param_type, const std::string &exodus_mesh, const std::vector< std::string > &var_names={}, const bool find_closest=false, const unsigned int kdtree_candidates=5)
Definition: ParameterMesh.C:37
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
Definition: ParameterMesh.h:98
virtual unsigned int n_sides() const=0
virtual void update()
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
Definition: ParameterMesh.h:94
OStreamProxy out
virtual const Node * node_ptr(const dof_id_type i) const override final
IntRange< T > make_range(T beg, T end)
TREE_LOCAL_ELEMENTS
std::vector< Point > _mesh_nodes
Node-based KDTree optimization.
Definition: ParameterMesh.h:97
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
std::vector< Real > getParameterValues(std::string var_name, unsigned int timestep) const
Initializes parameter data and sets bounds in the main optmiization application getParameterValues is...
Point projectToMesh(const Point &p) const
Returns the point on the parameter mesh that is projected from the test point.
uint8_t dof_id_type