https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MultiAppNearestNodeTransfer.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
11
12// MOOSE includes
13#include "DisplacedProblem.h"
14#include "FEProblem.h"
15#include "MooseMesh.h"
16#include "MooseTypes.h"
17#include "MooseVariableFE.h"
19
20#include "libmesh/system.h"
21#include "libmesh/mesh_tools.h"
22#include "libmesh/id_types.h"
23#include "libmesh/parallel_algebra.h"
24#include "libmesh/dof_object.h"
25
26// TIMPI includes
27#include "timpi/parallel_sync.h"
28
30
33{
36 "Transfer the value to the target domain from the nearest node in the source domain.");
37
38 params.addParam<BoundaryName>(
39 "source_boundary",
40 "The boundary we are transferring from (if not specified, whole domain is used).");
41 params.addParam<std::vector<BoundaryName>>(
42 "target_boundary",
43 "The boundary we are transferring to (if not specified, whole domain is used).");
44 params.addParam<bool>("fixed_meshes",
45 false,
46 "Set to true when the meshes are not changing (ie, "
47 "no movement or adaptivity). This will cache "
48 "nearest node neighbors to greatly speed up the "
49 "transfer.");
50
52 return params;
53}
54
56 : MultiAppConservativeTransfer(parameters),
57 _fixed_meshes(getParam<bool>("fixed_meshes")),
58 _node_map(declareRestartableData<std::map<dof_id_type, Node *>>("node_map")),
59 _distance_map(declareRestartableData<std::map<dof_id_type, Real>>("distance_map")),
60 _neighbors_cached(declareRestartableData<bool>("neighbors_cached", false)),
61 _cached_froms(declareRestartableData<std::map<processor_id_type, std::vector<unsigned int>>>(
62 "cached_froms")),
63 _cached_dof_ids(declareRestartableData<std::map<processor_id_type, std::vector<dof_id_type>>>(
64 "cached_dof_ids")),
65 _cached_from_inds(
66 declareRestartableData<std::map<std::pair<unsigned int, dof_id_type>, unsigned int>>(
67 "cached_from_ids")),
68 _cached_qp_inds(
69 declareRestartableData<std::map<std::pair<unsigned int, dof_id_type>, unsigned int>>(
70 "cached_qp_inds"))
71{
72 mooseDeprecated("MultiAppNearestNodeTransfer is deprecated. Use "
73 "MultiAppGeneralFieldNearestNodeTransfer instead and adapt the parameters");
74
75 if (_to_var_names.size() != 1)
76 paramError("variable", " Support single to-variable only");
77
78 if (_from_var_names.size() != 1)
79 paramError("source_variable", " Support single from-variable only");
80}
81
82void
84{
85 TIME_SECTION(
86 "MultiAppNearestNodeTransfer::execute()", 5, "Transferring variables based on nearest nodes");
87
88 // Get the bounding boxes for the "from" domains.
89 std::vector<BoundingBox> bboxes;
90 if (isParamValid("source_boundary"))
91 {
92 if (_from_meshes.size())
93 {
94 const auto & sb = getParam<BoundaryName>("source_boundary");
96 paramError("source_boundary", "The boundary '", sb, "' was not found in the mesh");
97
98 bboxes = getFromBoundingBoxes(_from_meshes[0]->getBoundaryID(sb));
99 }
100 else
102 }
103 else
104 bboxes = getFromBoundingBoxes();
105
106 // Figure out how many "from" domains each processor owns.
107 std::vector<unsigned int> froms_per_proc = getFromsPerProc();
108
110 // For every point in the local "to" domain, figure out which "from" domains
111 // might contain its nearest neighbor, and send that point to the processors
112 // that own those "from" domains.
113 //
114 // How do we know which "from" domains might contain the nearest neighbor, you
115 // ask? Well, consider two "from" domains, A and B. If every point in A is
116 // closer than every point in B, then we know that B cannot possibly contain
117 // the nearest neighbor. Hence, we'll only check A for the nearest neighbor.
118 // We'll use the functions bboxMaxDistance and bboxMinDistance to figure out
119 // if every point in A is closer than every point in B.
121
122 // outgoing_qps = nodes/centroids we'll send to other processors.
123 // Map processor to quadrature points. We will send these points to remote processors
124 std::map<processor_id_type, std::vector<Point>> outgoing_qps;
125 // When we get results back, node_index_map will tell us which results go with
126 // which points
127 // <processor, <system_id, node_i>> --> point_id
128 std::map<processor_id_type, std::map<std::pair<unsigned int, dof_id_type>, dof_id_type>>
129 node_index_map;
130
132 {
133 for (unsigned int i_to = 0; i_to < _to_problems.size(); i_to++)
134 {
135 System * to_sys = find_sys(*_to_es[i_to], _to_var_name);
136 unsigned int sys_num = to_sys->number();
137 unsigned int var_num = to_sys->variable_number(_to_var_name);
138 MeshBase * to_mesh = &_to_meshes[i_to]->getMesh();
139 const auto to_global_num =
141 const auto & to_transform = *_to_transforms[to_global_num];
142 auto & fe_type = to_sys->variable_type(var_num);
143 bool is_constant = fe_type.order == CONSTANT;
144 bool is_to_nodal = fe_type.family == LAGRANGE;
145
146 // We support L2_LAGRANGE elemental variable with the first order
147 if (fe_type.order > FIRST && !is_to_nodal)
148 mooseError("We don't currently support second order or higher elemental variable ");
149
150 if (!is_to_nodal && isParamValid("target_boundary"))
151 paramWarning("target_boundary",
152 "Setting a target boundary is only valid for receiving "
153 "variables of the LAGRANGE basis");
154
155 if (is_to_nodal)
156 {
157 const std::vector<Node *> & target_local_nodes = getTargetLocalNodes(i_to);
158
159 // For error checking: keep track of all target_local_nodes
160 // which are successfully mapped to at least one domain where
161 // the nearest neighbor might be found.
162 std::set<Node *> local_nodes_found;
163
164 for (const auto & node : target_local_nodes)
165 {
166 // Skip this node if the variable has no dofs at it.
167 if (node->n_dofs(sys_num, var_num) < 1)
168 continue;
169
170 const auto transformed_node = to_transform(*node);
171
172 // Find which bboxes might have the nearest node to this point.
173 Real nearest_max_distance = std::numeric_limits<Real>::max();
174 for (const auto & bbox : bboxes)
175 {
176 Real distance = bboxMaxDistance(transformed_node, bbox);
177 if (distance < nearest_max_distance)
178 nearest_max_distance = distance;
179 }
180
181 unsigned int from0 = 0;
182 for (processor_id_type i_proc = 0; i_proc < n_processors();
183 from0 += froms_per_proc[i_proc], i_proc++)
184 {
185 for (unsigned int i_from = from0; i_from < from0 + froms_per_proc[i_proc]; i_from++)
186 {
187 Real distance = bboxMinDistance(transformed_node, bboxes[i_from]);
188
189 if (distance <= nearest_max_distance ||
190 bboxes[i_from].contains_point(transformed_node))
191 {
192 std::pair<unsigned int, dof_id_type> key(i_to, node->id());
193 // Record a local ID for each quadrature point
194 node_index_map[i_proc][key] = outgoing_qps[i_proc].size();
195 outgoing_qps[i_proc].push_back(transformed_node);
196 local_nodes_found.insert(node);
197 }
198 }
199 }
200 }
201
202 // By the time we get to here, we should have found at least
203 // one candidate BoundingBox for every node in the
204 // target_local_nodes array that has dofs for the current
205 // variable in the current System.
206 for (const auto & node : target_local_nodes)
207 if (node->n_dofs(sys_num, var_num) && !local_nodes_found.count(node))
208 mooseError("In ",
209 name(),
210 ": No candidate BoundingBoxes found for node ",
211 node->id(),
212 " at position ",
213 to_transform(*node));
214 }
215 else // Elemental
216 {
217 // For error checking: keep track of all local elements
218 // which are successfully mapped to at least one domain where
219 // the nearest neighbor might be found.
220 std::set<Elem *> local_elems_found;
221 std::vector<Point> points;
222 std::vector<dof_id_type> point_ids;
223 for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
224 {
225 // Skip this element if the variable has no dofs at it.
226 if (elem->n_dofs(sys_num, var_num) < 1)
227 continue;
228
229 points.clear();
230 point_ids.clear();
231 // For constant monomial, we take the centroid of element
232 if (is_constant)
233 {
234 points.push_back(to_transform(elem->vertex_average()));
235 point_ids.push_back(elem->id());
236 }
237
238 // For L2_LAGRANGE, we take all the nodes of element
239 else
240 for (auto & node : elem->node_ref_range())
241 {
242 points.push_back(to_transform(node));
243 point_ids.push_back(node.id());
244 }
245
246 unsigned int offset = 0;
247 for (auto & point : points)
248 {
249 // Find which bboxes might have the nearest node to this point.
250 Real nearest_max_distance = std::numeric_limits<Real>::max();
251 for (const auto & bbox : bboxes)
252 {
253 Real distance = bboxMaxDistance(point, bbox);
254 if (distance < nearest_max_distance)
255 nearest_max_distance = distance;
256 }
257
258 unsigned int from0 = 0;
259 for (processor_id_type i_proc = 0; i_proc < n_processors();
260 from0 += froms_per_proc[i_proc], i_proc++)
261 {
262 for (unsigned int i_from = from0; i_from < from0 + froms_per_proc[i_proc]; i_from++)
263 {
264 Real distance = bboxMinDistance(point, bboxes[i_from]);
265 if (distance <= nearest_max_distance || bboxes[i_from].contains_point(point))
266 {
267 std::pair<unsigned int, dof_id_type> key(
268 i_to,
269 point_ids[offset]); // Create an unique ID
270 // If this point already exist, we skip it
271 if (node_index_map[i_proc].find(key) != node_index_map[i_proc].end())
272 continue;
273 node_index_map[i_proc][key] = outgoing_qps[i_proc].size();
274 outgoing_qps[i_proc].push_back(point);
275 local_elems_found.insert(elem);
276 } // if distance
277 } // for i_from
278 } // for i_proc
279 offset++;
280 } // point
281 } // for elem
282
283 // Verify that we found at least one candidate bounding
284 // box for each local element with dofs for the current
285 // variable in the current System.
286 for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
287 if (elem->n_dofs(sys_num, var_num) && !local_elems_found.count(elem))
288 mooseError("In ",
289 name(),
290 ": No candidate BoundingBoxes found for Elem ",
291 elem->id(),
292 ", centroid = ",
293 to_transform(elem->vertex_average()));
294 }
295 }
296 }
297
299 // Send local node/centroid positions off to the other processors and take
300 // care of points sent to this processor. We'll need to check the points
301 // against all of the "from" domains that this processor owns. For each
302 // point, we'll find the nearest node, then we'll send the value at that node
303 // and the distance between the node and the point back to the processor that
304 // requested that point.
306
307 std::map<processor_id_type, std::vector<Real>> incoming_evals;
308
309 // Create these here so that they live the entire life of this function
310 // and are NOT reused per processor.
311 std::map<processor_id_type, std::vector<Real>> processor_outgoing_evals;
312
314 {
315 // Build an array of pointers to all of this processor's local entities (nodes or
316 // elements). We need to do this to avoid the expense of using LibMesh iterators.
317 // This step also takes care of limiting the search to boundary nodes, if
318 // applicable.
319 std::vector<std::vector<std::pair<Point, DofObject *>>> local_entities(
320 froms_per_proc[processor_id()]);
321
322 std::vector<std::vector<unsigned int>> local_comps(froms_per_proc[processor_id()]);
323
324 // Local array of all from Variable references
325 std::vector<std::reference_wrapper<MooseVariableFEBase>> _from_vars;
326
327 for (unsigned int i_local_from = 0; i_local_from < froms_per_proc[processor_id()];
328 i_local_from++)
329 {
330 MooseVariableFEBase & from_var = _from_problems[i_local_from]->getVariable(
332 auto & from_fe_type = from_var.feType();
333 bool is_constant = from_fe_type.order == CONSTANT;
334 bool is_to_nodal = from_fe_type.family == LAGRANGE;
335
336 // We support L2_LAGRANGE elemental variable with the first order
337 if (from_fe_type.order > FIRST && !is_to_nodal)
338 mooseError("We don't currently support second order or higher elemental variable ");
339
340 _from_vars.emplace_back(from_var);
342 local_entities[i_local_from],
343 local_comps[i_local_from],
344 is_to_nodal,
345 is_constant);
346 }
347
348 // Quadrature points I will receive from remote processors
349 std::map<processor_id_type, std::vector<Point>> incoming_qps;
350 auto qps_action_functor = [&incoming_qps](processor_id_type pid, const std::vector<Point> & qps)
351 {
352 // Quadrature points from processor 'pid'
353 auto & incoming_qps_from_pid = incoming_qps[pid];
354 // Store data for late use
355 incoming_qps_from_pid.reserve(incoming_qps_from_pid.size() + qps.size());
356 std::copy(qps.begin(), qps.end(), std::back_inserter(incoming_qps_from_pid));
357 };
358
359 Parallel::push_parallel_vector_data(comm(), outgoing_qps, qps_action_functor);
360
361 for (auto & qps : incoming_qps)
362 {
363 const processor_id_type pid = qps.first;
364
365 if (_fixed_meshes)
366 {
367 auto & froms = _cached_froms[pid];
368 froms.resize(qps.second.size());
369 std::fill(froms.begin(), froms.end(), libMesh::invalid_uint);
370
371 auto & dof_ids = _cached_dof_ids[pid];
372 dof_ids.resize(qps.second.size());
373 std::fill(dof_ids.begin(), dof_ids.end(), DofObject::invalid_id);
374 }
375
376 std::vector<Real> & outgoing_evals = processor_outgoing_evals[pid];
377 // Resize this vector to two times the size of the incoming_qps
378 // vector because we are going to store both the value from the nearest
379 // local node *and* the distance between the incoming_qp and that node
380 // for later comparison purposes.
381 outgoing_evals.resize(2 * qps.second.size());
382
383 for (std::size_t qp = 0; qp < qps.second.size(); qp++)
384 {
385 const Point & qpt = qps.second[qp];
386 outgoing_evals[2 * qp] = std::numeric_limits<Real>::max();
387 for (unsigned int i_local_from = 0; i_local_from < froms_per_proc[processor_id()];
388 i_local_from++)
389 {
390 MooseVariableFEBase & from_var = _from_vars[i_local_from];
391 System & from_sys = from_var.sys().system();
392 unsigned int from_sys_num = from_sys.number();
393 unsigned int from_var_num = from_sys.variable_number(from_var.name());
394 const auto from_global_num =
396 const auto & from_transform = *_from_transforms[from_global_num];
397
398 for (unsigned int i_node = 0; i_node < local_entities[i_local_from].size(); i_node++)
399 {
400 // Compute distance between the current incoming_qp to local node i_node. No need to
401 // transform the 'to' because we already did it
402 Real current_distance =
403 (qpt - from_transform(local_entities[i_local_from][i_node].first)).norm();
404
405 // If an incoming_qp is equally close to two or more local nodes, then
406 // the first one we test will "win", even though any of the others could
407 // also potentially be chosen instead... there's no way to decide among
408 // the set of all equidistant points.
409 //
410 // outgoing_evals[2 * qp] is the current closest distance between a local point and
411 // the incoming_qp.
412 if (current_distance < outgoing_evals[2 * qp])
413 {
414 // Assuming LAGRANGE!
415 if (local_entities[i_local_from][i_node].second->n_dofs(from_sys_num, from_var_num) >
416 0)
417 {
418 dof_id_type from_dof = local_entities[i_local_from][i_node].second->dof_number(
419 from_sys_num, from_var_num, local_comps[i_local_from][i_node]);
420
421 // The indexing of the outgoing_evals vector looks
422 // like [(distance, value), (distance, value), ...]
423 // for each incoming_qp. We only keep the value from
424 // the node with the smallest distance to the
425 // incoming_qp, and then we compare across all
426 // processors later and pick the closest one.
427 outgoing_evals[2 * qp] = current_distance;
428 outgoing_evals[2 * qp + 1] = (*from_sys.solution)(from_dof);
429
430 if (_fixed_meshes)
431 {
432 // Cache the nearest nodes.
433 _cached_froms[pid][qp] = i_local_from;
434 _cached_dof_ids[pid][qp] = from_dof;
435 }
436 }
437 }
438 }
439 }
440 }
441 }
442 }
443
444 else // We've cached the nearest nodes.
445 {
446 for (auto & problem_from : _cached_froms)
447 {
448 const processor_id_type pid = problem_from.first;
449 std::vector<Real> & outgoing_evals = processor_outgoing_evals[pid];
450 outgoing_evals.resize(problem_from.second.size());
451
452 for (unsigned int qp = 0; qp < outgoing_evals.size(); qp++)
453 {
454 const auto from_problem = problem_from.second[qp];
455 if (from_problem == libMesh::invalid_uint)
456 {
457 mooseAssert(_cached_dof_ids[pid][qp] == DofObject::invalid_id,
458 "The state of the from problem and dof id should match.");
459 continue;
460 }
461
462 MooseVariableFEBase & from_var =
463 _from_problems[from_problem]->getVariable(0,
467 System & from_sys = from_var.sys().system();
468 dof_id_type from_dof = _cached_dof_ids[pid][qp];
469 outgoing_evals[qp] = (*from_sys.solution)(from_dof);
470 }
471 }
472 }
473
474 auto evals_action_functor =
475 [&incoming_evals](processor_id_type pid, const std::vector<Real> & evals)
476 {
477 // evals for processor 'pid'
478 auto & incoming_evals_for_pid = incoming_evals[pid];
479 // Copy evals for late use
480 incoming_evals_for_pid.reserve(incoming_evals_for_pid.size() + evals.size());
481 std::copy(evals.begin(), evals.end(), std::back_inserter(incoming_evals_for_pid));
482 };
483
484 Parallel::push_parallel_vector_data(comm(), processor_outgoing_evals, evals_action_functor);
485
487 // Gather all of the evaluations, find the nearest one for each node/element,
488 // and apply the values.
490
491 for (unsigned int i_to = 0; i_to < _to_problems.size(); i_to++)
492 {
493 // Loop over the master nodes and set the value of the variable
494 System * to_sys = find_sys(*_to_es[i_to], _to_var_name);
495
496 unsigned int sys_num = to_sys->number();
497 unsigned int var_num = to_sys->variable_number(_to_var_name);
498
499 NumericVector<Real> * solution = nullptr;
500 switch (_current_direction)
501 {
502 case TO_MULTIAPP:
503 solution = &getTransferVector(i_to, _to_var_name);
504 break;
505 case FROM_MULTIAPP:
506 solution = to_sys->solution.get();
507 break;
508 default:
509 mooseError("Unknown direction");
510 }
511
512 const MeshBase & to_mesh = _to_meshes[i_to]->getMesh();
513
514 auto & fe_type = to_sys->variable_type(var_num);
515 bool is_constant = fe_type.order == CONSTANT;
516 bool is_to_nodal = fe_type.family == LAGRANGE;
517
518 // We support L2_LAGRANGE elemental variable with the first order
519 if (fe_type.order > FIRST && !is_to_nodal)
520 mooseError("We don't currently support second order or higher elemental variable ");
521
522 if (is_to_nodal)
523 {
524 const std::vector<Node *> & target_local_nodes = getTargetLocalNodes(i_to);
525
526 for (const auto & node : target_local_nodes)
527 {
528 // Skip this node if the variable has no dofs at it.
529 if (node->n_dofs(sys_num, var_num) < 1)
530 continue;
531
532 // If nothing is in the node_index_map for a given local node,
533 // it will get the value 0.
534 Real best_val = 0;
536 {
537 // Search through all the incoming evaluation points from
538 // different processors for the one with the closest
539 // point. If there are multiple values from other processors
540 // which are equidistant, the first one we check will "win".
541 Real min_dist = std::numeric_limits<Real>::max();
542 for (auto & evals : incoming_evals)
543 {
544 // processor Id
545 const processor_id_type pid = evals.first;
546 std::pair<unsigned int, dof_id_type> key(i_to, node->id());
547 if (node_index_map[pid].find(key) == node_index_map[pid].end())
548 continue;
549 unsigned int qp_ind = node_index_map[pid][key];
550 // Distances
551 if (evals.second[2 * qp_ind] >= min_dist)
552 continue;
553
554 // If we made it here, we are going to set a new value and
555 // distance because we found one that was closer.
556 min_dist = evals.second[2 * qp_ind];
557 best_val = evals.second[2 * qp_ind + 1];
558
559 if (_fixed_meshes)
560 {
561 // Cache these indices.
562 _cached_from_inds[std::make_pair(i_to, node->id())] = pid;
563 _cached_qp_inds[std::make_pair(i_to, node->id())] = qp_ind;
564 }
565 }
566 }
567
568 else
569 {
570 best_val = incoming_evals[_cached_from_inds[std::make_pair(i_to, node->id())]]
571 [_cached_qp_inds[std::make_pair(i_to, node->id())]];
572 }
573
574 dof_id_type dof = node->dof_number(sys_num, var_num, 0);
575 solution->set(dof, best_val);
576 }
577 }
578 else // Elemental
579 {
580 std::vector<Point> points;
581 std::vector<dof_id_type> point_ids;
582 for (auto & elem : to_mesh.active_local_element_ptr_range())
583 {
584 // Skip this element if the variable has no dofs at it.
585 if (elem->n_dofs(sys_num, var_num) < 1)
586 continue;
587
588 points.clear();
589 point_ids.clear();
590 // grap sample points
591 // for constant shape function, we take the element centroid
592 if (is_constant)
593 {
594 points.push_back(elem->vertex_average());
595 point_ids.push_back(elem->id());
596 }
597 // for higher order method, we take all nodes of element
598 // this works for the first order L2 Lagrange. Might not work
599 // with something higher than the first order
600 else
601 for (auto & node : elem->node_ref_range())
602 {
603 points.push_back(node);
604 point_ids.push_back(node.id());
605 }
606
607 unsigned int n_comp = elem->n_comp(sys_num, var_num);
608 // We assume each point corresponds to one component of elemental variable
609 if (points.size() != n_comp)
610 mooseError(" Number of points ",
611 points.size(),
612 " does not equal to number of variable components ",
613 n_comp);
614
615 for (MooseIndex(points) offset = 0; offset < points.size(); offset++)
616 {
617 dof_id_type point_id = point_ids[offset];
618 Real best_val = 0;
620 {
621 Real min_dist = std::numeric_limits<Real>::max();
622 for (auto & evals : incoming_evals)
623 {
624 const processor_id_type pid = evals.first;
625
626 std::pair<unsigned int, dof_id_type> key(i_to, point_id);
627 if (node_index_map[pid].find(key) == node_index_map[pid].end())
628 continue;
629
630 unsigned int qp_ind = node_index_map[pid][key];
631 if (evals.second[2 * qp_ind] >= min_dist)
632 continue;
633
634 min_dist = evals.second[2 * qp_ind];
635 best_val = evals.second[2 * qp_ind + 1];
636
637 if (_fixed_meshes)
638 {
639 // Cache these indices.
640 _cached_from_inds[std::make_pair(i_to, point_id)] = pid;
641 _cached_qp_inds[std::make_pair(i_to, point_id)] = qp_ind;
642 } // if _fixed_meshes
643 } // i_from
644 } //
645 else
646 {
647 best_val = incoming_evals[_cached_from_inds[std::make_pair(i_to, point_id)]]
648 [_cached_qp_inds[std::make_pair(i_to, point_id)]];
649 }
650 dof_id_type dof = elem->dof_number(sys_num, var_num, offset);
651 solution->set(dof, best_val);
652 } // for offset
653 }
654 }
655 solution->close();
656 to_sys->update();
657 }
658
659 if (_fixed_meshes)
660 _neighbors_cached = true;
661
662 postExecute();
663}
664
665Real
666MultiAppNearestNodeTransfer::bboxMaxDistance(const Point & p, const BoundingBox & bbox)
667{
668 std::array<Point, 2> source_points = {{bbox.first, bbox.second}};
669
670 std::array<Point, 8> all_points;
671 for (unsigned int x = 0; x < 2; x++)
672 for (unsigned int y = 0; y < 2; y++)
673 for (unsigned int z = 0; z < 2; z++)
674 all_points[x + 2 * y + 4 * z] =
675 Point(source_points[x](0), source_points[y](1), source_points[z](2));
676
677 Real max_distance = 0.;
678
679 for (unsigned int i = 0; i < 8; i++)
680 {
681 Real distance = (p - all_points[i]).norm();
682 if (distance > max_distance)
683 max_distance = distance;
684 }
685
686 return max_distance;
687}
688
689Real
690MultiAppNearestNodeTransfer::bboxMinDistance(const Point & p, const BoundingBox & bbox)
691{
692 std::array<Point, 2> source_points = {{bbox.first, bbox.second}};
693
694 std::array<Point, 8> all_points;
695 for (unsigned int x = 0; x < 2; x++)
696 for (unsigned int y = 0; y < 2; y++)
697 for (unsigned int z = 0; z < 2; z++)
698 all_points[x + 2 * y + 4 * z] =
699 Point(source_points[x](0), source_points[y](1), source_points[z](2));
700
701 Real min_distance = std::numeric_limits<Real>::max();
702
703 for (unsigned int i = 0; i < 8; i++)
704 {
705 Real distance = (p - all_points[i]).norm();
706 if (distance < min_distance)
707 min_distance = distance;
708 }
709
710 return min_distance;
711}
712
713void
715 MooseMesh * mesh,
716 std::vector<std::pair<Point, DofObject *>> & local_entities,
717 std::vector<unsigned int> & local_comps,
718 bool is_nodal,
719 bool is_constant)
720{
721 mooseAssert(mesh, "mesh should not be a nullptr");
722 mooseAssert(local_entities.empty(), "local_entities should be empty");
723 MeshBase & mesh_base = mesh->getMesh();
724
725 if (isParamValid("source_boundary"))
726 {
727 const auto & sb = getParam<BoundaryName>("source_boundary");
728 BoundaryID src_bnd_id = mesh->getBoundaryID(sb);
729 if (!MooseMeshUtils::hasBoundaryNameOrID(mesh_base, sb))
730 paramError("source_boundary", "The boundary '", sb, "' was not found in the mesh");
731
732 if (is_nodal)
733 {
734 const ConstBndNodeRange & bnd_nodes = *mesh->getBoundaryNodeRange();
735 for (const auto & bnode : bnd_nodes)
736 {
737 unsigned int comp = 0;
738 if (bnode->_bnd_id == src_bnd_id &&
739 bnode->_node->processor_id() == mesh_base.processor_id())
740 {
741 local_entities.emplace_back(*bnode->_node, bnode->_node);
742 local_comps.push_back(comp++);
743 }
744 }
745 }
746 else
747 {
748 const ConstBndElemRange & bnd_elems = *mesh->getBoundaryElementRange();
749 for (const auto & belem : bnd_elems)
750 {
751 unsigned int comp = 0;
752 if (belem->_bnd_id == src_bnd_id &&
753 belem->_elem->processor_id() == mesh_base.processor_id())
754 {
755 // CONSTANT Monomial
756 if (is_constant)
757 {
758 local_entities.emplace_back(belem->_elem->vertex_average(), belem->_elem);
759 local_comps.push_back(comp++);
760 }
761 // L2_LAGRANGE
762 else
763 {
764 for (auto & node : belem->_elem->node_ref_range())
765 {
766 local_entities.emplace_back(node, belem->_elem);
767 local_comps.push_back(comp++);
768 }
769 }
770 }
771 }
772 }
773 }
774 else
775 {
776 if (is_nodal)
777 {
778 local_entities.reserve(mesh_base.n_local_nodes());
779 for (auto & node : mesh_base.local_node_ptr_range())
780 {
781 unsigned int comp = 0;
782 local_entities.emplace_back(*node, node);
783 local_comps.push_back(comp++);
784 }
785 }
786 else
787 {
788 local_entities.reserve(mesh_base.n_local_elem());
789 for (auto & elem : mesh_base.active_local_element_ptr_range())
790 {
791 unsigned int comp = 0;
792 // CONSTANT Monomial
793 if (is_constant)
794 {
795 local_entities.emplace_back(elem->vertex_average(), elem);
796 local_comps.push_back(comp++);
797 }
798 // L2_LAGRANGE
799 else
800 {
801 for (auto & node : elem->node_ref_range())
802 {
803 local_entities.emplace_back(node, elem);
804 local_comps.push_back(comp++);
805 }
806 }
807 }
808 }
809 }
810}
811
812const std::vector<Node *> &
814{
815 _target_local_nodes.clear();
816 MeshBase & to_mesh = _to_meshes[to_problem_id]->getMesh();
817
818 if (isParamValid("target_boundary"))
819 {
820 const std::vector<BoundaryName> & target_boundaries =
821 getParam<std::vector<BoundaryName>>("target_boundary");
822 for (const auto & b : target_boundaries)
824 paramError("target_boundary", "The boundary '", b, "' was not found in the mesh");
825
826 ConstBndNodeRange & bnd_nodes = *(_to_meshes[to_problem_id])->getBoundaryNodeRange();
827
828 for (const auto & t : target_boundaries)
829 {
830 BoundaryID target_bnd_id = _to_meshes[to_problem_id]->getBoundaryID(t);
831
832 for (const auto & bnode : bnd_nodes)
833 if (bnode->_bnd_id == target_bnd_id &&
834 bnode->_node->processor_id() == _to_meshes[to_problem_id]->processor_id())
835 _target_local_nodes.push_back(bnode->_node);
836 }
837 }
838 else
839 {
840 _target_local_nodes.resize(to_mesh.n_local_nodes());
841 unsigned int i = 0;
842 for (auto & node : to_mesh.local_node_ptr_range())
843 _target_local_nodes[i++] = node;
844 }
845
846 return _target_local_nodes;
847}
boundary_id_type BoundaryID
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
void mooseDeprecated(Args &&... args)
Emit a deprecated code/feature message with the given stringified, concatenated args.
Definition MooseError.h:363
registerMooseObjectDeprecated("MooseApp", MultiAppNearestNodeTransfer, "12/31/2024 24:00")
void ErrorVector unsigned int
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump.
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition MooseBase.h:457
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
const libMesh::FEType & feType() const
Get the type of finite element object.
SystemBase & sys()
Get the system this variable is part of.
This class provides an interface for common operations on field variables of both FE and FV types wit...
Transfers variables on possibly different meshes while conserving a user defined property (Postproces...
VariableName _from_var_name
This values are used if a derived class only supports one variable.
const std::vector< VariableName > _from_var_names
Name of variables transferring from.
const std::vector< AuxVariableName > _to_var_names
Name of variables transferring to.
virtual void postExecute()
Add some extra work if necessary after execute().
Copy the value to the target domain from the nearest node in the source domain.
void getLocalEntitiesAndComponents(MooseMesh *mesh, std::vector< std::pair< Point, DofObject * > > &local_entities, std::vector< unsigned int > &local_comps, bool nodal, bool constant)
Get nearest node candidates.
std::map< processor_id_type, std::vector< dof_id_type > > & _cached_dof_ids
Real bboxMinDistance(const Point &p, const libMesh::BoundingBox &bbox)
Return the distance between the given point and the nearest corner of the given bounding box.
virtual void execute() override
Execute the transfer.
std::map< std::pair< unsigned int, dof_id_type >, unsigned int > & _cached_from_inds
const std::vector< Node * > & getTargetLocalNodes(const unsigned int to_problem_id)
Get the local nodes on the target boundary for the transfer.
std::map< processor_id_type, std::vector< unsigned int > > & _cached_froms
MultiAppNearestNodeTransfer(const InputParameters &parameters)
Real bboxMaxDistance(const Point &p, const libMesh::BoundingBox &bbox)
Return the distance between the given point and the farthest corner of the given bounding box.
std::vector< Node * > _target_local_nodes
Target local nodes for receiving a nodal variable.
std::map< std::pair< unsigned int, dof_id_type >, unsigned int > & _cached_qp_inds
bool _fixed_meshes
If true then node connections will be cached.
libMesh::NumericVector< Real > & getTransferVector(unsigned int i_local, std::string var_name)
If we are transferring to a multiapp, return the appropriate solution vector.
std::vector< unsigned int > _to_local2global_map
Given local app index, returns global app index.
std::vector< unsigned int > _from_local2global_map
Given local app index, returns global app index.
std::vector< MooseMesh * > _from_meshes
static void addBBoxFactorParam(InputParameters &params)
Add the bounding box factor parameter to the supplied input parameters.
std::vector< FEProblemBase * > _from_problems
std::vector< unsigned int > getFromsPerProc()
Return the number of "from" domains that each processor owns.
std::vector< libMesh::BoundingBox > getFromBoundingBoxes()
Return the bounding boxes of all the "from" domains, including all the domains not local to this proc...
std::vector< std::unique_ptr< MultiAppCoordTransform > > _from_transforms
std::vector< FEProblemBase * > _to_problems
std::vector< MooseMesh * > _to_meshes
std::vector< libMesh::EquationSystems * > _to_es
std::vector< std::unique_ptr< MultiAppCoordTransform > > _to_transforms
void paramWarning(const std::string &param, Args... args) const
virtual libMesh::System & system()=0
Get the reference to the libMesh system.
@ FROM_MULTIAPP
Definition Transfer.h:71
@ TO_MULTIAPP
Definition Transfer.h:70
MooseEnum _current_direction
Definition Transfer.h:106
static libMesh::System * find_sys(libMesh::EquationSystems &es, const std::string &var_name)
Small helper function for finding the system containing the variable.
Definition Transfer.C:91
OrderWrapper order
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
unsigned int number() const
MeshBase & mesh
bool hasBoundaryNameOrID(const MeshBase &mesh, const BoundaryName &name_or_id)
Whether a particular boundary name or ID exists in the mesh.
@ VAR_FIELD_STANDARD
Definition MooseTypes.h:777
const BoundaryID INVALID_BOUNDARY_ID
Definition MooseTypes.C:22
@ VAR_ANY
Definition MooseTypes.h:772
const unsigned int invalid_uint
Real distance(const Point &p)