Line data Source code
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 : // MOOSE includes
11 : #include "MultiAppTransfer.h"
12 : #include "Transfer.h"
13 : #include "MooseTypes.h"
14 : #include "FEProblem.h"
15 : #include "DisplacedProblem.h"
16 : #include "MultiApp.h"
17 : #include "MooseMesh.h"
18 :
19 : #include "libmesh/parallel_algebra.h"
20 : #include "libmesh/mesh_tools.h"
21 :
22 : InputParameters
23 331339 : MultiAppTransfer::validParams()
24 : {
25 331339 : InputParameters params = Transfer::validParams();
26 331339 : params.addDeprecatedParam<MultiAppName>("multi_app",
27 : "The name of the MultiApp to transfer data with",
28 : "Use to_multiapp & from_multiapp parameters now");
29 331339 : params.addParam<MultiAppName>("from_multi_app", "The name of the MultiApp to receive data from");
30 331339 : params.addParam<MultiAppName>("to_multi_app", "The name of the MultiApp to transfer the data to");
31 :
32 : // MultiAppTransfers by default will execute with their associated MultiApp. These flags will be
33 : // added by FEProblemBase when the transfer is added.
34 331339 : ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
35 331339 : exec_enum.addAvailableFlags(EXEC_SAME_AS_MULTIAPP);
36 : // Add the POST_ADAPTIVITY execution flag.
37 : #ifdef LIBMESH_ENABLE_AMR
38 331339 : exec_enum.addAvailableFlags(EXEC_POST_ADAPTIVITY);
39 : #endif
40 331339 : exec_enum = EXEC_SAME_AS_MULTIAPP;
41 331339 : params.setDocString("execute_on", exec_enum.getDocString());
42 :
43 994017 : params.addParam<bool>(
44 : "check_multiapp_execute_on",
45 662678 : true,
46 : "When false the check between the multiapp and transfer execute on flags is not performed.");
47 994017 : params.addParam<bool>("displaced_source_mesh",
48 662678 : false,
49 : "Whether or not to use the displaced mesh for the source mesh.");
50 994017 : params.addParam<bool>("displaced_target_mesh",
51 662678 : false,
52 : "Whether or not to use the displaced mesh for the target mesh.");
53 331339 : addSkipCoordCollapsingParam(params);
54 331339 : return params;
55 0 : }
56 :
57 : void
58 59398 : MultiAppTransfer::addBBoxFactorParam(InputParameters & params)
59 : {
60 178194 : params.addRangeCheckedParam<Real>(
61 : "bbox_factor",
62 118796 : 1 + TOLERANCE,
63 : "bbox_factor>0",
64 : "Multiply bounding box width (in all directions) by the prescribed factor. Values less than "
65 : "1 will shrink the bounding box; values greater than 1 will enlarge the bounding box. It is "
66 : "generally not advised to ever shrink the bounding box. On the other hand it may be helpful "
67 : "to enlarge the bounding box. Larger bounding boxes will lead to more accurate determination "
68 : "of the closest node/element with the tradeoff of more communication.");
69 59398 : }
70 :
71 : void
72 331339 : MultiAppTransfer::addSkipCoordCollapsingParam(InputParameters & params)
73 : {
74 994017 : params.addParam<bool>(
75 : "skip_coordinate_collapsing",
76 662678 : true,
77 : "Whether to skip coordinate collapsing (translation and rotation are still performed, only "
78 : "XYZ, RZ etc collapsing is skipped) when performing mapping and inverse "
79 : "mapping coordinate transformation operations. This parameter should only "
80 : "be set by users who really know what they're doing.");
81 331339 : params.addParamNamesToGroup("skip_coordinate_collapsing", "Advanced");
82 331339 : }
83 :
84 11560 : MultiAppTransfer::MultiAppTransfer(const InputParameters & parameters)
85 : : Transfer(parameters),
86 11560 : _skip_coordinate_collapsing(getParam<bool>("skip_coordinate_collapsing")),
87 11560 : _displaced_source_mesh(getParam<bool>("displaced_source_mesh")),
88 11560 : _displaced_target_mesh(getParam<bool>("displaced_target_mesh")),
89 23120 : _bbox_factor(isParamValid("bbox_factor") ? getParam<Real>("bbox_factor") : 1)
90 : {
91 : // Get the multiapps from their names
92 11560 : if (!isParamValid("multi_app"))
93 : {
94 11508 : if (isParamValid("from_multi_app"))
95 : {
96 6571 : _from_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("from_multi_app"));
97 6571 : _multi_app = _from_multi_app;
98 : }
99 11508 : if (isParamValid("to_multi_app"))
100 : {
101 6036 : _to_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("to_multi_app"));
102 6036 : _multi_app = _to_multi_app;
103 : }
104 27949 : if (!isParamValid("direction") && !isParamValid("from_multi_app") &&
105 16441 : !isParamValid("to_multi_app"))
106 4 : mooseError("from_multi_app and/or to_multi_app must be specified");
107 : }
108 : else
109 : {
110 : // Check deprecated direction parameter
111 104 : for (const auto & dir : _directions)
112 : {
113 52 : if (dir == FROM_MULTIAPP)
114 : {
115 52 : _from_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("multi_app"));
116 52 : _multi_app = _from_multi_app;
117 : }
118 0 : else if (dir == TO_MULTIAPP)
119 : {
120 0 : _to_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("multi_app"));
121 0 : _multi_app = _to_multi_app;
122 : }
123 : else
124 0 : paramError("direction",
125 : "BETWEN_MULTIAPP transfers should be specified using to/from_multi_app");
126 : }
127 : }
128 :
129 11556 : if (getParam<bool>("check_multiapp_execute_on"))
130 11436 : checkMultiAppExecuteOn();
131 :
132 : // Fill direction attributes, for backward compatibility but also convenience
133 11556 : if (!isParamValid("direction"))
134 : {
135 11500 : if (_from_multi_app && (!_to_multi_app || _from_multi_app == _to_multi_app))
136 5468 : _directions.setAdditionalValue("from_multiapp");
137 11500 : if (_to_multi_app && (!_from_multi_app || _from_multi_app == _to_multi_app))
138 4933 : _directions.setAdditionalValue("to_multiapp");
139 11500 : if (_from_multi_app && _to_multi_app && _from_multi_app != _to_multi_app)
140 1103 : _directions.setAdditionalValue("between_multiapp");
141 :
142 : // So it's available in the next constructors
143 11500 : _direction = _directions[0];
144 11500 : _current_direction = _directions[0];
145 : }
146 :
147 : // Handle deprecated parameters
148 11556 : if (parameters.isParamSetByUser("direction"))
149 : {
150 56 : if (!isParamValid("multi_app"))
151 4 : paramError("direction",
152 : "The deprecated 'direction' parameter is meant to be used in conjunction with the "
153 : "'multi_app' parameter");
154 52 : if (isParamValid("to_multi_app") || isParamValid("from_multi_app"))
155 4 : paramError("direction",
156 : "The deprecated 'direction' parameter is not meant to be used in conjunction with "
157 : "the 'from_multi_app' or 'to_multi_app' parameters");
158 : }
159 11548 : }
160 :
161 : void
162 11436 : MultiAppTransfer::checkMultiAppExecuteOn()
163 : {
164 11436 : if (_from_multi_app && !_to_multi_app)
165 5456 : if (getExecuteOnEnum() != _from_multi_app->getExecuteOnEnum())
166 241 : mooseDoOnce(
167 : mooseWarning("MultiAppTransfer execute_on flags do not match associated from_multi_app "
168 : "execute_on flags"));
169 :
170 11436 : if (_to_multi_app && !_from_multi_app)
171 4869 : if (getExecuteOnEnum() != _to_multi_app->getExecuteOnEnum())
172 234 : mooseDoOnce(
173 : mooseWarning("MultiAppTransfer execute_on flags do not match associated to_multi_app "
174 : "execute_on flags"));
175 :
176 : // In the case of siblings transfer, the check will be looser
177 11436 : if (_from_multi_app && _to_multi_app)
178 1114 : if (getExecuteOnEnum() != _from_multi_app->getExecuteOnEnum() &&
179 7 : getExecuteOnEnum() != _to_multi_app->getExecuteOnEnum())
180 7 : mooseDoOnce(
181 : mooseWarning("MultiAppTransfer execute_on flags do not match associated to_multi_app "
182 : "and from_multi_app execute_on flags"));
183 11436 : }
184 :
185 : void
186 9198 : MultiAppTransfer::variableIntegrityCheck(const AuxVariableName & var_name,
187 : bool is_from_multiapp) const
188 : {
189 9198 : bool variable_found = false;
190 9198 : bool has_an_app = false;
191 :
192 : // Check the from_multi_app for the variable
193 9198 : if (is_from_multiapp && _from_multi_app)
194 12339 : for (unsigned int i = 0; i < _from_multi_app->numGlobalApps(); i++)
195 8063 : if (_from_multi_app->hasLocalApp(i))
196 : {
197 6674 : has_an_app = true;
198 6674 : if (_from_multi_app->appProblemBase(i).hasVariable(var_name))
199 6670 : variable_found = true;
200 : }
201 :
202 : // Check the to_multi_app for the variable
203 9198 : if (!is_from_multiapp && _to_multi_app)
204 14248 : for (unsigned int i = 0; i < _to_multi_app->numGlobalApps(); i++)
205 9326 : if (_to_multi_app->hasLocalApp(i))
206 : {
207 7753 : has_an_app = true;
208 7753 : if (_to_multi_app->appProblemBase(i).hasVariable(var_name))
209 7753 : variable_found = true;
210 : }
211 :
212 9198 : if (!variable_found && has_an_app)
213 4 : mooseError("Cannot find variable ", var_name, " for ", name(), " Transfer");
214 9194 : }
215 :
216 : void
217 11220 : MultiAppTransfer::initialSetup()
218 : {
219 : // Check for siblings transfer support
220 11220 : if (_to_multi_app && _from_multi_app)
221 1103 : checkSiblingsTransferSupported();
222 :
223 11220 : getAppInfo();
224 :
225 11212 : if (_from_multi_app)
226 6411 : _from_multi_app->addAssociatedTransfer(*this);
227 11212 : if (_to_multi_app)
228 5904 : _to_multi_app->addAssociatedTransfer(*this);
229 11212 : }
230 :
231 : void
232 69981 : MultiAppTransfer::getAppInfo()
233 : {
234 : // I would like to do all of this in initialSetup, but it will fail with
235 : // multiapps that reset. A reset deletes and rebuilds the FEProblems so all
236 : // of the pointers will be broken.
237 :
238 : // Clear the vectors since we've probably built them up from a previous call
239 69981 : _from_problems.clear();
240 69981 : _to_problems.clear();
241 69981 : _from_es.clear();
242 69981 : _to_es.clear();
243 69981 : _from_meshes.clear();
244 69981 : _to_meshes.clear();
245 69981 : _to_positions.clear();
246 69981 : _from_positions.clear();
247 69981 : _to_transforms.clear();
248 69981 : _from_transforms.clear();
249 : // Clear this map since we build it from scratch every time we transfer
250 : // Otherwise, this will cause two issues: 1) increasing memory usage
251 : // for a simulation that requires many transfers, 2) producing wrong results
252 : // when we do collective communication on this vector.
253 69981 : _to_local2global_map.clear();
254 69981 : _from_local2global_map.clear();
255 :
256 : // Build the vectors for to problems, from problems, and subapps positions.
257 69981 : if (_current_direction == FROM_MULTIAPP)
258 : {
259 32739 : _to_problems.push_back(&_from_multi_app->problemBase());
260 32739 : _to_positions.push_back(Point(0., 0., 0.));
261 32739 : getFromMultiAppInfo();
262 : }
263 37242 : else if (_current_direction == TO_MULTIAPP)
264 : {
265 30586 : _from_problems.push_back(&_to_multi_app->problemBase());
266 30586 : _from_positions.push_back(Point(0., 0., 0.));
267 30586 : getToMultiAppInfo();
268 : }
269 6656 : else if (_current_direction == BETWEEN_MULTIAPP)
270 : {
271 : mooseAssert(&_from_multi_app->problemBase().coordTransform() ==
272 : &_to_multi_app->problemBase().coordTransform(),
273 : "I believe these should be the same. If not, then it will be difficult to define a "
274 : "canonical reference frame.");
275 6656 : getToMultiAppInfo();
276 6656 : getFromMultiAppInfo();
277 : }
278 :
279 : // Build the from and to equation systems and mesh vectors.
280 147405 : for (unsigned int i = 0; i < _to_problems.size(); i++)
281 : {
282 : // TODO: Do I actually want es or displaced es?
283 77424 : _to_es.push_back(&_to_problems[i]->es());
284 77424 : if (_displaced_target_mesh && _to_problems[i]->getDisplacedProblem())
285 504 : _to_meshes.push_back(&_to_problems[i]->getDisplacedProblem()->mesh());
286 : else
287 76920 : _to_meshes.push_back(&_to_problems[i]->mesh());
288 : }
289 :
290 147058 : for (unsigned int i = 0; i < _from_problems.size(); i++)
291 : {
292 77077 : _from_es.push_back(&_from_problems[i]->es());
293 77077 : if (_displaced_source_mesh && _from_problems[i]->getDisplacedProblem())
294 849 : _from_meshes.push_back(&_from_problems[i]->getDisplacedProblem()->mesh());
295 : else
296 76228 : _from_meshes.push_back(&_from_problems[i]->mesh());
297 : }
298 :
299 69981 : MooseAppCoordTransform::MinimalData from_app_transform_construction_data{};
300 69981 : if (_communicator.rank() == 0)
301 : from_app_transform_construction_data =
302 51123 : _current_direction == TO_MULTIAPP
303 102246 : ? _to_multi_app->problemBase().coordTransform().minimalDataDescription()
304 51123 : : _from_multi_app->appProblemBase(0).coordTransform().minimalDataDescription();
305 69981 : _communicator.broadcast(from_app_transform_construction_data);
306 : _from_moose_app_transform =
307 69981 : std::make_unique<MooseAppCoordTransform>(from_app_transform_construction_data);
308 :
309 69981 : MooseAppCoordTransform::MinimalData to_app_transform_construction_data{};
310 69981 : if (_communicator.rank() == 0)
311 : to_app_transform_construction_data =
312 51123 : _current_direction == FROM_MULTIAPP
313 102246 : ? _from_multi_app->problemBase().coordTransform().minimalDataDescription()
314 51123 : : _to_multi_app->appProblemBase(0).coordTransform().minimalDataDescription();
315 69981 : _communicator.broadcast(to_app_transform_construction_data);
316 : _to_moose_app_transform =
317 69981 : std::make_unique<MooseAppCoordTransform>(to_app_transform_construction_data);
318 :
319 : /*
320 : * skip_coordinate_collapsing: whether to set the transform to skip coordinate collapsing
321 : * (from XYZ to RZ for example)
322 : * transforms: vector of transforms to add the new transforms to
323 : * moose_app_transform: base for the new transform
324 : * is_parent_app_transform: whether working on the transform for the parent app (this app, the
325 : * one creating the transfer) or for child apps
326 : * multiapp: pointer to the multiapp to obtain the position of the child apps
327 : */
328 139962 : auto create_multiapp_transforms = [this](auto & transforms,
329 : const auto & moose_app_transform,
330 : const bool is_parent_app_transform,
331 164288 : const MultiApp * const multiapp = nullptr)
332 : {
333 : mooseAssert(is_parent_app_transform || multiapp,
334 : "Coordinate transform must be created either for child app or parent app");
335 139962 : if (is_parent_app_transform)
336 : {
337 63325 : transforms.push_back(std::make_unique<MultiAppCoordTransform>(moose_app_transform));
338 63325 : transforms.back()->skipCoordinateCollapsing(_skip_coordinate_collapsing);
339 : // zero translation
340 : }
341 : else
342 : {
343 : mooseAssert(transforms.size() == 0, "transforms should not be initialized at this point");
344 177600 : for (const auto i : make_range(multiapp->numGlobalApps()))
345 : {
346 100963 : transforms.push_back(std::make_unique<MultiAppCoordTransform>(moose_app_transform));
347 100963 : auto & transform = transforms[i];
348 100963 : transform->skipCoordinateCollapsing(_skip_coordinate_collapsing);
349 100963 : if (multiapp->usingPositions())
350 100963 : transform->setTranslationVector(multiapp->position(i));
351 : }
352 : }
353 139962 : };
354 :
355 69981 : if (_current_direction == TO_MULTIAPP)
356 : {
357 30586 : create_multiapp_transforms(
358 30586 : _to_transforms, *_to_moose_app_transform, false, _to_multi_app.get());
359 30586 : create_multiapp_transforms(_from_transforms, *_from_moose_app_transform, true);
360 : }
361 69981 : if (_current_direction == FROM_MULTIAPP)
362 : {
363 32739 : create_multiapp_transforms(_to_transforms, *_to_moose_app_transform, true);
364 32739 : create_multiapp_transforms(
365 32739 : _from_transforms, *_from_moose_app_transform, false, _from_multi_app.get());
366 : }
367 69981 : if (_current_direction == BETWEEN_MULTIAPP)
368 : {
369 6656 : create_multiapp_transforms(
370 6656 : _to_transforms, *_to_moose_app_transform, false, _to_multi_app.get());
371 6656 : create_multiapp_transforms(
372 6656 : _from_transforms, *_from_moose_app_transform, false, _from_multi_app.get());
373 : }
374 :
375 141288 : auto check_transform_compatibility = [this](const MultiAppCoordTransform & transform)
376 : {
377 139954 : if (transform.hasNonTranslationTransformation() && !usesMooseAppCoordTransform())
378 24 : mooseWarning("Transfer '",
379 8 : name(),
380 : "' of type '",
381 8 : type(),
382 : "' has non-translation transformations but it does not implement coordinate "
383 : "transformations using the 'MooseAppCoordTransform' class. Your data transfers "
384 : "will not be performed in the expected transformed frame");
385 209927 : };
386 :
387 : // set the destination coordinate systems for each transform for the purposes of determining
388 : // coordinate collapsing. For example if TO is XYZ and FROM is RZ, then TO will have its XYZ
389 : // coordinates collapsed into RZ and FROM will have a no-op for coordinate collapsing
390 :
391 152186 : for (const auto i : index_range(_from_transforms))
392 : {
393 82213 : auto & from_transform = _from_transforms[i];
394 82213 : from_transform->setDestinationCoordTransform(*_to_moose_app_transform);
395 82213 : if (i == 0)
396 69981 : check_transform_compatibility(*from_transform);
397 : }
398 151644 : for (const auto i : index_range(_to_transforms))
399 : {
400 81671 : auto & to_transform = _to_transforms[i];
401 81671 : to_transform->setDestinationCoordTransform(*_from_moose_app_transform);
402 81671 : if (i == 0)
403 69973 : check_transform_compatibility(*to_transform);
404 : }
405 69973 : }
406 :
407 : namespace
408 : {
409 : void
410 76637 : fillInfo(MultiApp & multi_app,
411 : std::vector<unsigned int> & map,
412 : std::vector<FEProblemBase *> & problems,
413 : std::vector<Point> & positions)
414 : {
415 177600 : for (unsigned int i_app = 0; i_app < multi_app.numGlobalApps(); i_app++)
416 : {
417 100963 : if (!multi_app.hasLocalApp(i_app))
418 9787 : continue;
419 :
420 91176 : auto & subapp_problem = multi_app.appProblemBase(i_app);
421 :
422 91176 : map.push_back(i_app);
423 91176 : problems.push_back(&subapp_problem);
424 91176 : if (multi_app.usingPositions())
425 91176 : positions.push_back(multi_app.position(i_app));
426 : }
427 76637 : }
428 : }
429 :
430 : void
431 37242 : MultiAppTransfer::getToMultiAppInfo()
432 : {
433 37242 : if (!_to_multi_app)
434 0 : mooseError("There is no to_multiapp to get info from");
435 :
436 37242 : fillInfo(*_to_multi_app, _to_local2global_map, _to_problems, _to_positions);
437 37242 : }
438 :
439 : void
440 39395 : MultiAppTransfer::getFromMultiAppInfo()
441 : {
442 39395 : if (!_from_multi_app)
443 0 : mooseError("There is no from_multiapp to get info from");
444 :
445 39395 : fillInfo(*_from_multi_app, _from_local2global_map, _from_problems, _from_positions);
446 39395 : }
447 :
448 : void
449 63695 : MultiAppTransfer::transformBoundingBox(BoundingBox & box, const MultiAppCoordTransform & transform)
450 : {
451 63695 : MultiApp::transformBoundingBox(box, transform);
452 63695 : }
453 :
454 : void
455 60628 : MultiAppTransfer::extendBoundingBoxes(const Real factor, std::vector<BoundingBox> & bboxes) const
456 : {
457 60628 : const auto extension_factor = factor - 1;
458 :
459 : // Extend (or contract if the extension factor is negative) bounding boxes along all the
460 : // directions by the same length. Greater than zero values of this member may be necessary because
461 : // the nearest bounding box does not necessarily give you the closest node/element. It will depend
462 : // on the partition and geometry. A node/element will more likely find its nearest source
463 : // element/node by extending bounding boxes. If each of the bounding boxes covers the entire
464 : // domain, a node/element will be able to find its nearest source element/node for sure, but at
465 : // the same time, more communication will be involved and can be expensive.
466 157936 : for (auto & box : bboxes)
467 : {
468 : // libmesh set an invalid bounding box using this code
469 : // for (unsigned int i=0; i<LIBMESH_DIM; i++)
470 : // {
471 : // this->first(i) = std::numeric_limits<Real>::max();
472 : // this->second(i) = -std::numeric_limits<Real>::max();
473 : // }
474 : // If it is an invalid box, we should skip it
475 97308 : if (box.first(0) == std::numeric_limits<Real>::max())
476 0 : continue;
477 :
478 97308 : auto width = box.second - box.first;
479 97308 : box.second += width * extension_factor;
480 97308 : box.first -= width * extension_factor;
481 : }
482 60628 : }
483 :
484 : std::vector<BoundingBox>
485 2333 : MultiAppTransfer::getFromBoundingBoxes()
486 : {
487 2333 : std::vector<std::pair<Point, Point>> bb_points(_from_meshes.size());
488 5114 : for (unsigned int i = 0; i < _from_meshes.size(); i++)
489 : {
490 : // Get a bounding box around the mesh elements that are local to the current
491 : // processor.
492 2781 : BoundingBox bbox = MeshTools::create_local_bounding_box(*_from_meshes[i]);
493 :
494 : // Translate the bounding box to the from domain's position. We may have rotations so we must
495 : // be careful in constructing the new min and max (first and second)
496 2781 : const auto from_global_num = getGlobalSourceAppIndex(i);
497 2781 : transformBoundingBox(bbox, *_from_transforms[from_global_num]);
498 :
499 : // Cast the bounding box into a pair of points (so it can be put through
500 : // MPI communication).
501 2781 : bb_points[i] = static_cast<std::pair<Point, Point>>(bbox);
502 : }
503 :
504 : // Serialize the bounding box points.
505 2333 : _communicator.allgather(bb_points);
506 :
507 : // Recast the points back into bounding boxes and return.
508 2333 : std::vector<BoundingBox> bboxes(bb_points.size());
509 6542 : for (unsigned int i = 0; i < bb_points.size(); i++)
510 4209 : bboxes[i] = static_cast<BoundingBox>(bb_points[i]);
511 :
512 : // possibly extend bounding boxes
513 2333 : extendBoundingBoxes(_bbox_factor, bboxes);
514 :
515 4666 : return bboxes;
516 2333 : }
517 :
518 : std::vector<BoundingBox>
519 85 : MultiAppTransfer::getFromBoundingBoxes(BoundaryID boundary_id)
520 : {
521 85 : std::vector<std::pair<Point, Point>> bb_points(_from_meshes.size());
522 85 : const Real min_r = std::numeric_limits<Real>::lowest();
523 85 : const Real max_r = std::numeric_limits<Real>::max();
524 :
525 184 : for (unsigned int i = 0; i < _from_meshes.size(); i++)
526 : {
527 :
528 99 : Point min(max_r, max_r, max_r);
529 99 : Point max(min_r, min_r, min_r);
530 99 : bool at_least_one = false;
531 :
532 : // TODO: Factor this into mesh_tools after adding new boundary bounding box routine.
533 99 : const ConstBndNodeRange & bnd_nodes = *_from_meshes[i]->getBoundaryNodeRange();
534 5893 : for (const auto & bnode : bnd_nodes)
535 : {
536 7242 : if (bnode->_bnd_id == boundary_id &&
537 1448 : bnode->_node->processor_id() == _from_meshes[i]->processor_id())
538 : {
539 1404 : at_least_one = true;
540 1404 : const auto & node = *bnode->_node;
541 5616 : for (const auto i : make_range(Moose::dim))
542 : {
543 4212 : min(i) = std::min(min(i), node(i));
544 4212 : max(i) = std::max(max(i), node(i));
545 : }
546 : }
547 : }
548 :
549 99 : BoundingBox bbox(min, max);
550 99 : if (!at_least_one)
551 9 : bbox.min() = max; // If we didn't hit any nodes, this will be _the_ minimum bbox
552 : else
553 : {
554 : // Translate the bounding box to the from domain's position. We may have rotations so we must
555 : // be careful in constructing the new min and max (first and second)
556 90 : const auto from_global_num = getGlobalSourceAppIndex(i);
557 90 : transformBoundingBox(bbox, *_from_transforms[from_global_num]);
558 : }
559 :
560 : // Cast the bounding box into a pair of points (so it can be put through
561 : // MPI communication).
562 99 : bb_points[i] = static_cast<std::pair<Point, Point>>(bbox);
563 : }
564 :
565 : // Serialize the bounding box points.
566 85 : _communicator.allgather(bb_points);
567 :
568 : // Recast the points back into bounding boxes and return.
569 85 : std::vector<BoundingBox> bboxes(bb_points.size());
570 226 : for (unsigned int i = 0; i < bb_points.size(); i++)
571 141 : bboxes[i] = static_cast<BoundingBox>(bb_points[i]);
572 :
573 : // possibly extend bounding boxes
574 85 : extendBoundingBoxes(_bbox_factor, bboxes);
575 :
576 170 : return bboxes;
577 85 : }
578 :
579 : std::vector<unsigned int>
580 60628 : MultiAppTransfer::getFromsPerProc()
581 : {
582 60628 : std::vector<unsigned int> froms_per_proc;
583 60628 : if (_to_multi_app)
584 32340 : froms_per_proc.resize(n_processors(), 1);
585 60628 : if (_from_multi_app)
586 : {
587 33841 : froms_per_proc.resize(n_processors());
588 33841 : _communicator.allgather(_from_multi_app->numLocalApps(), froms_per_proc);
589 : }
590 60628 : return froms_per_proc;
591 0 : }
592 :
593 : NumericVector<Real> &
594 1391 : MultiAppTransfer::getTransferVector(unsigned int i_local, std::string var_name)
595 : {
596 : mooseAssert(_to_multi_app, "getTransferVector only works for transfers to multiapps");
597 :
598 1391 : return _to_multi_app->appTransferVector(_to_local2global_map[i_local], var_name);
599 : }
600 :
601 : void
602 3768 : MultiAppTransfer::checkVariable(const FEProblemBase & fe_problem,
603 : const VariableName & var_name,
604 : const std::string & param_name) const
605 : {
606 3768 : if (!fe_problem.hasVariable(var_name))
607 : {
608 4 : if (param_name.empty())
609 4 : mooseError("The variable '", var_name, "' does not exist.");
610 : else
611 0 : paramError(param_name, "The variable '", var_name, "' does not exist.");
612 : }
613 3764 : }
614 :
615 : Point
616 394 : MultiAppTransfer::getPointInTargetAppFrame(const Point & p,
617 : unsigned int local_i_to,
618 : const std::string & phase) const
619 : {
620 394 : const auto & to_transform = _to_transforms[getGlobalTargetAppIndex(local_i_to)];
621 394 : if (to_transform->hasCoordinateSystemTypeChange())
622 : {
623 0 : if (!_skip_coordinate_collapsing)
624 0 : mooseInfo(phase + " cannot use the point in the target app frame due to the "
625 : "non-uniqueness of the coordinate collapsing reverse mapping."
626 : " Coordinate collapse is ignored for this operation");
627 0 : to_transform->skipCoordinateCollapsing(true);
628 0 : const auto target_point = to_transform->mapBack(p);
629 0 : to_transform->skipCoordinateCollapsing(false);
630 0 : return target_point;
631 : }
632 : else
633 394 : return to_transform->mapBack(p);
634 : }
635 :
636 : unsigned int
637 5265251 : MultiAppTransfer::getGlobalSourceAppIndex(unsigned int i_from) const
638 : {
639 : mooseAssert(_current_direction == TO_MULTIAPP || i_from < _from_local2global_map.size(),
640 : "Out of bounds local from-app index");
641 5265251 : return _current_direction == TO_MULTIAPP ? 0 : _from_local2global_map[i_from];
642 : }
643 :
644 : unsigned int
645 434155 : MultiAppTransfer::getGlobalTargetAppIndex(unsigned int i_to) const
646 : {
647 : mooseAssert(_current_direction == FROM_MULTIAPP || i_to < _to_local2global_map.size(),
648 : "Out of bounds local to-app index");
649 434155 : return _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
650 : }
651 :
652 : unsigned int
653 0 : MultiAppTransfer::getLocalSourceAppIndex(unsigned int i_from) const
654 : {
655 0 : return _current_direction == TO_MULTIAPP
656 0 : ? 0
657 0 : : _from_local2global_map[i_from] - _from_local2global_map[0];
658 : }
659 :
660 : void
661 27741 : MultiAppTransfer::errorIfObjectExecutesOnTransferInSourceApp(const std::string & object_name) const
662 : {
663 : // parent app is the source app, EXEC_TRANSFER is fine
664 27741 : if (!hasFromMultiApp())
665 0 : return;
666 : // Get the app and problem
667 27741 : const auto & app = getFromMultiApp();
668 27741 : if (!app->hasApp())
669 0 : return;
670 27741 : const auto & problem = app->appProblemBase(app->firstLocalApp());
671 : // Use the warehouse to find the object
672 27741 : std::vector<SetupInterface *> objects_with_exec_on;
673 27741 : problem.theWarehouse()
674 55482 : .query()
675 27741 : .template condition<AttribName>(object_name)
676 27741 : .template condition<AttribExecOns>(EXEC_TRANSFER)
677 27741 : .queryInto(objects_with_exec_on);
678 27741 : if (objects_with_exec_on.size())
679 4 : mooseError("Object '" + object_name +
680 : "' should not be executed on EXEC_TRANSFER, because this transfer has "
681 : "indicated it does not support it.\nExecuting this object on TIMESTEP_END should be "
682 : "sufficient to get updated values.");
683 27737 : }
|