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 : #include "UserObject.h"
19 :
20 : #include "libmesh/parallel_algebra.h"
21 : #include "libmesh/mesh_tools.h"
22 :
23 : InputParameters
24 101780 : MultiAppTransfer::validParams()
25 : {
26 101780 : InputParameters params = Transfer::validParams();
27 610680 : params.addDeprecatedParam<MultiAppName>("multi_app",
28 : "The name of the MultiApp to transfer data with",
29 : "Use to_multiapp & from_multiapp parameters now");
30 407120 : params.addParam<MultiAppName>("from_multi_app", "The name of the MultiApp to receive data from");
31 407120 : params.addParam<MultiAppName>("to_multi_app", "The name of the MultiApp to transfer the data to");
32 :
33 : // MultiAppTransfers by default will execute with their associated MultiApp. These flags will be
34 : // added by FEProblemBase when the transfer is added.
35 101780 : ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
36 101780 : exec_enum.addAvailableFlags(EXEC_SAME_AS_MULTIAPP);
37 : // Add the POST_ADAPTIVITY execution flag.
38 : #ifdef LIBMESH_ENABLE_AMR
39 101780 : exec_enum.addAvailableFlags(EXEC_POST_ADAPTIVITY);
40 : #endif
41 101780 : exec_enum = EXEC_SAME_AS_MULTIAPP;
42 305340 : params.setDocString("execute_on", exec_enum.getDocString());
43 :
44 305340 : params.addParam<bool>(
45 : "check_multiapp_execute_on",
46 203560 : true,
47 : "When false the check between the multiapp and transfer execute on flags is not performed.");
48 305340 : params.addParam<bool>("displaced_source_mesh",
49 203560 : false,
50 : "Whether or not to use the displaced mesh for the source mesh.");
51 203560 : params.addParam<bool>("displaced_target_mesh",
52 203560 : false,
53 : "Whether or not to use the displaced mesh for the target mesh.");
54 101780 : addSkipCoordCollapsingParam(params);
55 101780 : return params;
56 0 : }
57 :
58 : void
59 14548 : MultiAppTransfer::addBBoxFactorParam(InputParameters & params)
60 : {
61 58192 : params.addRangeCheckedParam<Real>(
62 : "bbox_factor",
63 29096 : 1 + TOLERANCE,
64 : "bbox_factor>0",
65 : "Multiply bounding box width (in all directions) by the prescribed factor. Values less than "
66 : "1 will shrink the bounding box; values greater than 1 will enlarge the bounding box. It is "
67 : "generally not advised to ever shrink the bounding box. On the other hand it may be helpful "
68 : "to enlarge the bounding box. Larger bounding boxes will lead to more accurate determination "
69 : "of the closest node/element with the tradeoff of more communication.");
70 14548 : }
71 :
72 : void
73 101780 : MultiAppTransfer::addSkipCoordCollapsingParam(InputParameters & params)
74 : {
75 305340 : params.addParam<bool>(
76 : "skip_coordinate_collapsing",
77 203560 : true,
78 : "Whether to skip coordinate collapsing (translation and rotation are still performed, only "
79 : "XYZ, RZ etc collapsing is skipped) when performing mapping and inverse "
80 : "mapping coordinate transformation operations. This parameter should only "
81 : "be set by users who really know what they're doing.");
82 305340 : params.addParamNamesToGroup("skip_coordinate_collapsing", "Advanced");
83 101780 : }
84 :
85 : void
86 21023 : MultiAppTransfer::addUserObjectExecutionCheckParam(InputParameters & params)
87 : {
88 42046 : params.addParam<bool>("warn_source_object_execution_schedule",
89 42046 : true,
90 : "Emit a warning when the transfer execution schedule is detected to lag "
91 : "information from the user object. Note that the check cannot detect all "
92 : "potential wrong combinations of user-object/transfer execution schedules");
93 21023 : }
94 :
95 13011 : MultiAppTransfer::MultiAppTransfer(const InputParameters & parameters)
96 : : Transfer(parameters),
97 26022 : _skip_coordinate_collapsing(getParam<bool>("skip_coordinate_collapsing")),
98 26022 : _displaced_source_mesh(getParam<bool>("displaced_source_mesh")),
99 26022 : _displaced_target_mesh(getParam<bool>("displaced_target_mesh")),
100 69314 : _bbox_factor(isParamValid("bbox_factor") ? getParam<Real>("bbox_factor") : 1)
101 : {
102 : // Get the multiapps from their names
103 39033 : if (!isParamValid("multi_app"))
104 : {
105 38883 : if (isParamValid("from_multi_app"))
106 : {
107 15554 : _from_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("from_multi_app"));
108 7777 : _multi_app = _from_multi_app;
109 : }
110 38883 : if (isParamValid("to_multi_app"))
111 : {
112 13300 : _to_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("to_multi_app"));
113 6650 : _multi_app = _to_multi_app;
114 : }
115 82941 : if (!isParamValid("direction") && !isParamValid("from_multi_app") &&
116 28504 : !isParamValid("to_multi_app"))
117 3 : mooseError("from_multi_app and/or to_multi_app must be specified");
118 : }
119 : else
120 : {
121 : // Check deprecated direction parameter
122 100 : for (const auto & dir : _directions)
123 : {
124 50 : if (dir == FROM_MULTIAPP)
125 : {
126 100 : _from_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("multi_app"));
127 50 : _multi_app = _from_multi_app;
128 : }
129 0 : else if (dir == TO_MULTIAPP)
130 : {
131 0 : _to_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("multi_app"));
132 0 : _multi_app = _to_multi_app;
133 : }
134 : else
135 0 : paramError("direction",
136 : "BETWEN_MULTIAPP transfers should be specified using to/from_multi_app");
137 : }
138 : }
139 :
140 39024 : if (getParam<bool>("check_multiapp_execute_on"))
141 12888 : checkMultiAppExecuteOn();
142 :
143 : // Fill direction attributes, for backward compatibility but also convenience
144 39024 : if (!isParamValid("direction"))
145 : {
146 12955 : if (_from_multi_app && (!_to_multi_app || _from_multi_app == _to_multi_app))
147 18924 : _directions.setAdditionalValue("from_multiapp");
148 12955 : if (_to_multi_app && (!_from_multi_app || _from_multi_app == _to_multi_app))
149 15543 : _directions.setAdditionalValue("to_multiapp");
150 12955 : if (_from_multi_app && _to_multi_app && _from_multi_app != _to_multi_app)
151 4407 : _directions.setAdditionalValue("between_multiapp");
152 :
153 : // So it's available in the next constructors
154 12955 : _direction = _directions[0];
155 12955 : _current_direction = _directions[0];
156 : }
157 :
158 : // Handle deprecated parameters
159 26016 : if (parameters.isParamSetByUser("direction"))
160 : {
161 159 : if (!isParamValid("multi_app"))
162 6 : paramError("direction",
163 : "The deprecated 'direction' parameter is meant to be used in conjunction with the "
164 : "'multi_app' parameter");
165 250 : if (isParamValid("to_multi_app") || isParamValid("from_multi_app"))
166 6 : paramError("direction",
167 : "The deprecated 'direction' parameter is not meant to be used in conjunction with "
168 : "the 'from_multi_app' or 'to_multi_app' parameters");
169 : }
170 13002 : }
171 :
172 : void
173 12888 : MultiAppTransfer::checkMultiAppExecuteOn()
174 : {
175 12888 : if (_from_multi_app && !_to_multi_app)
176 6295 : if (getExecuteOnEnum() != _from_multi_app->getExecuteOnEnum())
177 238 : mooseDoOnce(
178 : mooseWarning("MultiAppTransfer execute_on flags do not match associated from_multi_app "
179 : "execute_on flags"));
180 :
181 12888 : if (_to_multi_app && !_from_multi_app)
182 5118 : if (getExecuteOnEnum() != _to_multi_app->getExecuteOnEnum())
183 234 : mooseDoOnce(
184 : mooseWarning("MultiAppTransfer execute_on flags do not match associated to_multi_app "
185 : "execute_on flags"));
186 :
187 : // In the case of siblings transfer, the check will be looser
188 12888 : if (_from_multi_app && _to_multi_app)
189 1489 : if (getExecuteOnEnum() != _from_multi_app->getExecuteOnEnum() &&
190 17 : getExecuteOnEnum() != _to_multi_app->getExecuteOnEnum())
191 4 : mooseDoOnce(
192 : mooseWarning("MultiAppTransfer execute_on flags do not match associated to_multi_app "
193 : "and from_multi_app execute_on flags"));
194 12888 : }
195 :
196 : void
197 9724 : MultiAppTransfer::variableIntegrityCheck(const AuxVariableName & var_name,
198 : bool is_from_multiapp) const
199 : {
200 9724 : bool variable_found = false;
201 9724 : bool has_an_app = false;
202 :
203 : // Check the from_multi_app for the variable
204 9724 : if (is_from_multiapp && _from_multi_app)
205 12296 : for (unsigned int i = 0; i < _from_multi_app->numGlobalApps(); i++)
206 8040 : if (_from_multi_app->hasLocalApp(i))
207 : {
208 6633 : has_an_app = true;
209 6633 : if (_from_multi_app->appProblemBase(i).hasVariable(var_name))
210 6630 : variable_found = true;
211 : }
212 :
213 : // Check the to_multi_app for the variable
214 9724 : if (!is_from_multiapp && _to_multi_app)
215 16009 : for (unsigned int i = 0; i < _to_multi_app->numGlobalApps(); i++)
216 10541 : if (_to_multi_app->hasLocalApp(i))
217 : {
218 8701 : has_an_app = true;
219 8701 : if (_to_multi_app->appProblemBase(i).hasVariable(var_name))
220 8701 : variable_found = true;
221 : }
222 :
223 9724 : if (!variable_found && has_an_app)
224 3 : mooseError("Cannot find variable ", var_name, " for ", name(), " Transfer");
225 9721 : }
226 :
227 : void
228 12720 : MultiAppTransfer::initialSetup()
229 : {
230 : // Check for siblings transfer support
231 12720 : if (_to_multi_app && _from_multi_app)
232 1469 : checkSiblingsTransferSupported();
233 :
234 12720 : getAppInfo();
235 :
236 12714 : if (_from_multi_app)
237 7644 : _from_multi_app->addAssociatedTransfer(*this);
238 12714 : if (_to_multi_app)
239 6539 : _to_multi_app->addAssociatedTransfer(*this);
240 12714 : }
241 :
242 : void
243 65623 : MultiAppTransfer::getAppInfo()
244 : {
245 : // I would like to do all of this in initialSetup, but it will fail with
246 : // multiapps that reset. A reset deletes and rebuilds the FEProblems so all
247 : // of the pointers will be broken.
248 :
249 : // Clear the vectors since we've probably built them up from a previous call
250 65623 : _from_problems.clear();
251 65623 : _to_problems.clear();
252 65623 : _from_es.clear();
253 65623 : _to_es.clear();
254 65623 : _from_meshes.clear();
255 65623 : _to_meshes.clear();
256 65623 : _to_positions.clear();
257 65623 : _from_positions.clear();
258 65623 : _to_transforms.clear();
259 65623 : _from_transforms.clear();
260 : // Clear this map since we build it from scratch every time we transfer
261 : // Otherwise, this will cause two issues: 1) increasing memory usage
262 : // for a simulation that requires many transfers, 2) producing wrong results
263 : // when we do collective communication on this vector.
264 65623 : _to_local2global_map.clear();
265 65623 : _from_local2global_map.clear();
266 :
267 : // Build the vectors for to problems, from problems, and subapps positions.
268 65623 : if (_current_direction == FROM_MULTIAPP)
269 : {
270 32884 : _to_problems.push_back(&_from_multi_app->problemBase());
271 32884 : _to_positions.push_back(Point(0., 0., 0.));
272 32884 : getFromMultiAppInfo();
273 : }
274 32739 : else if (_current_direction == TO_MULTIAPP)
275 : {
276 30096 : _from_problems.push_back(&_to_multi_app->problemBase());
277 30096 : _from_positions.push_back(Point(0., 0., 0.));
278 30096 : getToMultiAppInfo();
279 : }
280 2643 : else if (_current_direction == BETWEEN_MULTIAPP)
281 : {
282 : mooseAssert(&_from_multi_app->problemBase().coordTransform() ==
283 : &_to_multi_app->problemBase().coordTransform(),
284 : "I believe these should be the same. If not, then it will be difficult to define a "
285 : "canonical reference frame.");
286 2643 : getToMultiAppInfo();
287 2643 : getFromMultiAppInfo();
288 : }
289 :
290 : // Build the from and to equation systems and mesh vectors.
291 138189 : for (unsigned int i = 0; i < _to_problems.size(); i++)
292 : {
293 : // TODO: Do I actually want es or displaced es?
294 72566 : _to_es.push_back(&_to_problems[i]->es());
295 72566 : if (_displaced_target_mesh && _to_problems[i]->getDisplacedProblem())
296 524 : _to_meshes.push_back(&_to_problems[i]->getDisplacedProblem()->mesh());
297 : else
298 72042 : _to_meshes.push_back(&_to_problems[i]->mesh());
299 : }
300 :
301 138425 : for (unsigned int i = 0; i < _from_problems.size(); i++)
302 : {
303 72802 : _from_es.push_back(&_from_problems[i]->es());
304 72802 : if (_displaced_source_mesh && _from_problems[i]->getDisplacedProblem())
305 863 : _from_meshes.push_back(&_from_problems[i]->getDisplacedProblem()->mesh());
306 : else
307 71939 : _from_meshes.push_back(&_from_problems[i]->mesh());
308 : }
309 :
310 65623 : MooseAppCoordTransform::MinimalData from_app_transform_construction_data{};
311 65623 : if (_communicator.rank() == 0)
312 : from_app_transform_construction_data =
313 47736 : _current_direction == TO_MULTIAPP
314 95472 : ? _to_multi_app->problemBase().coordTransform().minimalDataDescription()
315 47736 : : _from_multi_app->appProblemBase(0).coordTransform().minimalDataDescription();
316 65623 : _communicator.broadcast(from_app_transform_construction_data);
317 : _from_moose_app_transform =
318 65623 : std::make_unique<MooseAppCoordTransform>(from_app_transform_construction_data);
319 :
320 65623 : MooseAppCoordTransform::MinimalData to_app_transform_construction_data{};
321 65623 : if (_communicator.rank() == 0)
322 : to_app_transform_construction_data =
323 47736 : _current_direction == FROM_MULTIAPP
324 95472 : ? _from_multi_app->problemBase().coordTransform().minimalDataDescription()
325 47736 : : _to_multi_app->appProblemBase(0).coordTransform().minimalDataDescription();
326 65623 : _communicator.broadcast(to_app_transform_construction_data);
327 : _to_moose_app_transform =
328 65623 : std::make_unique<MooseAppCoordTransform>(to_app_transform_construction_data);
329 :
330 : /*
331 : * skip_coordinate_collapsing: whether to set the transform to skip coordinate collapsing
332 : * (from XYZ to RZ for example)
333 : * transforms: vector of transforms to add the new transforms to
334 : * moose_app_transform: base for the new transform
335 : * is_parent_app_transform: whether working on the transform for the parent app (this app, the
336 : * one creating the transfer) or for child apps
337 : * multiapp: pointer to the multiapp to obtain the position of the child apps
338 : */
339 131246 : auto create_multiapp_transforms = [this](auto & transforms,
340 : const auto & moose_app_transform,
341 : const bool is_parent_app_transform,
342 : const MultiApp * const multiapp = nullptr)
343 : {
344 : mooseAssert(is_parent_app_transform || multiapp,
345 : "Coordinate transform must be created either for child app or parent app");
346 131246 : if (is_parent_app_transform)
347 : {
348 62980 : transforms.push_back(std::make_unique<MultiAppCoordTransform>(moose_app_transform));
349 62980 : transforms.back()->skipCoordinateCollapsing(_skip_coordinate_collapsing);
350 : // zero translation
351 : }
352 : else
353 : {
354 : mooseAssert(transforms.size() == 0, "transforms should not be initialized at this point");
355 159635 : for (const auto i : make_range(multiapp->numGlobalApps()))
356 : {
357 91369 : transforms.push_back(std::make_unique<MultiAppCoordTransform>(moose_app_transform));
358 91369 : auto & transform = transforms[i];
359 91369 : transform->skipCoordinateCollapsing(_skip_coordinate_collapsing);
360 91369 : if (multiapp->usingPositions())
361 91369 : transform->setTranslationVector(multiapp->position(i));
362 : }
363 : }
364 131246 : };
365 :
366 65623 : if (_current_direction == TO_MULTIAPP)
367 : {
368 30096 : create_multiapp_transforms(
369 30096 : _to_transforms, *_to_moose_app_transform, false, _to_multi_app.get());
370 30096 : create_multiapp_transforms(_from_transforms, *_from_moose_app_transform, true);
371 : }
372 65623 : if (_current_direction == FROM_MULTIAPP)
373 : {
374 32884 : create_multiapp_transforms(_to_transforms, *_to_moose_app_transform, true);
375 32884 : create_multiapp_transforms(
376 32884 : _from_transforms, *_from_moose_app_transform, false, _from_multi_app.get());
377 : }
378 65623 : if (_current_direction == BETWEEN_MULTIAPP)
379 : {
380 2643 : create_multiapp_transforms(
381 2643 : _to_transforms, *_to_moose_app_transform, false, _to_multi_app.get());
382 2643 : create_multiapp_transforms(
383 2643 : _from_transforms, *_from_moose_app_transform, false, _from_multi_app.get());
384 : }
385 :
386 131240 : auto check_transform_compatibility = [this](const MultiAppCoordTransform & transform)
387 : {
388 131240 : if (transform.hasNonTranslationTransformation() && !usesMooseAppCoordTransform())
389 6 : mooseWarning("Transfer '",
390 6 : name(),
391 : "' of type '",
392 6 : type(),
393 : "' has non-translation transformations but it does not implement coordinate "
394 : "transformations using the 'MooseAppCoordTransform' class. Your data transfers "
395 : "will not be performed in the expected transformed frame");
396 196857 : };
397 :
398 : // set the destination coordinate systems for each transform for the purposes of determining
399 : // coordinate collapsing. For example if TO is XYZ and FROM is RZ, then TO will have its XYZ
400 : // coordinates collapsed into RZ and FROM will have a no-op for coordinate collapsing
401 :
402 143234 : for (const auto i : index_range(_from_transforms))
403 : {
404 77617 : auto & from_transform = _from_transforms[i];
405 77617 : from_transform->setDestinationCoordTransform(*_to_moose_app_transform);
406 77617 : if (i == 0)
407 65623 : check_transform_compatibility(*from_transform);
408 : }
409 142340 : for (const auto i : index_range(_to_transforms))
410 : {
411 76723 : auto & to_transform = _to_transforms[i];
412 76723 : to_transform->setDestinationCoordTransform(*_from_moose_app_transform);
413 76723 : if (i == 0)
414 65617 : check_transform_compatibility(*to_transform);
415 : }
416 65617 : }
417 :
418 : namespace
419 : {
420 : void
421 68266 : fillInfo(MultiApp & multi_app,
422 : std::vector<unsigned int> & map,
423 : std::vector<FEProblemBase *> & problems,
424 : std::vector<Point> & positions)
425 : {
426 159635 : for (unsigned int i_app = 0; i_app < multi_app.numGlobalApps(); i_app++)
427 : {
428 91369 : if (!multi_app.hasLocalApp(i_app))
429 8981 : continue;
430 :
431 82388 : auto & subapp_problem = multi_app.appProblemBase(i_app);
432 :
433 82388 : map.push_back(i_app);
434 82388 : problems.push_back(&subapp_problem);
435 82388 : if (multi_app.usingPositions())
436 82388 : positions.push_back(multi_app.position(i_app));
437 : }
438 68266 : }
439 : }
440 :
441 : void
442 32739 : MultiAppTransfer::getToMultiAppInfo()
443 : {
444 32739 : if (!_to_multi_app)
445 0 : mooseError("There is no to_multiapp to get info from");
446 :
447 32739 : fillInfo(*_to_multi_app, _to_local2global_map, _to_problems, _to_positions);
448 32739 : }
449 :
450 : void
451 35527 : MultiAppTransfer::getFromMultiAppInfo()
452 : {
453 35527 : if (!_from_multi_app)
454 0 : mooseError("There is no from_multiapp to get info from");
455 :
456 35527 : fillInfo(*_from_multi_app, _from_local2global_map, _from_problems, _from_positions);
457 35527 : }
458 :
459 : void
460 57625 : MultiAppTransfer::transformBoundingBox(BoundingBox & box, const MultiAppCoordTransform & transform)
461 : {
462 57625 : MultiApp::transformBoundingBox(box, transform);
463 57625 : }
464 :
465 : void
466 54816 : MultiAppTransfer::extendBoundingBoxes(const Real factor, std::vector<BoundingBox> & bboxes) const
467 : {
468 54816 : const auto extension_factor = factor - 1;
469 :
470 : // Extend (or contract if the extension factor is negative) bounding boxes along all the
471 : // directions by the same length. Greater than zero values of this member may be necessary because
472 : // the nearest bounding box does not necessarily give you the closest node/element. It will depend
473 : // on the partition and geometry. A node/element will more likely find its nearest source
474 : // element/node by extending bounding boxes. If each of the bounding boxes covers the entire
475 : // domain, a node/element will be able to find its nearest source element/node for sure, but at
476 : // the same time, more communication will be involved and can be expensive.
477 143322 : for (auto & box : bboxes)
478 : {
479 : // libmesh set an invalid bounding box using this code
480 : // for (unsigned int i=0; i<LIBMESH_DIM; i++)
481 : // {
482 : // this->first(i) = std::numeric_limits<Real>::max();
483 : // this->second(i) = -std::numeric_limits<Real>::max();
484 : // }
485 : // If it is an invalid box, we should skip it
486 88506 : if (box.first(0) == std::numeric_limits<Real>::max())
487 0 : continue;
488 :
489 88506 : auto width = box.second - box.first;
490 88506 : box.second += width * extension_factor;
491 88506 : box.first -= width * extension_factor;
492 : }
493 54816 : }
494 :
495 : std::vector<BoundingBox>
496 2386 : MultiAppTransfer::getFromBoundingBoxes()
497 : {
498 2386 : std::vector<std::pair<Point, Point>> bb_points(_from_meshes.size());
499 5260 : for (unsigned int i = 0; i < _from_meshes.size(); i++)
500 : {
501 : // Get a bounding box around the mesh elements that are local to the current
502 : // processor.
503 2874 : BoundingBox bbox = MeshTools::create_local_bounding_box(*_from_meshes[i]);
504 :
505 : // Translate the bounding box to the from domain's position. We may have rotations so we must
506 : // be careful in constructing the new min and max (first and second)
507 2874 : const auto from_global_num = getGlobalSourceAppIndex(i);
508 2874 : transformBoundingBox(bbox, *_from_transforms[from_global_num]);
509 :
510 : // Cast the bounding box into a pair of points (so it can be put through
511 : // MPI communication).
512 2874 : bb_points[i] = static_cast<std::pair<Point, Point>>(bbox);
513 : }
514 :
515 : // Serialize the bounding box points.
516 2386 : _communicator.allgather(bb_points);
517 :
518 : // Recast the points back into bounding boxes and return.
519 2386 : std::vector<BoundingBox> bboxes(bb_points.size());
520 6690 : for (unsigned int i = 0; i < bb_points.size(); i++)
521 4304 : bboxes[i] = static_cast<BoundingBox>(bb_points[i]);
522 :
523 : // possibly extend bounding boxes
524 2386 : extendBoundingBoxes(_bbox_factor, bboxes);
525 :
526 4772 : return bboxes;
527 2386 : }
528 :
529 : std::vector<BoundingBox>
530 88 : MultiAppTransfer::getFromBoundingBoxes(BoundaryID boundary_id)
531 : {
532 88 : std::vector<std::pair<Point, Point>> bb_points(_from_meshes.size());
533 88 : const Real min_r = std::numeric_limits<Real>::lowest();
534 88 : const Real max_r = std::numeric_limits<Real>::max();
535 :
536 189 : for (unsigned int i = 0; i < _from_meshes.size(); i++)
537 : {
538 :
539 101 : Point min(max_r, max_r, max_r);
540 101 : Point max(min_r, min_r, min_r);
541 101 : bool at_least_one = false;
542 :
543 : // TODO: Factor this into mesh_tools after adding new boundary bounding box routine.
544 101 : const ConstBndNodeRange & bnd_nodes = *_from_meshes[i]->getBoundaryNodeRange();
545 5713 : for (const auto & bnode : bnd_nodes)
546 : {
547 7014 : if (bnode->_bnd_id == boundary_id &&
548 1402 : bnode->_node->processor_id() == _from_meshes[i]->processor_id())
549 : {
550 1358 : at_least_one = true;
551 1358 : const auto & node = *bnode->_node;
552 5432 : for (const auto i : make_range(Moose::dim))
553 : {
554 4074 : min(i) = std::min(min(i), node(i));
555 4074 : max(i) = std::max(max(i), node(i));
556 : }
557 : }
558 : }
559 :
560 101 : BoundingBox bbox(min, max);
561 101 : if (!at_least_one)
562 9 : bbox.min() = max; // If we didn't hit any nodes, this will be _the_ minimum bbox
563 : else
564 : {
565 : // Translate the bounding box to the from domain's position. We may have rotations so we must
566 : // be careful in constructing the new min and max (first and second)
567 92 : const auto from_global_num = getGlobalSourceAppIndex(i);
568 92 : transformBoundingBox(bbox, *_from_transforms[from_global_num]);
569 : }
570 :
571 : // Cast the bounding box into a pair of points (so it can be put through
572 : // MPI communication).
573 101 : bb_points[i] = static_cast<std::pair<Point, Point>>(bbox);
574 : }
575 :
576 : // Serialize the bounding box points.
577 88 : _communicator.allgather(bb_points);
578 :
579 : // Recast the points back into bounding boxes and return.
580 88 : std::vector<BoundingBox> bboxes(bb_points.size());
581 231 : for (unsigned int i = 0; i < bb_points.size(); i++)
582 143 : bboxes[i] = static_cast<BoundingBox>(bb_points[i]);
583 :
584 : // possibly extend bounding boxes
585 88 : extendBoundingBoxes(_bbox_factor, bboxes);
586 :
587 176 : return bboxes;
588 88 : }
589 :
590 : std::vector<unsigned int>
591 54816 : MultiAppTransfer::getFromsPerProc()
592 : {
593 54816 : std::vector<unsigned int> froms_per_proc;
594 54816 : if (_to_multi_app)
595 27230 : froms_per_proc.resize(n_processors(), 1);
596 54816 : if (_from_multi_app)
597 : {
598 28760 : froms_per_proc.resize(n_processors());
599 28760 : _communicator.allgather(_from_multi_app->numLocalApps(), froms_per_proc);
600 : }
601 54816 : return froms_per_proc;
602 0 : }
603 :
604 : NumericVector<Real> &
605 1443 : MultiAppTransfer::getTransferVector(unsigned int i_local, std::string var_name)
606 : {
607 : mooseAssert(_to_multi_app, "getTransferVector only works for transfers to multiapps");
608 :
609 1443 : return _to_multi_app->appTransferVector(_to_local2global_map[i_local], var_name);
610 : }
611 :
612 : void
613 4112 : MultiAppTransfer::checkVariable(const FEProblemBase & fe_problem,
614 : const VariableName & var_name,
615 : const std::string & param_name) const
616 : {
617 4112 : if (!fe_problem.hasVariable(var_name))
618 : {
619 3 : if (param_name.empty())
620 3 : mooseError("The variable '", var_name, "' does not exist.");
621 : else
622 0 : paramError(param_name, "The variable '", var_name, "' does not exist.");
623 : }
624 4109 : }
625 :
626 : Point
627 938068 : MultiAppTransfer::mapBackWithoutCollapsing(MultiAppCoordTransform & transform,
628 : const Point & p,
629 : const std::string & phase) const
630 : {
631 938068 : if (transform.hasCoordinateSystemTypeChange())
632 : {
633 0 : if (!_skip_coordinate_collapsing)
634 0 : mooseInfo(phase + " cannot use the point in the app frame due to the "
635 : "non-uniqueness of the coordinate collapsing reverse mapping."
636 : " Coordinate collapse is ignored for this operation");
637 0 : transform.skipCoordinateCollapsing(true);
638 0 : const auto pt = transform.mapBack(p);
639 0 : transform.skipCoordinateCollapsing(false);
640 0 : return pt;
641 : }
642 : else
643 938068 : return transform.mapBack(p);
644 : }
645 :
646 : Point
647 937738 : MultiAppTransfer::getPointInSourceAppFrame(const Point & p,
648 : unsigned int local_i_from,
649 : const std::string & phase) const
650 : {
651 937738 : return mapBackWithoutCollapsing(
652 1875476 : *_from_transforms[getGlobalSourceAppIndex(local_i_from)], p, phase);
653 : }
654 :
655 : Point
656 330 : MultiAppTransfer::getPointInTargetAppFrame(const Point & p,
657 : unsigned int local_i_to,
658 : const std::string & phase) const
659 : {
660 330 : return mapBackWithoutCollapsing(*_to_transforms[getGlobalTargetAppIndex(local_i_to)], p, phase);
661 : }
662 :
663 : unsigned int
664 5595405 : MultiAppTransfer::getGlobalSourceAppIndex(unsigned int i_from) const
665 : {
666 : mooseAssert(_current_direction == TO_MULTIAPP || i_from < _from_local2global_map.size(),
667 : "Out of bounds local from-app index");
668 5595405 : return _current_direction == TO_MULTIAPP ? 0 : _from_local2global_map[i_from];
669 : }
670 :
671 : unsigned int
672 452359 : MultiAppTransfer::getGlobalTargetAppIndex(unsigned int i_to) const
673 : {
674 : mooseAssert(_current_direction == FROM_MULTIAPP || i_to < _to_local2global_map.size(),
675 : "Out of bounds local to-app index");
676 452359 : return _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
677 : }
678 :
679 : unsigned int
680 0 : MultiAppTransfer::getLocalSourceAppIndex(unsigned int i_from) const
681 : {
682 0 : return _current_direction == TO_MULTIAPP
683 0 : ? 0
684 0 : : _from_local2global_map[i_from] - _from_local2global_map[0];
685 : }
686 :
687 : void
688 27319 : MultiAppTransfer::checkParentAppUserObjectExecuteOn(const std::string & object_name) const
689 : {
690 : // Source app is not the parent, most execution schedules are fine since the transfer occurs after
691 : // the app has run NOTE: not true for siblings transfer
692 27319 : if (hasFromMultiApp())
693 0 : return;
694 : // Get user object from parent. We don't know the type
695 27319 : const auto & uo = _fe_problem.getUserObject<UserObject>(object_name);
696 : // If we are executing on transfers, every additional schedule is not a problem
697 27319 : if (uo.getExecuteOnEnum().contains(EXEC_TRANSFER))
698 25540 : return;
699 : // If we are transferring on the same schedule as we are executing, we are lagging. Is it on
700 : // purpose? We don't know, so we will give a warning unless silenced.
701 : // The derived-classes offer the parameter to silence this warning
702 : // Note: UOs execute before transfers on INITIAL so it's not a problem at this time
703 1790 : if (uo.getExecuteOnEnum().contains(_fe_problem.getCurrentExecuteOnFlag()) &&
704 11 : _fe_problem.getCurrentExecuteOnFlag() != EXEC_INITIAL)
705 0 : if (!isParamValid("warn_source_object_execution_schedule") ||
706 0 : getParam<bool>("warn_source_object_execution_schedule"))
707 0 : uo.paramWarning("execute_on",
708 0 : "This UserObject-derived class is being executed on '" +
709 0 : Moose::stringify(_fe_problem.getCurrentExecuteOnFlag()) +
710 0 : "' and also providing values for the '" + name() +
711 : "' transfer, on that same execution schedule. Because user objects are "
712 : "executed after transfers are, this means the values provided by this "
713 : "user object are lagged. If you are ok with this, then set the "
714 : "'warn_source_object_execution_schedule' parameter to false in this "
715 0 : "Transfer. If not, then execute '" +
716 0 : uo.name() +
717 : "' on TRANSFER by adding it to the 'execute_on' vector parameter.");
718 : }
719 :
720 : void
721 27506 : MultiAppTransfer::errorIfObjectExecutesOnTransferInSourceApp(const std::string & object_name) const
722 : {
723 : // parent app is the source app, EXEC_TRANSFER is fine
724 27506 : if (!hasFromMultiApp())
725 0 : return;
726 : // Get the app and problem
727 27506 : const auto & app = getFromMultiApp();
728 27506 : if (!app->hasApp())
729 0 : return;
730 27506 : const auto & problem = app->appProblemBase(app->firstLocalApp());
731 : // Use the warehouse to find the object
732 27506 : std::vector<SetupInterface *> objects_with_exec_on;
733 27506 : problem.theWarehouse()
734 55012 : .query()
735 27506 : .template condition<AttribName>(object_name)
736 27506 : .template condition<AttribExecOns>(EXEC_TRANSFER)
737 27506 : .queryInto(objects_with_exec_on);
738 27506 : if (objects_with_exec_on.size())
739 3 : mooseError("Object '" + object_name +
740 : "' should not be executed on EXEC_TRANSFER, because this transfer has "
741 : "indicated it does not support it.\nExecuting this object on TIMESTEP_END should be "
742 : "sufficient to get updated values.");
743 27503 : }
|