https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
38using 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{
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
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
109void
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
137void
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
166Point
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
236Point
237ParameterMesh::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 ",
313 " for projection of parameter mesh.");
314 }
315 }
316 }
317}
318
319template <typename T>
320T
321ParameterMesh::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 (",
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
374Real
375ParameterMesh::computeRegularizationObjective(const std::vector<Real> & parameter_values,
376 RegularizationType reg_type) const
377{
378 return computeRegularizationLoop<Real>(parameter_values, reg_type);
379}
380
381std::vector<Real>
382ParameterMesh::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
388Real
389ParameterMesh::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
420void
421ParameterMesh::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}
const Real p
const double T
void mooseError(Args &&... args)
unsigned int dim
Point closest_point(const Point &p) const
dof_id_type _param_dofs
RegularizationType
Enumerations for regularization computations.
libMesh::System * _sys
unsigned int _kdtree_candidates
const bool _find_closest
Find closest projection points.
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 ...
std::vector< Real > computeRegularizationGradient(const std::vector< Real > &parameter_values, RegularizationType reg_type) const
Computes regularization gradient for a given regularization type.
std::unordered_map< dof_id_type, std::set< const libMesh::Elem * > > _node_to_elements
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...
std::unique_ptr< libMesh::EquationSystems > _eq
std::unique_ptr< libMesh::ExodusII_IO > _exodusII_io
Point closestPoint(const Elem &elem, const Point &p) const
Find closest point on the element to the given point.
const unsigned short int _param_var_id
std::unique_ptr< libMesh::PointLocatorBase > _point_locator
T computeRegularizationLoop(const std::vector< Real > &parameter_values, RegularizationType reg_type) const
Template method containing the element loop for regularization computations.
Point projectToMesh(const Point &p) const
Returns the point on the parameter mesh that is projected from the test point.
libMesh::ReplicatedMesh _mesh
std::unique_ptr< KDTree > _node_kdtree
ParameterMesh(const libMesh::FEType &param_type, const std::string &exodus_mesh, const bool find_closest=false, const unsigned int kdtree_candidates=5)
Real computeRegularizationObjective(const std::vector< Real > &parameter_values, RegularizationType reg_type) const
Computes regularization objective value for a given regularization type.
const libMesh::DofMap * _dof_map
const libMesh::FEType _fe_type
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...
std::vector< Point > _mesh_nodes
Node-based KDTree optimization.
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
const FEType & variable_type(const unsigned int i) const
dof_id_type id() const
virtual bool contains_point(const Point &p, Real tol=TOLERANCE) const
virtual unsigned short dim() const=0
const Node * node_ptr(const unsigned int i) const
virtual ElemType type() const=0
virtual unsigned int n_edges() const=0
virtual unsigned int n_sides() const=0
std::vector< Gradient > dshape
std::vector< Number > shape
std::unique_ptr< FEGenericBase< Real > > build(const unsigned int dim, const FEType &fet)
static void compute_data(const unsigned int dim, const FEType &fe_t, const Elem *elem, FEComputeData &data)
static Point inverse_map(const unsigned int dim, const Elem *elem, const Point &p, const Real tolerance=TOLERANCE, const bool secure=true, const bool extra_checks=true)
Order default_quadrature_order() const
void allow_renumbering(bool allow)
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
virtual Point closest_point(const Point &p) const override
static std::unique_ptr< PointLocatorBase > build(PointLocatorType t, const MeshBase &mesh, const PointLocatorBase *master=nullptr)
unsigned int n_points() const
virtual const Node * node_ptr(const dof_id_type i) const override final
virtual dof_id_type n_nodes() const override final
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
void local_dof_indices(const unsigned int var, std::set< dof_id_type > &var_indices) const
unsigned int variable_number(std::string_view var) const
bool has_variable(std::string_view var) const
const DofMap & get_dof_map() const
bool absolute_fuzzy_equals(const TypeVector< Real > &rhs, Real tol=TOLERANCE) const
auto norm_sq() const
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false, bool skip_detect_interior_parents=false) override
std::string enum_to_string(const T e)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
auto index_range(const T &sizable)
static constexpr Real TOLERANCE
uint8_t dof_id_type
TREE_LOCAL_ELEMENTS
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
Real distance(const Point &p)