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