https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MultiAppShapeEvaluationTransfer.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/meshfree_interpolation.h"
21#include "libmesh/system.h"
22#include "libmesh/mesh_function.h"
23#include "libmesh/mesh_tools.h"
24#include "libmesh/parallel_algebra.h" // for communicator send and receive stuff
25
26// TIMPI includes
27#include "timpi/communicator.h"
28#include "timpi/parallel_sync.h"
29
32 MultiAppMeshFunctionTransfer,
33 "12/31/2023 24:00",
35
38{
41 "Transfers field data at the MultiApp position using solution the finite element function "
42 "from the main/parent application, via a 'libMesh::MeshFunction' object.");
43
44 params.addParam<bool>(
45 "error_on_miss",
46 false,
47 "Whether or not to error in the case that a target point is not found in the source domain.");
49 return params;
50}
51
53 : MultiAppConservativeTransfer(parameters), _error_on_miss(getParam<bool>("error_on_miss"))
54{
55 mooseDeprecated("MultiAppShapeEvaluationTransfer is deprecated. Use "
56 "MultiAppGeneralFieldShapeEvaluationTransfer instead and adapt the parameters");
57
58 if (_to_var_names.size() == _from_var_names.size())
59 _var_size = _to_var_names.size();
60 else
61 paramError("variable", "The number of variables to transfer to and from should be equal");
62}
63
64void
66{
67 TIME_SECTION("MultiAppShapeEvaluationTransfer::execute()",
68 5,
69 "Transferring variables via finite element interpolation");
70
71 // loop over the vector of variables and make the transfer one by one
72 for (unsigned int i = 0; i < _var_size; ++i)
74
76}
77
78void
80{
81 mooseAssert(i < _var_size, "The variable of index " << i << " does not exist");
82
91 // Get the bounding boxes for the "from" domains.
92 std::vector<BoundingBox> bboxes = getFromBoundingBoxes();
93
94 // Figure out how many "from" domains each processor owns.
95 std::vector<unsigned int> froms_per_proc = getFromsPerProc();
96
97 // Point locations needed to send to from-domain
98 // processor to points
99 std::map<processor_id_type, std::vector<Point>> outgoing_points;
100 // <processor, <system_id, node_i>> --> point_id
101 std::map<processor_id_type, std::map<std::pair<unsigned int, dof_id_type>, dof_id_type>>
102 point_index_map;
103
104 for (unsigned int i_to = 0; i_to < _to_problems.size(); ++i_to)
105 {
106 System * to_sys = find_sys(*_to_es[i_to], _to_var_names[i]);
107 unsigned int sys_num = to_sys->number();
108 unsigned int var_num = to_sys->variable_number(_to_var_names[i]);
109 MeshBase * to_mesh = &_to_meshes[i_to]->getMesh();
110 auto & fe_type = to_sys->variable_type(var_num);
111 bool is_constant = fe_type.order == CONSTANT;
112 bool is_nodal = fe_type.family == LAGRANGE;
113 const auto to_global_num = _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
114 const auto & to_transform = *_to_transforms[to_global_num];
115
116 if (fe_type.order > FIRST && !is_nodal)
117 mooseError("We don't currently support second order or higher elemental variable.");
118
119 if (is_nodal)
120 {
121 for (const auto & node : to_mesh->local_node_ptr_range())
122 {
123 // Skip this node if the variable has no dofs at it.
124 if (node->n_dofs(sys_num, var_num) < 1)
125 continue;
126
127 // Loop over the "froms" on processor i_proc. If the node is found in
128 // any of the "froms", add that node to the vector that will be sent to
129 // i_proc.
130 unsigned int from0 = 0;
131 for (processor_id_type i_proc = 0; i_proc < n_processors();
132 from0 += froms_per_proc[i_proc], ++i_proc)
133 {
134 bool point_found = false;
135 for (unsigned int i_from = from0; i_from < from0 + froms_per_proc[i_proc] && !point_found;
136 ++i_from)
137 {
138 auto transformed_node = to_transform(*node);
139 if (bboxes[i_from].contains_point(transformed_node))
140 {
141 // <system id, node id>
142 std::pair<unsigned int, dof_id_type> key(i_to, node->id());
143 // map a tuple of pid, problem id and node id to point id
144 // point id is counted from zero
145 point_index_map[i_proc][key] = outgoing_points[i_proc].size();
146 // map pid to points
147 outgoing_points[i_proc].push_back(std::move(transformed_node));
148 point_found = true;
149 }
150 }
151 }
152 }
153 }
154 else // Elemental
155 {
156 std::vector<Point> points;
157 std::vector<dof_id_type> point_ids;
158 for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
159 {
160 // Skip this element if the variable has no dofs at it.
161 if (elem->n_dofs(sys_num, var_num) < 1)
162 continue;
163
164 points.clear();
165 point_ids.clear();
166 // grab sample points
167 // for constant shape function, we take the element centroid
168 if (is_constant)
169 {
170 points.push_back(elem->vertex_average());
171 point_ids.push_back(elem->id());
172 }
173
174 // for higher order method, we take all nodes of element
175 // this works for the first order L2 Lagrange.
176 else
177 for (auto & node : elem->node_ref_range())
178 {
179 points.push_back(node);
180 point_ids.push_back(node.id());
181 }
182
183 unsigned int offset = 0;
184 for (auto & point : points)
185 {
186 // Loop over the "froms" on processor i_proc. If the elem is found in
187 // any of the "froms", add that elem to the vector that will be sent to
188 // i_proc.
189 unsigned int from0 = 0;
190 for (processor_id_type i_proc = 0; i_proc < n_processors();
191 from0 += froms_per_proc[i_proc], ++i_proc)
192 {
193 bool point_found = false;
194 for (unsigned int i_from = from0;
195 i_from < from0 + froms_per_proc[i_proc] && !point_found;
196 ++i_from)
197 {
198 auto transformed_point = to_transform(point);
199 if (bboxes[i_from].contains_point(transformed_point))
200 {
201 std::pair<unsigned int, dof_id_type> key(i_to, point_ids[offset]);
202 if (point_index_map[i_proc].find(key) != point_index_map[i_proc].end())
203 continue;
204
205 point_index_map[i_proc][key] = outgoing_points[i_proc].size();
206 outgoing_points[i_proc].push_back(std::move(transformed_point));
207 point_found = true;
208 } // if
209 } // i_from
210 } // i_proc
211 offset++;
212 } // point
213
214 } // else
215 }
216 }
217
218 // Get the local bounding boxes for current processor.
219 // There could be more than one box because of the number of local apps
220 // can be larger than one
221 std::vector<BoundingBox> local_bboxes(froms_per_proc[processor_id()]);
222 {
223 // Find the index to the first of this processor's local bounding boxes.
224 unsigned int local_start = 0;
225 for (processor_id_type i_proc = 0; i_proc < n_processors() && i_proc != processor_id();
226 ++i_proc)
227 {
228 local_start += froms_per_proc[i_proc];
229 }
230
231 // Extract the local bounding boxes.
232 for (unsigned int i_from = 0; i_from < froms_per_proc[processor_id()]; ++i_from)
233 {
234 local_bboxes[i_from] = bboxes[local_start + i_from];
235 }
236 }
237
238 // Setup the local mesh functions.
239 std::vector<libMesh::MeshFunction> local_meshfuns;
240 local_meshfuns.reserve(_from_problems.size());
241 for (unsigned int i_from = 0; i_from < _from_problems.size(); ++i_from)
242 {
243 FEProblemBase & from_problem = *_from_problems[i_from];
244 MooseVariableFEBase & from_var =
245 from_problem.getVariable(0,
249 System & from_sys = from_var.sys().system();
250 unsigned int from_var_num = from_sys.variable_number(from_var.name());
251
252 local_meshfuns.emplace_back(getEquationSystem(from_problem, _displaced_source_mesh),
253 *from_sys.current_local_solution,
254 from_sys.get_dof_map(),
255 from_var_num);
256 local_meshfuns.back().init();
257 local_meshfuns.back().enable_out_of_mesh_mode(OutOfMeshValue);
258 }
259
267 // Fill values and app ids for incoming points
268 // We are responsible to compute values for these incoming points
269 auto gather_functor =
270 [this, &local_meshfuns, &local_bboxes](
271 processor_id_type /*pid*/,
272 const std::vector<Point> & incoming_points,
273 std::vector<std::pair<Real, unsigned int>> & vals_ids_for_incoming_points)
274 {
275 vals_ids_for_incoming_points.resize(incoming_points.size(), std::make_pair(OutOfMeshValue, 0));
276 for (MooseIndex(incoming_points.size()) i_pt = 0; i_pt < incoming_points.size(); ++i_pt)
277 {
278 Point pt = incoming_points[i_pt];
279
280 // Loop until we've found the lowest-ranked app that actually contains
281 // the quadrature point.
282 for (MooseIndex(_from_problems.size()) i_from = 0;
283 i_from < _from_problems.size() &&
284 vals_ids_for_incoming_points[i_pt].first == OutOfMeshValue;
285 ++i_from)
286 {
287 if (local_bboxes[i_from].contains_point(pt))
288 {
289 // Use mesh function to compute interpolation values
290 vals_ids_for_incoming_points[i_pt].first = (local_meshfuns[i_from])(
291 getPointInSourceAppFrame(pt, i_from, "Shape evaluation transfer"));
292 // Record problem ID as well
293 switch (_current_direction)
294 {
295 case FROM_MULTIAPP:
296 vals_ids_for_incoming_points[i_pt].second = _from_local2global_map[i_from];
297 break;
298 case TO_MULTIAPP:
299 vals_ids_for_incoming_points[i_pt].second = _to_local2global_map[i_from];
300 break;
301 default:
302 mooseError("Unsupported direction");
303 }
304 }
305 }
306 }
307 };
308
309 // Incoming values and APP ids for outgoing points
310 std::map<processor_id_type, std::vector<std::pair<Real, unsigned int>>> incoming_vals_ids;
311 // Copy data out to incoming_vals_ids
312 auto action_functor =
313 [&incoming_vals_ids](
314 processor_id_type pid,
315 const std::vector<Point> & /*my_outgoing_points*/,
316 const std::vector<std::pair<Real, unsigned int>> & vals_ids_for_outgoing_points)
317 {
318 // This lambda function might be called multiple times
319 incoming_vals_ids[pid].reserve(vals_ids_for_outgoing_points.size());
320 // Copy data for processor 'pid'
321 std::copy(vals_ids_for_outgoing_points.begin(),
322 vals_ids_for_outgoing_points.end(),
323 std::back_inserter(incoming_vals_ids[pid]));
324 };
325
326 // We assume incoming_vals_ids is ordered in the same way as outgoing_points
327 // Hopefully, pull_parallel_vector_data will not mess up this
328 const std::pair<Real, unsigned int> * ex = nullptr;
329 libMesh::Parallel::pull_parallel_vector_data(
330 comm(), outgoing_points, gather_functor, action_functor, ex);
331
332 for (unsigned int i_to = 0; i_to < _to_problems.size(); ++i_to)
333 {
334 const auto to_global_num = _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
335 System * to_sys = find_sys(*_to_es[i_to], _to_var_names[i]);
336
337 unsigned int sys_num = to_sys->number();
338 unsigned int var_num = to_sys->variable_number(_to_var_names[i]);
339
340 NumericVector<Real> * solution = nullptr;
341 switch (_current_direction)
342 {
343 case TO_MULTIAPP:
344 solution = &getTransferVector(i_to, _to_var_names[i]);
345 break;
346 case FROM_MULTIAPP:
347 solution = to_sys->solution.get();
348 break;
349 default:
350 mooseError("Unknown direction");
351 }
352
353 MeshBase * to_mesh = &_to_meshes[i_to]->getMesh();
354 auto & fe_type = to_sys->variable_type(var_num);
355 bool is_constant = fe_type.order == CONSTANT;
356 bool is_nodal = fe_type.family == LAGRANGE;
357
358 if (is_nodal)
359 {
360 for (const auto & node : to_mesh->local_node_ptr_range())
361 {
362 // Skip this node if the variable has no dofs at it.
363 if (node->n_dofs(sys_num, var_num) < 1)
364 continue;
365
366 unsigned int lowest_app_rank = libMesh::invalid_uint;
367 Real best_val = 0.;
368 bool point_found = false;
369 for (auto & group : incoming_vals_ids)
370 {
371 // Skip this proc if the node wasn't in it's bounding boxes.
372 std::pair<unsigned int, dof_id_type> key(i_to, node->id());
373 // Make sure point_index_map has data for corresponding pid
374 mooseAssert(point_index_map.find(group.first) != point_index_map.end(),
375 "Point index map does not have data for processor group.first");
376 if (point_index_map[group.first].find(key) == point_index_map[group.first].end())
377 continue;
378
379 auto i_pt = point_index_map[group.first][key];
380
381 // Ignore this proc if it's app has a higher rank than the
382 // previously found lowest app rank.
384 {
385 if (group.second[i_pt].second >= lowest_app_rank)
386 continue;
387 }
388
389 // Ignore this proc if the point was actually outside its meshes.
390 if (group.second[i_pt].first == OutOfMeshValue)
391 continue;
392
393 best_val = group.second[i_pt].first;
394 point_found = true;
395 }
396
397 if (_error_on_miss && !point_found)
398 mooseError("Point not found in the reference space! ",
399 (*_to_transforms[to_global_num])(*node));
400
401 dof_id_type dof = node->dof_number(sys_num, var_num, 0);
402 solution->set(dof, best_val);
403 }
404 }
405 else // Elemental
406 {
407 std::vector<Point> points;
408 std::vector<dof_id_type> point_ids;
409 for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
410 {
411 // Skip this element if the variable has no dofs at it.
412 if (elem->n_dofs(sys_num, var_num) < 1)
413 continue;
414
415 points.clear();
416 point_ids.clear();
417 // grab sample points
418 // for constant shape function, we take the element centroid
419 if (is_constant)
420 {
421 points.push_back(elem->vertex_average());
422 point_ids.push_back(elem->id());
423 }
424 // for higher order method, we take all nodes of element
425 // this works for the first order L2 Lagrange. Might not work
426 // with something higher than the first order
427 else
428 {
429 for (auto & node : elem->node_ref_range())
430 {
431 points.push_back(node);
432 point_ids.push_back(node.id());
433 }
434 }
435
436 auto n_points = points.size();
437 unsigned int n_comp = elem->n_comp(sys_num, var_num);
438 // We assume each point corresponds to one component of elemental variable
439 if (n_points != n_comp)
440 mooseError(" Number of points ",
441 n_points,
442 " does not equal to number of variable components ",
443 n_comp);
444 for (unsigned int offset = 0; offset < n_points; offset++)
445 {
446 unsigned int lowest_app_rank = libMesh::invalid_uint;
447 Real best_val = 0;
448 bool point_found = false;
449 for (auto & group : incoming_vals_ids)
450 {
451 // Skip this proc if the elem wasn't in it's bounding boxes.
452 std::pair<unsigned int, dof_id_type> key(i_to, point_ids[offset]);
453 if (point_index_map[group.first].find(key) == point_index_map[group.first].end())
454 continue;
455
456 unsigned int i_pt = point_index_map[group.first][key];
457
458 // Ignore this proc if it's app has a higher rank than the
459 // previously found lowest app rank.
461 {
462 if (group.second[i_pt].second >= lowest_app_rank)
463 continue;
464 }
465
466 // Ignore this proc if the point was actually outside its meshes.
467 if (group.second[i_pt].first == OutOfMeshValue)
468 continue;
469
470 best_val = group.second[i_pt].first;
471 point_found = true;
472 }
473
474 if (_error_on_miss && !point_found)
475 mooseError("Point not found in the reference space! ",
476 (*_to_transforms[to_global_num])(elem->vertex_average()));
477
478 // Get the value for a dof
479 dof_id_type dof = elem->dof_number(sys_num, var_num, offset);
480 solution->set(dof, best_val);
481 } // point
482 } // element
483 }
484 solution->close();
485 to_sys->update();
486 }
487}
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", MultiAppShapeEvaluationTransfer, "12/31/2024 24:00")
registerMooseObjectRenamed("MooseApp", MultiAppMeshFunctionTransfer, "12/31/2023 24:00", MultiAppShapeEvaluationTransfer)
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
virtual const MooseVariableFieldBase & getVariable(const THREAD_ID tid, const std::string &var_name, Moose::VarKindType expected_var_type=Moose::VarKindType::VAR_ANY, Moose::VarFieldType expected_var_field_type=Moose::VarFieldType::VAR_FIELD_ANY) const override
Returns the variable reference for requested variable which must be of the expected_var_type (Nonline...
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
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...
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().
libMesh::EquationSystems & getEquationSystem(FEProblemBase &problem, bool use_displaced) const
Returns the Problem's equation system, displaced or not Be careful! If you transfer TO a displaced sy...
MultiAppShapeEvaluationTransfer(const InputParameters &parameters)
virtual void execute() override
Execute the transfer.
bool _error_on_miss
Whether to error if the target point is not found in the source domain.
void transferVariable(unsigned int i)
Performs the transfer for the variable of index i.
unsigned int _var_size
The number of variables to transfer.
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.
bool _displaced_source_mesh
True if displaced mesh is used for the source mesh, otherwise false.
std::vector< unsigned int > _from_local2global_map
Given local app index, returns global app index.
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< FEProblemBase * > _to_problems
std::vector< MooseMesh * > _to_meshes
Point getPointInSourceAppFrame(const Point &p, unsigned int local_i_from, const std::string &phase) const
Get the source app point from a point in the reference frame.
std::vector< libMesh::EquationSystems * > _to_es
std::vector< std::unique_ptr< MultiAppCoordTransform > > _to_transforms
virtual libMesh::System & system()=0
Get the reference to the libMesh system.
@ FROM_MULTIAPP
Definition Transfer.h:71
@ TO_MULTIAPP
Definition Transfer.h:70
static const libMesh::Number OutOfMeshValue
Definition Transfer.h:121
MooseEnum _current_direction
Definition Transfer.h:109
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:99
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
unsigned int variable_number(std::string_view var) const
@ VAR_FIELD_STANDARD
Definition MooseTypes.h:777
@ VAR_ANY
Definition MooseTypes.h:772
const unsigned int invalid_uint