LCOV - code coverage report
Current view: top level - src/transfers - MultiAppTransfer.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 316 347 91.1 %
Date: 2026-05-29 20:35:17 Functions: 28 29 96.6 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.14