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 333205 : MultiAppTransfer::validParams()
24 : {
25 333205 : InputParameters params = Transfer::validParams();
26 333205 : params.addDeprecatedParam<MultiAppName>("multi_app",
27 : "The name of the MultiApp to transfer data with",
28 : "Use to_multiapp & from_multiapp parameters now");
29 333205 : params.addParam<MultiAppName>("from_multi_app", "The name of the MultiApp to receive data from");
30 333205 : 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 333205 : ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
35 333205 : exec_enum.addAvailableFlags(EXEC_SAME_AS_MULTIAPP);
36 : // Add the POST_ADAPTIVITY execution flag.
37 : #ifdef LIBMESH_ENABLE_AMR
38 333205 : exec_enum.addAvailableFlags(EXEC_POST_ADAPTIVITY);
39 : #endif
40 333205 : exec_enum = EXEC_SAME_AS_MULTIAPP;
41 333205 : params.setDocString("execute_on", exec_enum.getDocString());
42 :
43 999615 : params.addParam<bool>(
44 : "check_multiapp_execute_on",
45 666410 : true,
46 : "When false the check between the multiapp and transfer execute on flags is not performed.");
47 999615 : params.addParam<bool>("displaced_source_mesh",
48 666410 : false,
49 : "Whether or not to use the displaced mesh for the source mesh.");
50 999615 : params.addParam<bool>("displaced_target_mesh",
51 666410 : false,
52 : "Whether or not to use the displaced mesh for the target mesh.");
53 333205 : addSkipCoordCollapsingParam(params);
54 333205 : return params;
55 0 : }
56 :
57 : void
58 59588 : MultiAppTransfer::addBBoxFactorParam(InputParameters & params)
59 : {
60 178764 : params.addRangeCheckedParam<Real>(
61 : "bbox_factor",
62 119176 : 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 59588 : }
70 :
71 : void
72 333205 : MultiAppTransfer::addSkipCoordCollapsingParam(InputParameters & params)
73 : {
74 999615 : params.addParam<bool>(
75 : "skip_coordinate_collapsing",
76 666410 : 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 333205 : params.addParamNamesToGroup("skip_coordinate_collapsing", "Advanced");
82 333205 : }
83 :
84 12493 : MultiAppTransfer::MultiAppTransfer(const InputParameters & parameters)
85 : : Transfer(parameters),
86 12493 : _skip_coordinate_collapsing(getParam<bool>("skip_coordinate_collapsing")),
87 12493 : _displaced_source_mesh(getParam<bool>("displaced_source_mesh")),
88 12493 : _displaced_target_mesh(getParam<bool>("displaced_target_mesh")),
89 24986 : _bbox_factor(isParamValid("bbox_factor") ? getParam<Real>("bbox_factor") : 1)
90 : {
91 : // Get the multiapps from their names
92 12493 : if (!isParamValid("multi_app"))
93 : {
94 12437 : if (isParamValid("from_multi_app"))
95 : {
96 7110 : _from_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("from_multi_app"));
97 7110 : _multi_app = _from_multi_app;
98 : }
99 12437 : if (isParamValid("to_multi_app"))
100 : {
101 6519 : _to_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("to_multi_app"));
102 6519 : _multi_app = _to_multi_app;
103 : }
104 30197 : if (!isParamValid("direction") && !isParamValid("from_multi_app") &&
105 17760 : !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 112 : for (const auto & dir : _directions)
112 : {
113 56 : if (dir == FROM_MULTIAPP)
114 : {
115 56 : _from_multi_app = _fe_problem.getMultiApp(getParam<MultiAppName>("multi_app"));
116 56 : _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 12489 : if (getParam<bool>("check_multiapp_execute_on"))
130 12359 : checkMultiAppExecuteOn();
131 :
132 : // Fill direction attributes, for backward compatibility but also convenience
133 12489 : if (!isParamValid("direction"))
134 : {
135 12429 : if (_from_multi_app && (!_to_multi_app || _from_multi_app == _to_multi_app))
136 5914 : _directions.setAdditionalValue("from_multiapp");
137 12429 : if (_to_multi_app && (!_from_multi_app || _from_multi_app == _to_multi_app))
138 5323 : _directions.setAdditionalValue("to_multiapp");
139 12429 : if (_from_multi_app && _to_multi_app && _from_multi_app != _to_multi_app)
140 1196 : _directions.setAdditionalValue("between_multiapp");
141 :
142 : // So it's available in the next constructors
143 12429 : _direction = _directions[0];
144 12429 : _current_direction = _directions[0];
145 : }
146 :
147 : // Handle deprecated parameters
148 12489 : if (parameters.isParamSetByUser("direction"))
149 : {
150 60 : 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 56 : 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 12481 : }
160 :
161 : void
162 12359 : MultiAppTransfer::checkMultiAppExecuteOn()
163 : {
164 12359 : if (_from_multi_app && !_to_multi_app)
165 5901 : if (getExecuteOnEnum() != _from_multi_app->getExecuteOnEnum())
166 261 : mooseDoOnce(
167 : mooseWarning("MultiAppTransfer execute_on flags do not match associated from_multi_app "
168 : "execute_on flags"));
169 :
170 12359 : if (_to_multi_app && !_from_multi_app)
171 5254 : if (getExecuteOnEnum() != _to_multi_app->getExecuteOnEnum())
172 254 : 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 12359 : if (_from_multi_app && _to_multi_app)
178 1207 : 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 12359 : }
184 :
185 : void
186 9955 : MultiAppTransfer::variableIntegrityCheck(const AuxVariableName & var_name,
187 : bool is_from_multiapp) const
188 : {
189 9955 : bool variable_found = false;
190 9955 : bool has_an_app = false;
191 :
192 : // Check the from_multi_app for the variable
193 9955 : if (is_from_multiapp && _from_multi_app)
194 13354 : for (unsigned int i = 0; i < _from_multi_app->numGlobalApps(); i++)
195 8721 : if (_from_multi_app->hasLocalApp(i))
196 : {
197 7332 : has_an_app = true;
198 7332 : if (_from_multi_app->appProblemBase(i).hasVariable(var_name))
199 7328 : variable_found = true;
200 : }
201 :
202 : // Check the to_multi_app for the variable
203 9955 : if (!is_from_multiapp && _to_multi_app)
204 15392 : for (unsigned int i = 0; i < _to_multi_app->numGlobalApps(); i++)
205 10070 : if (_to_multi_app->hasLocalApp(i))
206 : {
207 8497 : has_an_app = true;
208 8497 : if (_to_multi_app->appProblemBase(i).hasVariable(var_name))
209 8497 : variable_found = true;
210 : }
211 :
212 9955 : if (!variable_found && has_an_app)
213 4 : mooseError("Cannot find variable ", var_name, " for ", name(), " Transfer");
214 9951 : }
215 :
216 : void
217 12153 : MultiAppTransfer::initialSetup()
218 : {
219 : // Check for siblings transfer support
220 12153 : if (_to_multi_app && _from_multi_app)
221 1196 : checkSiblingsTransferSupported();
222 :
223 12153 : getAppInfo();
224 :
225 12145 : if (_from_multi_app)
226 6954 : _from_multi_app->addAssociatedTransfer(*this);
227 12145 : if (_to_multi_app)
228 6387 : _to_multi_app->addAssociatedTransfer(*this);
229 12145 : }
230 :
231 : void
232 74530 : 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 74530 : _from_problems.clear();
240 74530 : _to_problems.clear();
241 74530 : _from_es.clear();
242 74530 : _to_es.clear();
243 74530 : _from_meshes.clear();
244 74530 : _to_meshes.clear();
245 74530 : _to_positions.clear();
246 74530 : _from_positions.clear();
247 74530 : _to_transforms.clear();
248 74530 : _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 74530 : _to_local2global_map.clear();
254 74530 : _from_local2global_map.clear();
255 :
256 : // Build the vectors for to problems, from problems, and subapps positions.
257 74530 : if (_current_direction == FROM_MULTIAPP)
258 : {
259 34843 : _to_problems.push_back(&_from_multi_app->problemBase());
260 34843 : _to_positions.push_back(Point(0., 0., 0.));
261 34843 : getFromMultiAppInfo();
262 : }
263 39687 : else if (_current_direction == TO_MULTIAPP)
264 : {
265 32446 : _from_problems.push_back(&_to_multi_app->problemBase());
266 32446 : _from_positions.push_back(Point(0., 0., 0.));
267 32446 : getToMultiAppInfo();
268 : }
269 7241 : 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 7241 : getToMultiAppInfo();
276 7241 : getFromMultiAppInfo();
277 : }
278 :
279 : // Build the from and to equation systems and mesh vectors.
280 157522 : for (unsigned int i = 0; i < _to_problems.size(); i++)
281 : {
282 : // TODO: Do I actually want es or displaced es?
283 82992 : _to_es.push_back(&_to_problems[i]->es());
284 82992 : if (_displaced_target_mesh && _to_problems[i]->getDisplacedProblem())
285 574 : _to_meshes.push_back(&_to_problems[i]->getDisplacedProblem()->mesh());
286 : else
287 82418 : _to_meshes.push_back(&_to_problems[i]->mesh());
288 : }
289 :
290 157192 : for (unsigned int i = 0; i < _from_problems.size(); i++)
291 : {
292 82662 : _from_es.push_back(&_from_problems[i]->es());
293 82662 : if (_displaced_source_mesh && _from_problems[i]->getDisplacedProblem())
294 950 : _from_meshes.push_back(&_from_problems[i]->getDisplacedProblem()->mesh());
295 : else
296 81712 : _from_meshes.push_back(&_from_problems[i]->mesh());
297 : }
298 :
299 74530 : MooseAppCoordTransform::MinimalData from_app_transform_construction_data{};
300 74530 : if (_communicator.rank() == 0)
301 : from_app_transform_construction_data =
302 56052 : _current_direction == TO_MULTIAPP
303 112104 : ? _to_multi_app->problemBase().coordTransform().minimalDataDescription()
304 56052 : : _from_multi_app->appProblemBase(0).coordTransform().minimalDataDescription();
305 74530 : _communicator.broadcast(from_app_transform_construction_data);
306 : _from_moose_app_transform =
307 74530 : std::make_unique<MooseAppCoordTransform>(from_app_transform_construction_data);
308 :
309 74530 : MooseAppCoordTransform::MinimalData to_app_transform_construction_data{};
310 74530 : if (_communicator.rank() == 0)
311 : to_app_transform_construction_data =
312 56052 : _current_direction == FROM_MULTIAPP
313 112104 : ? _from_multi_app->problemBase().coordTransform().minimalDataDescription()
314 56052 : : _to_multi_app->appProblemBase(0).coordTransform().minimalDataDescription();
315 74530 : _communicator.broadcast(to_app_transform_construction_data);
316 : _to_moose_app_transform =
317 74530 : 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 149060 : auto create_multiapp_transforms = [this](auto & transforms,
329 : const auto & moose_app_transform,
330 : const bool is_parent_app_transform,
331 175519 : 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 149060 : if (is_parent_app_transform)
336 : {
337 67289 : transforms.push_back(std::make_unique<MultiAppCoordTransform>(moose_app_transform));
338 67289 : 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 190001 : for (const auto i : make_range(multiapp->numGlobalApps()))
345 : {
346 108230 : transforms.push_back(std::make_unique<MultiAppCoordTransform>(moose_app_transform));
347 108230 : auto & transform = transforms[i];
348 108230 : transform->skipCoordinateCollapsing(_skip_coordinate_collapsing);
349 108230 : if (multiapp->usingPositions())
350 108230 : transform->setTranslationVector(multiapp->position(i));
351 : }
352 : }
353 149060 : };
354 :
355 74530 : if (_current_direction == TO_MULTIAPP)
356 : {
357 32446 : create_multiapp_transforms(
358 32446 : _to_transforms, *_to_moose_app_transform, false, _to_multi_app.get());
359 32446 : create_multiapp_transforms(_from_transforms, *_from_moose_app_transform, true);
360 : }
361 74530 : if (_current_direction == FROM_MULTIAPP)
362 : {
363 34843 : create_multiapp_transforms(_to_transforms, *_to_moose_app_transform, true);
364 34843 : create_multiapp_transforms(
365 34843 : _from_transforms, *_from_moose_app_transform, false, _from_multi_app.get());
366 : }
367 74530 : if (_current_direction == BETWEEN_MULTIAPP)
368 : {
369 7241 : create_multiapp_transforms(
370 7241 : _to_transforms, *_to_moose_app_transform, false, _to_multi_app.get());
371 7241 : create_multiapp_transforms(
372 7241 : _from_transforms, *_from_moose_app_transform, false, _from_multi_app.get());
373 : }
374 :
375 150502 : auto check_transform_compatibility = [this](const MultiAppCoordTransform & transform)
376 : {
377 149052 : 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 223574 : };
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 162378 : for (const auto i : index_range(_from_transforms))
392 : {
393 87856 : auto & from_transform = _from_transforms[i];
394 87856 : from_transform->setDestinationCoordTransform(*_to_moose_app_transform);
395 87856 : if (i == 0)
396 74530 : check_transform_compatibility(*from_transform);
397 : }
398 161781 : for (const auto i : index_range(_to_transforms))
399 : {
400 87259 : auto & to_transform = _to_transforms[i];
401 87259 : to_transform->setDestinationCoordTransform(*_from_moose_app_transform);
402 87259 : if (i == 0)
403 74522 : check_transform_compatibility(*to_transform);
404 : }
405 74522 : }
406 :
407 : namespace
408 : {
409 : void
410 81771 : fillInfo(MultiApp & multi_app,
411 : std::vector<unsigned int> & map,
412 : std::vector<FEProblemBase *> & problems,
413 : std::vector<Point> & positions)
414 : {
415 190001 : for (unsigned int i_app = 0; i_app < multi_app.numGlobalApps(); i_app++)
416 : {
417 108230 : if (!multi_app.hasLocalApp(i_app))
418 9865 : continue;
419 :
420 98365 : auto & subapp_problem = multi_app.appProblemBase(i_app);
421 :
422 98365 : map.push_back(i_app);
423 98365 : problems.push_back(&subapp_problem);
424 98365 : if (multi_app.usingPositions())
425 98365 : positions.push_back(multi_app.position(i_app));
426 : }
427 81771 : }
428 : }
429 :
430 : void
431 39687 : MultiAppTransfer::getToMultiAppInfo()
432 : {
433 39687 : if (!_to_multi_app)
434 0 : mooseError("There is no to_multiapp to get info from");
435 :
436 39687 : fillInfo(*_to_multi_app, _to_local2global_map, _to_problems, _to_positions);
437 39687 : }
438 :
439 : void
440 42084 : MultiAppTransfer::getFromMultiAppInfo()
441 : {
442 42084 : if (!_from_multi_app)
443 0 : mooseError("There is no from_multiapp to get info from");
444 :
445 42084 : fillInfo(*_from_multi_app, _from_local2global_map, _from_problems, _from_positions);
446 42084 : }
447 :
448 : void
449 68106 : MultiAppTransfer::transformBoundingBox(BoundingBox & box, const MultiAppCoordTransform & transform)
450 : {
451 68106 : MultiApp::transformBoundingBox(box, transform);
452 68106 : }
453 :
454 : void
455 64460 : MultiAppTransfer::extendBoundingBoxes(const Real factor, std::vector<BoundingBox> & bboxes) const
456 : {
457 64460 : 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 165359 : 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 100899 : if (box.first(0) == std::numeric_limits<Real>::max())
476 0 : continue;
477 :
478 100899 : auto width = box.second - box.first;
479 100899 : box.second += width * extension_factor;
480 100899 : box.first -= width * extension_factor;
481 : }
482 64460 : }
483 :
484 : std::vector<BoundingBox>
485 2608 : MultiAppTransfer::getFromBoundingBoxes()
486 : {
487 2608 : std::vector<std::pair<Point, Point>> bb_points(_from_meshes.size());
488 5766 : 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 3158 : 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 3158 : const auto from_global_num = getGlobalSourceAppIndex(i);
497 3158 : 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 3158 : bb_points[i] = static_cast<std::pair<Point, Point>>(bbox);
502 : }
503 :
504 : // Serialize the bounding box points.
505 2608 : _communicator.allgather(bb_points);
506 :
507 : // Recast the points back into bounding boxes and return.
508 2608 : std::vector<BoundingBox> bboxes(bb_points.size());
509 7198 : for (unsigned int i = 0; i < bb_points.size(); i++)
510 4590 : bboxes[i] = static_cast<BoundingBox>(bb_points[i]);
511 :
512 : // possibly extend bounding boxes
513 2608 : extendBoundingBoxes(_bbox_factor, bboxes);
514 :
515 5216 : return bboxes;
516 2608 : }
517 :
518 : std::vector<BoundingBox>
519 97 : MultiAppTransfer::getFromBoundingBoxes(BoundaryID boundary_id)
520 : {
521 97 : std::vector<std::pair<Point, Point>> bb_points(_from_meshes.size());
522 97 : const Real min_r = std::numeric_limits<Real>::lowest();
523 97 : const Real max_r = std::numeric_limits<Real>::max();
524 :
525 210 : for (unsigned int i = 0; i < _from_meshes.size(); i++)
526 : {
527 :
528 113 : Point min(max_r, max_r, max_r);
529 113 : Point max(min_r, min_r, min_r);
530 113 : bool at_least_one = false;
531 :
532 : // TODO: Factor this into mesh_tools after adding new boundary bounding box routine.
533 113 : const ConstBndNodeRange & bnd_nodes = *_from_meshes[i]->getBoundaryNodeRange();
534 6555 : for (const auto & bnode : bnd_nodes)
535 : {
536 8052 : if (bnode->_bnd_id == boundary_id &&
537 1610 : bnode->_node->processor_id() == _from_meshes[i]->processor_id())
538 : {
539 1566 : at_least_one = true;
540 1566 : const auto & node = *bnode->_node;
541 6264 : for (const auto i : make_range(Moose::dim))
542 : {
543 4698 : min(i) = std::min(min(i), node(i));
544 4698 : max(i) = std::max(max(i), node(i));
545 : }
546 : }
547 : }
548 :
549 113 : BoundingBox bbox(min, max);
550 113 : 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 104 : const auto from_global_num = getGlobalSourceAppIndex(i);
557 104 : 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 113 : bb_points[i] = static_cast<std::pair<Point, Point>>(bbox);
563 : }
564 :
565 : // Serialize the bounding box points.
566 97 : _communicator.allgather(bb_points);
567 :
568 : // Recast the points back into bounding boxes and return.
569 97 : std::vector<BoundingBox> bboxes(bb_points.size());
570 252 : for (unsigned int i = 0; i < bb_points.size(); i++)
571 155 : bboxes[i] = static_cast<BoundingBox>(bb_points[i]);
572 :
573 : // possibly extend bounding boxes
574 97 : extendBoundingBoxes(_bbox_factor, bboxes);
575 :
576 194 : return bboxes;
577 97 : }
578 :
579 : std::vector<unsigned int>
580 64460 : MultiAppTransfer::getFromsPerProc()
581 : {
582 64460 : std::vector<unsigned int> froms_per_proc;
583 64460 : if (_to_multi_app)
584 34422 : froms_per_proc.resize(n_processors(), 1);
585 64460 : if (_from_multi_app)
586 : {
587 36083 : froms_per_proc.resize(n_processors());
588 36083 : _communicator.allgather(_from_multi_app->numLocalApps(), froms_per_proc);
589 : }
590 64460 : return froms_per_proc;
591 0 : }
592 :
593 : NumericVector<Real> &
594 1598 : 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 1598 : return _to_multi_app->appTransferVector(_to_local2global_map[i_local], var_name);
599 : }
600 :
601 : void
602 4358 : MultiAppTransfer::checkVariable(const FEProblemBase & fe_problem,
603 : const VariableName & var_name,
604 : const std::string & param_name) const
605 : {
606 4358 : 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 4354 : }
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 5766675 : 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 5766675 : return _current_direction == TO_MULTIAPP ? 0 : _from_local2global_map[i_from];
642 : }
643 :
644 : unsigned int
645 472470 : 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 472470 : 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 30802 : MultiAppTransfer::errorIfObjectExecutesOnTransferInSourceApp(const std::string & object_name) const
662 : {
663 : // parent app is the source app, EXEC_TRANSFER is fine
664 30802 : if (!hasFromMultiApp())
665 0 : return;
666 : // Get the app and problem
667 30802 : const auto & app = getFromMultiApp();
668 30802 : if (!app->hasApp())
669 0 : return;
670 30802 : const auto & problem = app->appProblemBase(app->firstLocalApp());
671 : // Use the warehouse to find the object
672 30802 : std::vector<SetupInterface *> objects_with_exec_on;
673 30802 : problem.theWarehouse()
674 61604 : .query()
675 30802 : .template condition<AttribName>(object_name)
676 30802 : .template condition<AttribExecOns>(EXEC_TRANSFER)
677 30802 : .queryInto(objects_with_exec_on);
678 30802 : 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 30798 : }
|