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
38ParameterMesh::ParameterMesh(const FEType & param_type,
39 const std::string & exodus_mesh,
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 _param_var_id(0),
47 _dof_map(nullptr),
48 _fe_type(param_type)
49{
52 _exodusII_io = std::make_unique<ExodusII_IO>(_mesh);
53 _exodusII_io->read(exodus_mesh);
54 _mesh.read(exodus_mesh);
55 // Create system to store parameter values
56 _eq = std::make_unique<libMesh::EquationSystems>(_mesh);
57 _sys = &_eq->add_system<ExplicitSystem>("_parameter_mesh_sys");
58 _sys->add_variable("_parameter_mesh_var", param_type);
59
60 // Create point locator
61 _point_locator = PointLocatorBase::build(TREE_LOCAL_ELEMENTS, _mesh);
62 _point_locator->enable_out_of_mesh_mode();
63
64 // Initialize the equations systems
65 _eq->init();
66
67 // getting number of parameter dofs for size() function
68 const unsigned short int var_id = _sys->variable_number("_parameter_mesh_var");
69 std::set<dof_id_type> var_indices;
70 _sys->local_dof_indices(var_id, var_indices);
71 _param_dofs = var_indices.size();
72
73 if (_find_closest)
74 {
75 for (const auto & elem : _mesh.element_ptr_range())
76 if (elem->default_order() != FIRST)
77 mooseError("Closet point projection currently does not support second order elements.");
78 }
79
80 // Initialize node-based KDTree optimization
81 _mesh_nodes.clear();
82 _node_to_elements.clear();
83
84 // Extract all node coordinates
85 for (const auto & node : _mesh.node_ptr_range())
86 _mesh_nodes.push_back(*node);
87
88 // Build node-to-elements connectivity map
89 for (const auto & elem : _mesh.element_ptr_range())
90 {
91 for (const auto n : make_range(elem->n_nodes()))
92 {
93 dof_id_type node_id = elem->node_id(n);
94 _node_to_elements[node_id].insert(elem);
95 }
96 }
97
98 // Create KDTree from node coordinates
99 if (!_mesh_nodes.empty())
100 _node_kdtree = std::make_unique<KDTree>(_mesh_nodes, 10);
101 // Update cached values for gradient computations
102 const_cast<unsigned short int &>(_param_var_id) = var_id;
103 const_cast<const libMesh::DofMap *&>(_dof_map) = &_sys->get_dof_map();
104 const_cast<FEType &>(_fe_type) = _dof_map->variable_type(_param_var_id);
105}
106
107void
109 std::vector<dof_id_type> & dof_indices,
110 std::vector<Real> & weights) const
111{
112 Point test_point = (_find_closest ? projectToMesh(pt) : pt);
113
114 const Elem * elem = (*_point_locator)(test_point);
115 if (!elem)
116 mooseError("No element was found to contain point ", test_point);
117
118 // Get the dof_indices for our element
119 // variable id is hard coded to _param_var_id
120 // this is probably the only variable in the ParameterMesh system used by ParameterMeshFunction
121 _dof_map->dof_indices(elem, dof_indices, _param_var_id);
122
123 // Map the physical co-ordinates to the reference co-ordinates
124 Point coor = FEMap::inverse_map(elem->dim(), elem, test_point);
125 // get the shape function value via the FEInterface
126 libMesh::FEComputeData fe_data(*_eq, coor);
127 FEInterface::compute_data(elem->dim(), _fe_type, elem, fe_data);
128 // Set weights to the value of the shape functions
129 weights = fe_data.shape;
130
131 if (dof_indices.size() != weights.size())
132 mooseError("Internal error: weights and DoF indices do not have the same size.");
133}
134
135void
137 std::vector<dof_id_type> & dof_indices,
138 std::vector<RealGradient> & weights) const
139{
140 if (!_sys->has_variable("_parameter_mesh_var"))
141 mooseError("Internal error: System being read does not contain _parameter_mesh_var.");
142 Point test_point = (_find_closest ? projectToMesh(pt) : pt);
143 // Locate the element the point is in
144 const Elem * elem = (*_point_locator)(test_point);
145
146 // Get the dof_indices for our element
147 // variable id is hard coded to _param_var_id
148 // this is probably the only variable in the ParameterMesh system used by ParameterMeshFunction
149 _dof_map->dof_indices(elem, dof_indices, _param_var_id);
150
151 // Map the physical co-ordinates to the reference co-ordinates
152 Point coor = FEMap::inverse_map(elem->dim(), elem, test_point);
153 // get the shape function value via the FEInterface
154 libMesh::FEComputeData fe_data(*_eq, coor);
155 fe_data.enable_derivative();
156 FEInterface::compute_data(elem->dim(), _fe_type, elem, fe_data);
157 // Set weights to the value of the shape functions
158 weights = fe_data.dshape;
159
160 if (dof_indices.size() != weights.size())
161 mooseError("Internal error: weights and DoF indices do not have the same size.");
162}
163
164Point
165ParameterMesh::projectToMesh(const Point & p) const
166{
167 // quick path: p already inside an element
168 if ((*_point_locator)(p))
169 return p;
170
171 // Lambda to find closest point from elements using squared distance for efficiency
172 auto findClosestElement = [&p, this](const auto & elements) -> Point
173 {
174 Real best_d2 = std::numeric_limits<Real>::max();
175 Point best_point = p;
176
177 for (const auto * elem : elements)
178 {
179 Point trial = closestPoint(*elem, p);
180 Real d2 = (trial - p).norm_sq();
181 if (d2 < best_d2)
182 {
183 best_d2 = d2;
184 best_point = trial;
185 }
186 }
187
188 if (best_d2 == std::numeric_limits<Real>::max())
189 mooseError("project_to_mesh failed - no candidate elements.");
190 return best_point;
191 };
192
193 // Use KDTree optimization if available
194 if (_node_kdtree && !_mesh_nodes.empty())
195 {
196 // Find K nearest nodes using KDTree
197 std::vector<std::size_t> nearest_node_indices;
198 _node_kdtree->neighborSearch(p, _kdtree_candidates, nearest_node_indices);
199
200 // Collect all elements connected to these nodes
201 std::set<const Elem *> candidate_elements;
202 for (auto node_idx : nearest_node_indices)
203 {
204 // Get the actual node from the mesh using the index
205 if (node_idx < _mesh.n_nodes())
206 {
207 const Node * node = _mesh.node_ptr(node_idx);
208 dof_id_type node_id = node->id();
209 auto it = _node_to_elements.find(node_id);
210 if (it != _node_to_elements.end())
211 {
212 const auto & connected_elems = it->second;
213 candidate_elements.insert(connected_elems.begin(), connected_elems.end());
214 }
215 }
216 }
217
218 // Convert set to vector for consistent type
219 std::vector<const Elem *> candidate_vector(candidate_elements.begin(),
220 candidate_elements.end());
221 return findClosestElement(candidate_vector);
222 }
223 else
224 {
225 // Fallback to original O(n) method if KDTree not available
226 std::vector<const Elem *> all_elements;
227 for (const auto & elem : _mesh.element_ptr_range())
228 all_elements.push_back(elem);
229
230 return findClosestElement(all_elements);
231 }
232}
233
234Point
235ParameterMesh::closestPoint(const Elem & elem, const Point & p) const
236{
237 mooseAssert(!elem.contains_point(p),
238 "Points inside of elements shouldn't need to find closestPoint.");
239
240 // Lambda to find closest point from range without storing temporary vectors
241 auto findClosest = [&p](auto range, auto point_func) -> Point
242 {
243 Real min_distance = std::numeric_limits<Real>::max();
244 Point min_point = p;
245
246 for (const auto & item : range)
247 {
248 Point candidate = point_func(item);
249 Real distance = (candidate - p).norm();
250 if (distance < min_distance)
251 {
252 min_distance = distance;
253 min_point = candidate;
254 }
255 }
256 return min_point;
257 };
258
259 switch (elem.type())
260 {
261 case EDGE2:
262 {
263 LineSegment ls(*(elem.node_ptr(0)), *(elem.node_ptr(1)));
264 return ls.closest_point(p);
265 }
266
267 case TRI3:
268 {
269 Point a = *(elem.node_ptr(0));
270 Point b = *(elem.node_ptr(1));
271 Point c = *(elem.node_ptr(2));
272 libMesh::Plane pl(a, b, c);
273 Point trial = pl.closest_point(p);
274 if (elem.contains_point(trial))
275 return trial;
276
277 return findClosest(make_range(elem.n_edges()),
278 [&](dof_id_type i) { return closestPoint(*elem.build_edge_ptr(i), p); });
279 }
280 case QUAD4:
281 {
282 Point a = *(elem.node_ptr(0));
283 Point b = *(elem.node_ptr(1));
284 Point c = *(elem.node_ptr(2));
285 Point d = *(elem.node_ptr(3));
286 libMesh::Plane pl1(a, b, c);
287 libMesh::Plane pl2(b, c, d);
288 Point trial1 = pl1.closest_point(p);
289 Point trial2 = pl2.closest_point(p);
290 if (!trial1.absolute_fuzzy_equals(trial2, TOLERANCE * TOLERANCE))
291 mooseError("Quad4 element is not coplanar");
292
293 if (elem.contains_point(trial1))
294 return trial1;
295
296 return findClosest(make_range(elem.n_edges()),
297 [&](dof_id_type i) { return closestPoint(*elem.build_edge_ptr(i), p); });
298 }
299
300 default:
301 {
302 if (elem.dim() == 3)
303 {
304 return findClosest(make_range(elem.n_sides()),
305 [&](dof_id_type i) { return closestPoint(*elem.build_side_ptr(i), p); });
306 }
307 else
308 {
309 mooseError("Unsupported element type ",
310 Utility::enum_to_string(elem.type()),
311 " for projection of parameter mesh.");
312 }
313 }
314 }
315}
316
317template <typename T>
318T
319ParameterMesh::computeRegularizationLoop(const std::vector<Real> & parameter_values,
320 RegularizationType reg_type) const
321{
322 if (parameter_values.size() != _param_dofs)
323 mooseError("Parameter values size (",
324 parameter_values.size(),
325 ") does not match mesh DOFs (",
327 ")");
328
329 T result;
330 if constexpr (std::is_same_v<T, Real>)
331 result = 0.0;
332 else if constexpr (std::is_same_v<T, std::vector<Real>>)
333 result.resize(_param_dofs, 0.0);
334
335 // Iterate over all elements in the mesh
336 for (const auto & elem : _mesh.element_ptr_range())
337 {
338 // Get DOF indices for this element
339 std::vector<dof_id_type> dof_indices;
340 _dof_map->dof_indices(elem, dof_indices, _param_var_id);
341
342 // Get quadrature rule for this element
343 const unsigned int dim = elem->dim();
344 QGauss qrule(dim, _fe_type.default_quadrature_order());
345
346 // Create finite element objects
347 std::unique_ptr<FEBase> fe(FEBase::build(dim, _fe_type));
348 fe->attach_quadrature_rule(&qrule);
349
350 // Request shape functions and derivatives before reinit
351 const std::vector<Real> & JxW = fe->get_JxW();
352 const std::vector<std::vector<Real>> & phi = fe->get_phi();
353 const std::vector<std::vector<RealGradient>> & dphi = fe->get_dphi();
354
355 // Reinitialize for current element
356 fe->reinit(elem);
357
358 for (const auto qp : make_range(qrule.n_points()))
359 {
360 if constexpr (std::is_same_v<T, Real>)
361 result +=
362 computeRegularizationQp(parameter_values, phi, dphi, qp, dof_indices, JxW, reg_type);
363 else if constexpr (std::is_same_v<T, std::vector<Real>>)
365 parameter_values, phi, dphi, qp, dof_indices, JxW, reg_type, result);
366 }
367 }
368
369 return result;
370}
371
372Real
373ParameterMesh::computeRegularizationObjective(const std::vector<Real> & parameter_values,
374 RegularizationType reg_type) const
375{
376 return computeRegularizationLoop<Real>(parameter_values, reg_type);
377}
378
379std::vector<Real>
380ParameterMesh::computeRegularizationGradient(const std::vector<Real> & parameter_values,
381 RegularizationType reg_type) const
382{
383 return computeRegularizationLoop<std::vector<Real>>(parameter_values, reg_type);
384}
385
386Real
387ParameterMesh::computeRegularizationQp(const std::vector<Real> & parameter_values,
388 const std::vector<std::vector<Real>> & /*phi*/,
389 const std::vector<std::vector<RealGradient>> & dphi,
390 const unsigned int qp,
391 const std::vector<dof_id_type> & dof_indices,
392 const std::vector<Real> & JxW,
393 RegularizationType reg_type) const
394{
395 Real objective_contribution = 0.0;
396
397 // Switch on regularization type
398 switch (reg_type)
399 {
401 {
402 // Compute parameter gradient at this quadrature point
403 RealGradient param_grad;
404 for (const auto i : index_range(dof_indices))
405 param_grad += parameter_values[dof_indices[i]] * dphi[i][qp];
406
407 // Add L2 norm squared of gradient for regularization
408 objective_contribution = param_grad.norm_sq() * JxW[qp];
409 break;
410 }
411 default:
412 mooseError("Unknown Regularization Type");
413 }
414
415 return objective_contribution;
416}
417
418void
419ParameterMesh::computeRegularizationGradientQp(const std::vector<Real> & parameter_values,
420 const std::vector<std::vector<Real>> & /*phi*/,
421 const std::vector<std::vector<RealGradient>> & dphi,
422 const unsigned int qp,
423 const std::vector<dof_id_type> & dof_indices,
424 const std::vector<Real> & JxW,
425 RegularizationType reg_type,
426 std::vector<Real> & gradient) const
427{
428 // Switch on regularization type
429 switch (reg_type)
430 {
432 {
433 // Compute parameter gradient at this quadrature point
434 RealGradient param_grad;
435 for (const auto i : index_range(dof_indices))
436 param_grad += parameter_values[dof_indices[i]] * dphi[i][qp];
437
438 // Compute gradient contribution: 2 * grad(p) * dphi_j
439 for (const auto j : index_range(dof_indices))
440 gradient[dof_indices[j]] += 2.0 * param_grad * dphi[j][qp] * JxW[qp];
441 break;
442 }
443
444 default:
445 mooseError("Unknown Regularization Type");
446 }
447}
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
std::vector< Gradient > dshape
std::vector< Number > shape
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
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
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
Real distance(const Point &p)