LCOV - code coverage report
Current view: top level - src/transfers - MultiAppUserObjectTransfer.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 1f9d31 Lines: 274 331 82.8 %
Date: 2025-09-02 20:01:20 Functions: 10 10 100.0 %
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             : #include "MultiAppUserObjectTransfer.h"
      11             : #include "MooseAppCoordTransform.h"
      12             : 
      13             : #include <limits>
      14             : 
      15             : // MOOSE includes
      16             : #include "DisplacedProblem.h"
      17             : #include "FEProblem.h"
      18             : #include "MooseMesh.h"
      19             : #include "MooseTypes.h"
      20             : #include "MooseVariableFE.h"
      21             : #include "MultiApp.h"
      22             : #include "UserObject.h"
      23             : 
      24             : // libMesh
      25             : #include "libmesh/meshfree_interpolation.h"
      26             : #include "libmesh/system.h"
      27             : #include "libmesh/mesh_function.h"
      28             : #include "libmesh/mesh_tools.h"
      29             : 
      30             : registerMooseObjectDeprecated("MooseApp", MultiAppUserObjectTransfer, "12/31/2024 24:00");
      31             : 
      32             : InputParameters
      33       15025 : MultiAppUserObjectTransfer::validParams()
      34             : {
      35       15025 :   InputParameters params = MultiAppConservativeTransfer::validParams();
      36             :   //  MultiAppUserObjectTransfer does not need source variable since it query values from user
      37             :   //  objects
      38       45075 :   params.set<std::vector<VariableName>>("source_variable") = std::vector<VariableName>{};
      39       30050 :   params.suppressParameter<std::vector<VariableName>>("source_variable");
      40       60100 :   params.addRequiredParam<UserObjectName>(
      41             :       "user_object",
      42             :       "The UserObject you want to transfer values from.  Note: This might be a "
      43             :       "UserObject from your MultiApp's input file!");
      44       45075 :   params.addParam<bool>("all_parent_nodes_contained_in_sub_app",
      45       30050 :                         false,
      46             :                         "Set to true if every parent app node is mapped to a distinct point on one "
      47             :                         "of the subApps during a transfer from sub App to Parent App. If parent app"
      48             :                         " node cannot be found within bounding boxes of any of the subApps, an "
      49             :                         " error is generated.");
      50       90150 :   params.addDeprecatedParam<bool>(
      51             :       "all_master_nodes_contained_in_sub_app",
      52             :       "Set to true if every parent app node is mapped to a distinct point on one "
      53             :       "of the subApps during a transfer from sub App to Parent App. If parent app"
      54             :       " node cannot be found within bounding boxes of any of the subApps, an "
      55             :       " error is generated.",
      56             :       "all_master_nodes_contained_in_sub_app is deprecated. Use "
      57             :       "all_parent_nodes_contained_in_sub_app");
      58       45075 :   params.addParam<bool>(
      59             :       "skip_bounding_box_check",
      60       30050 :       false,
      61             :       "Skip the check if the to_elem is within the bounding box of the from_app.");
      62       60100 :   params.addParam<std::vector<SubdomainName>>(
      63             :       "block", "The block we are transferring to (if not specified, whole domain is used).");
      64       60100 :   params.addParam<std::vector<BoundaryName>>(
      65             :       "boundary",
      66             :       "The boundary we are transferring to (if not specified, whole domain is used unless 'block' "
      67             :       "parameter is used).");
      68             : 
      69       30050 :   params.addClassDescription(
      70             :       "Samples a variable's value in the Parent app domain at the point where the MultiApp is and "
      71             :       "copies that value into a post-processor in the MultiApp");
      72             : 
      73       30050 :   params.addParam<bool>("nearest_sub_app",
      74       30050 :                         false,
      75             :                         "When True, a from_multiapp transfer will work by finding the nearest "
      76             :                         "(using the `location`) sub-app and query that for the value to transfer");
      77       15025 :   MultiAppTransfer::addUserObjectExecutionCheckParam(params);
      78             : 
      79       15025 :   return params;
      80           0 : }
      81             : 
      82         380 : MultiAppUserObjectTransfer::MultiAppUserObjectTransfer(const InputParameters & parameters)
      83             :   : MultiAppConservativeTransfer(parameters),
      84         380 :     _user_object_name(getParam<UserObjectName>("user_object")),
      85         380 :     _all_parent_nodes_contained_in_sub_app(
      86         760 :         isParamValid("all_master_nodes_contained_in_sub_app")
      87         768 :             ? getParam<bool>("all_master_nodes_contained_in_sub_app")
      88        1508 :             : getParam<bool>("all_parent_nodes_contained_in_sub_app")),
      89         760 :     _skip_bbox_check(getParam<bool>("skip_bounding_box_check")),
      90        1520 :     _nearest_sub_app(getParam<bool>("nearest_sub_app"))
      91             : {
      92         380 :   mooseDeprecated("MultiAppUserObjectTransfer is deprecated. Use "
      93             :                   "MultiAppGeneralFieldUserObjectTransfer instead and adapt the parameters");
      94             : 
      95             :   // This transfer does not work with DistributedMesh
      96         760 :   _fe_problem.mesh().errorIfDistributedMesh("MultiAppUserObjectTransfer");
      97             : 
      98         380 :   if (_to_var_names.size() != 1)
      99           0 :     paramError("variable", " Support single to-variable only ");
     100             : 
     101         380 :   if (_from_var_names.size() > 0)
     102           0 :     paramError("source_variable",
     103             :                " You should not provide any source variables since the transfer takes values from "
     104             :                "user objects ");
     105             : 
     106        1284 :   if (isParamValid("block") && isParamValid("boundary"))
     107           0 :     mooseError(name(), ": Transfer can be either block- or boundary-restricted. Not both.");
     108             : 
     109        1910 :   if (isParamValid("to_multi_app") && isParamValid("from_multi_app") &&
     110         380 :       getToMultiApp() != getFromMultiApp())
     111           0 :     paramError("to_multi_app",
     112             :                "Sibling multiapp transfer has not been implemented for this transfer.");
     113         380 : }
     114             : 
     115             : void
     116         589 : MultiAppUserObjectTransfer::execute()
     117             : {
     118        2945 :   TIME_SECTION(
     119             :       "MultiAppUserObjectTransfer::execute()", 5, "Performing transfer with a user object");
     120             : 
     121         589 :   getAppInfo();
     122             : 
     123             :   // Execute the user object if it was specified to execute on TRANSFER
     124         589 :   switch (_current_direction)
     125             :   {
     126         248 :     case TO_MULTIAPP:
     127             :     {
     128         248 :       checkParentAppUserObjectExecuteOn(_user_object_name);
     129         248 :       _fe_problem.computeUserObjectByName(EXEC_TRANSFER, Moose::PRE_AUX, _user_object_name);
     130         248 :       _fe_problem.computeUserObjectByName(EXEC_TRANSFER, Moose::POST_AUX, _user_object_name);
     131         248 :       break;
     132             :     }
     133         341 :     case FROM_MULTIAPP:
     134         341 :       errorIfObjectExecutesOnTransferInSourceApp(_user_object_name);
     135             :   }
     136             : 
     137         589 :   switch (_current_direction)
     138             :   {
     139         248 :     case TO_MULTIAPP:
     140             :     {
     141         510 :       for (unsigned int i = 0; i < getToMultiApp()->numGlobalApps(); i++)
     142             :       {
     143         274 :         if (getToMultiApp()->hasLocalApp(i))
     144             :         {
     145         262 :           Moose::ScopedCommSwapper swapper(getToMultiApp()->comm());
     146             : 
     147             :           // Loop over the parent app nodes and set the value of the variable
     148         262 :           System * to_sys = find_sys(getToMultiApp()->appProblemBase(i).es(), _to_var_name);
     149             : 
     150         262 :           unsigned int sys_num = to_sys->number();
     151         262 :           unsigned int var_num = to_sys->variable_number(_to_var_name);
     152             : 
     153         262 :           NumericVector<Real> & solution = getToMultiApp()->appTransferVector(i, _to_var_name);
     154             : 
     155         262 :           MooseMesh * mesh = NULL;
     156             : 
     157         262 :           if (_displaced_target_mesh && getToMultiApp()->appProblemBase(i).getDisplacedProblem())
     158          61 :             mesh = &getToMultiApp()->appProblemBase(i).getDisplacedProblem()->mesh();
     159             :           else
     160         201 :             mesh = &getToMultiApp()->appProblemBase(i).mesh();
     161             : 
     162         262 :           _blk_ids.clear();
     163         262 :           _bnd_ids.clear();
     164         786 :           if (isParamValid("block"))
     165             :           {
     166             :             const std::vector<SubdomainName> & blocks =
     167         224 :                 getParam<std::vector<SubdomainName>>("block");
     168         220 :             for (const auto & b : blocks)
     169         112 :               if (!MooseMeshUtils::hasSubdomainName(*mesh, b))
     170           8 :                 paramError("block", "The block '", b, "' was not found in the mesh");
     171             : 
     172         108 :             std::vector<SubdomainID> ids = mesh->getSubdomainIDs(blocks);
     173         108 :             _blk_ids.insert(ids.begin(), ids.end());
     174         108 :           }
     175         450 :           else if (isParamValid("boundary"))
     176             :           {
     177             :             const std::vector<BoundaryName> & boundary_names =
     178           8 :                 getParam<std::vector<BoundaryName>>("boundary");
     179           4 :             for (const auto & b : boundary_names)
     180           4 :               if (!MooseMeshUtils::hasBoundaryName(*mesh, b))
     181           8 :                 paramError("boundary", "The boundary '", b, "' was not found in the mesh");
     182             : 
     183           0 :             std::vector<BoundaryID> ids = mesh->getBoundaryIDs(boundary_names, true);
     184           0 :             _bnd_ids.insert(ids.begin(), ids.end());
     185           0 :           }
     186             : 
     187         254 :           auto & fe_type = to_sys->variable_type(var_num);
     188         254 :           bool is_constant = fe_type.order == CONSTANT;
     189         254 :           bool is_nodal = fe_type.family == LAGRANGE;
     190             : 
     191         254 :           if (fe_type.order > FIRST && !is_nodal)
     192           0 :             mooseError("We don't currently support second order or higher elemental variable ");
     193             : 
     194             :           const UserObject & user_object =
     195         254 :               getToMultiApp()->problemBase().getUserObjectBase(_user_object_name);
     196             :           mooseAssert(_from_transforms.size() == 1, "This should have size 1");
     197         254 :           const auto & from_transform = *_from_transforms[0];
     198         254 :           const auto & to_transform = *_to_transforms[i];
     199             : 
     200         254 :           if (is_nodal)
     201             :           {
     202        2643 :             for (auto & node : mesh->getMesh().local_node_ptr_range())
     203             :             {
     204        2550 :               if (blockRestricted() && !hasBlocks(mesh, node))
     205          76 :                 continue;
     206             : 
     207        2474 :               if (boundaryRestricted() && !isBoundaryNode(mesh, node))
     208           0 :                 continue;
     209             : 
     210        2474 :               if (node->n_dofs(sys_num, var_num) > 0) // If this variable has dofs at this node
     211             :               {
     212             :                 // The zero only works for LAGRANGE!
     213        2474 :                 dof_id_type dof = node->dof_number(sys_num, var_num, 0);
     214             : 
     215        2474 :                 swapper.forceSwap();
     216             :                 Real from_value =
     217        2474 :                     user_object.spatialValue(from_transform.mapBack(to_transform(*node)));
     218        2470 :                 swapper.forceSwap();
     219             : 
     220        2470 :                 solution.set(dof, from_value);
     221             :               }
     222          93 :             }
     223             :           }
     224             :           else // Elemental
     225             :           {
     226         157 :             std::vector<Point> points;
     227         314 :             for (auto & elem : as_range(mesh->getMesh().local_elements_begin(),
     228        7265 :                                         mesh->getMesh().local_elements_end()))
     229             :             {
     230        6794 :               if (blockRestricted() && !hasBlocks(elem))
     231          92 :                 continue;
     232             : 
     233        6702 :               if (boundaryRestricted() && !isBoundaryElem(mesh, elem))
     234           0 :                 continue;
     235             : 
     236             :               // Skip this element if the variable has no dofs at it.
     237        6702 :               if (elem->n_dofs(sys_num, var_num) < 1)
     238           0 :                 continue;
     239             : 
     240        6702 :               points.clear();
     241             :               // grap sample points
     242             :               // for constant shape function, we take the element centroid
     243        6702 :               if (is_constant)
     244        3102 :                 points.push_back(elem->vertex_average());
     245             :               // for higher order method, we take all nodes of element
     246             :               // this works for the first order L2 Lagrange.
     247             :               else
     248       18000 :                 for (auto & node : elem->node_ref_range())
     249       14400 :                   points.push_back(node);
     250             : 
     251        6702 :               auto n_points = points.size();
     252        6702 :               unsigned int n_comp = elem->n_comp(sys_num, var_num);
     253             :               // We assume each point corresponds to one component of elemental variable
     254        6702 :               if (n_points != n_comp)
     255           0 :                 mooseError(" Number of points ",
     256             :                            n_points,
     257             :                            " does not equal to number of variable components ",
     258             :                            n_comp);
     259             : 
     260        6702 :               unsigned int offset = 0;
     261       24204 :               for (auto & point : points) // If this variable has dofs at this elem
     262             :               {
     263       17502 :                 dof_id_type dof = elem->dof_number(sys_num, var_num, offset++);
     264             : 
     265       17502 :                 swapper.forceSwap();
     266             :                 Real from_value =
     267       17502 :                     user_object.spatialValue(from_transform.mapBack(to_transform(point)));
     268       17502 :                 swapper.forceSwap();
     269             : 
     270       17502 :                 solution.set(dof, from_value);
     271             :               }
     272         157 :             }
     273         157 :           }
     274             : 
     275         250 :           solution.close();
     276         250 :           to_sys->update();
     277         250 :         }
     278             :       }
     279             : 
     280         236 :       break;
     281             :     }
     282         341 :     case FROM_MULTIAPP:
     283             :     {
     284         341 :       FEProblemBase & to_problem = getFromMultiApp()->problemBase();
     285             :       mooseAssert(_to_transforms.size() == 1, "This should only be size one");
     286         341 :       const auto & to_transform = *_to_transforms[0];
     287         341 :       MooseVariableFEBase & to_var = to_problem.getVariable(
     288             :           0, _to_var_name, Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_STANDARD);
     289         341 :       SystemBase & to_system_base = to_var.sys();
     290             : 
     291         341 :       System & to_sys = to_system_base.system();
     292             : 
     293         341 :       unsigned int to_sys_num = to_sys.number();
     294             : 
     295             :       // Only works with a serialized mesh to transfer to!
     296             :       mooseAssert(to_sys.get_mesh().is_serial(),
     297             :                   "MultiAppUserObjectTransfer only works with ReplicatedMesh!");
     298             : 
     299         341 :       unsigned int to_var_num = to_sys.variable_number(to_var.name());
     300             : 
     301             :       // EquationSystems & to_es = to_sys.get_equation_systems();
     302             : 
     303             :       // Create a serialized version of the solution vector
     304         341 :       NumericVector<Number> * to_solution = to_sys.solution.get();
     305             : 
     306         341 :       MooseMesh * to_mesh = NULL;
     307             : 
     308         341 :       if (_displaced_target_mesh && to_problem.getDisplacedProblem())
     309           0 :         to_mesh = &to_problem.getDisplacedProblem()->mesh();
     310             :       else
     311         341 :         to_mesh = &to_problem.mesh();
     312             : 
     313         341 :       _blk_ids.clear();
     314         341 :       _bnd_ids.clear();
     315        1023 :       if (isParamValid("block"))
     316             :       {
     317           8 :         const std::vector<SubdomainName> & blocks = getParam<std::vector<SubdomainName>>("block");
     318           4 :         for (const auto & b : blocks)
     319           4 :           if (!MooseMeshUtils::hasSubdomainName(*to_mesh, b))
     320           8 :             paramError("block", "The block '", b, "' was not found in the mesh");
     321             : 
     322           0 :         std::vector<SubdomainID> ids = to_mesh->getSubdomainIDs(blocks);
     323           0 :         _blk_ids.insert(ids.begin(), ids.end());
     324           0 :       }
     325        1011 :       else if (isParamValid("boundary"))
     326             :       {
     327             :         const std::vector<BoundaryName> & boundary_names =
     328         216 :             getParam<std::vector<BoundaryName>>("boundary");
     329         212 :         for (const auto & b : boundary_names)
     330         108 :           if (!MooseMeshUtils::hasBoundaryName(*to_mesh, b))
     331           8 :             paramError("boundary", "The boundary '", b, "' was not found in the mesh");
     332             : 
     333         104 :         std::vector<BoundaryID> ids = to_mesh->getBoundaryIDs(boundary_names, true);
     334         104 :         _bnd_ids.insert(ids.begin(), ids.end());
     335         104 :       }
     336             : 
     337         333 :       auto & fe_type = to_sys.variable_type(to_var_num);
     338         333 :       bool is_constant = fe_type.order == CONSTANT;
     339         333 :       bool is_nodal = fe_type.family == LAGRANGE;
     340             : 
     341         333 :       if (fe_type.order > FIRST && !is_nodal)
     342           0 :         mooseError("We don't currently support second order or higher elemental variable ");
     343             : 
     344         333 :       if (_all_parent_nodes_contained_in_sub_app)
     345             :       {
     346             :         // check to see if parent app nodes or elements lies within any of the sub application
     347             :         // bounding boxes
     348           4 :         if (is_nodal)
     349             :         {
     350           0 :           for (auto & node : to_mesh->getMesh().node_ptr_range())
     351             :           {
     352           0 :             if (blockRestricted() && !hasBlocks(to_mesh, node))
     353           0 :               continue;
     354             : 
     355           0 :             if (boundaryRestricted() && !isBoundaryNode(to_mesh, node))
     356           0 :               continue;
     357             : 
     358           0 :             if (node->n_dofs(to_sys_num, to_var_num) > 0)
     359             :             {
     360           0 :               const auto transformed_node = to_transform(*node);
     361             : 
     362           0 :               unsigned int node_found_in_sub_app = 0;
     363           0 :               for (unsigned int i = 0; i < getFromMultiApp()->numGlobalApps(); i++)
     364             :               {
     365           0 :                 if (!getFromMultiApp()->hasLocalApp(i))
     366           0 :                   continue;
     367             : 
     368           0 :                 BoundingBox app_box = getFromMultiApp()->getBoundingBox(
     369           0 :                     i, _displaced_source_mesh, _from_transforms[i].get());
     370             : 
     371           0 :                 if (app_box.contains_point(transformed_node))
     372           0 :                   ++node_found_in_sub_app;
     373             :               }
     374             : 
     375           0 :               if (node_found_in_sub_app == 0)
     376           0 :                 mooseError("MultiAppUserObjectTransfer: Parent app node ",
     377             :                            transformed_node,
     378             :                            " not found within the bounding box of any of the sub applications.");
     379           0 :               else if (node_found_in_sub_app > 1)
     380           0 :                 mooseError("MultiAppUserObjectTransfer: Parent app node ",
     381             :                            transformed_node,
     382             :                            " found within the bounding box of two or more sub applications.");
     383             :             }
     384           0 :           }
     385             :         }
     386             :         else // elemental
     387             :         {
     388           4 :           std::vector<Point> points;
     389           4 :           for (auto & elem :
     390           8 :                as_range(to_mesh->getMesh().elements_begin(), to_mesh->getMesh().elements_end()))
     391             :           {
     392           4 :             if (blockRestricted() && !hasBlocks(elem))
     393           0 :               continue;
     394             : 
     395           4 :             if (boundaryRestricted() && !isBoundaryElem(to_mesh, elem))
     396           0 :               continue;
     397             : 
     398             :             // Skip this element if the variable has no dofs at it.
     399           4 :             if (elem->n_dofs(to_sys_num, to_var_num) < 1)
     400           0 :               continue;
     401             : 
     402           4 :             points.clear();
     403             :             // grap sample points
     404             :             // for constant shape function, we take the element centroid
     405           4 :             if (is_constant)
     406           4 :               points.push_back(to_transform(elem->vertex_average()));
     407             :             // for higher order method, we take all nodes of element
     408             :             // this works for the first order L2 Lagrange.
     409             :             else
     410           0 :               for (auto & node : elem->node_ref_range())
     411           0 :                 points.push_back(to_transform(node));
     412             : 
     413           4 :             auto n_points = points.size();
     414           4 :             unsigned int n_comp = elem->n_comp(to_sys_num, to_var_num);
     415             :             // We assume each point corresponds to one component of elemental variable
     416           4 :             if (n_points != n_comp)
     417           0 :               mooseError(" Number of points ",
     418             :                          n_points,
     419             :                          " does not equal to number of variable components ",
     420             :                          n_comp);
     421             : 
     422           4 :             for (auto & point : points)
     423             :             {
     424           4 :               unsigned int elem_found_in_sub_app = 0;
     425             : 
     426           8 :               for (unsigned int i = 0; i < getFromMultiApp()->numGlobalApps(); i++)
     427             :               {
     428           4 :                 if (!getFromMultiApp()->hasLocalApp(i))
     429           0 :                   continue;
     430             : 
     431           8 :                 BoundingBox app_box = getFromMultiApp()->getBoundingBox(
     432           4 :                     i, _displaced_source_mesh, _from_transforms[i].get());
     433             : 
     434           4 :                 if (app_box.contains_point(point))
     435           0 :                   ++elem_found_in_sub_app;
     436             :               }
     437             : 
     438           4 :               if (elem_found_in_sub_app == 0)
     439           8 :                 mooseError("MultiAppUserObjectTransfer: Parent app element with ",
     440           4 :                            n_points > 1 ? "node" : "centroid",
     441             :                            " at ",
     442             :                            point,
     443             :                            " not found within the bounding box of any of the sub applications.");
     444             : 
     445           0 :               else if (elem_found_in_sub_app > 1)
     446           0 :                 mooseError("MultiAppUserObjectTransfer: Parent app element with ",
     447           0 :                            n_points > 1 ? "node" : "centroid",
     448             :                            " at ",
     449             :                            point,
     450             :                            " found within the bounding box of two or more sub applications.");
     451             :             }
     452           0 :           }
     453           0 :         }
     454             :       }
     455             : 
     456         329 :       if (is_nodal)
     457             :       {
     458      247368 :         for (auto & node : to_mesh->getMesh().node_ptr_range())
     459             :         {
     460      247171 :           if (blockRestricted() && !hasBlocks(to_mesh, node))
     461           0 :             continue;
     462             : 
     463      247171 :           if (boundaryRestricted() && !isBoundaryNode(to_mesh, node))
     464        1800 :             continue;
     465             : 
     466      245371 :           if (node->n_dofs(to_sys_num, to_var_num) > 0) // If this variable has dofs at this node
     467             :           {
     468      245371 :             const auto transformed_node = to_transform(*node);
     469      245371 :             const auto sub_app = findSubAppToTransferFrom(transformed_node);
     470             : 
     471             :             // Check to see if a sub-app was found
     472      245371 :             if (sub_app == static_cast<unsigned int>(-1))
     473      145273 :               continue;
     474             : 
     475      100098 :             const auto & from_transform = *_from_transforms[sub_app];
     476      100098 :             const auto & user_object = _multi_app->appUserObjectBase(sub_app, _user_object_name);
     477             : 
     478      100098 :             dof_id_type dof = node->dof_number(to_sys_num, to_var_num, 0);
     479             : 
     480      100098 :             Real from_value = 0;
     481             :             {
     482      100098 :               Moose::ScopedCommSwapper swapper(getFromMultiApp()->comm());
     483      100098 :               from_value = user_object.spatialValue(from_transform.mapBack(transformed_node));
     484      100098 :             }
     485             : 
     486      100098 :             if (from_value == std::numeric_limits<Real>::infinity())
     487           0 :               mooseError("MultiAppUserObjectTransfer: Point corresponding to parent app node at (",
     488             :                          transformed_node,
     489             :                          ") not found in the sub application.");
     490      100098 :             to_solution->set(dof, from_value);
     491             :           }
     492         197 :         }
     493             :       }
     494             :       else // Elemental
     495             :       {
     496         132 :         std::vector<Point> points;
     497         132 :         for (auto & elem :
     498      216291 :              as_range(to_mesh->getMesh().elements_begin(), to_mesh->getMesh().elements_end()))
     499             :         {
     500      216027 :           if (blockRestricted() && !hasBlocks(elem))
     501        2168 :             continue;
     502             : 
     503      216027 :           if (boundaryRestricted() && !isBoundaryElem(to_mesh, elem))
     504         648 :             continue;
     505             : 
     506             :           // Skip this element if the variable has no dofs at it.
     507      215379 :           if (elem->n_dofs(to_sys_num, to_var_num) < 1)
     508        1520 :             continue;
     509             : 
     510      213859 :           points.clear();
     511             :           // grap sample points
     512             :           // for constant shape function, we take the element centroid
     513      213859 :           if (is_constant)
     514      213859 :             points.push_back(to_transform(elem->vertex_average()));
     515             :           // for higher order method, we take all nodes of element
     516             :           // this works for the first order L2 Lagrange.
     517             :           else
     518           0 :             for (auto & node : elem->node_ref_range())
     519           0 :               points.push_back(to_transform(node));
     520             : 
     521      213859 :           auto n_points = points.size();
     522      213859 :           unsigned int n_comp = elem->n_comp(to_sys_num, to_var_num);
     523             :           // We assume each point corresponds to one component of elemental variable
     524      213859 :           if (n_points != n_comp)
     525           0 :             mooseError(" Number of points ",
     526             :                        n_points,
     527             :                        " does not equal to number of variable components ",
     528             :                        n_comp);
     529             : 
     530      213859 :           unsigned int offset = 0;
     531      427718 :           for (auto & point : points) // If this variable has dofs at this elem
     532             :           {
     533      213859 :             const auto sub_app = findSubAppToTransferFrom(point);
     534             : 
     535             :             // Check to see if a sub-app was found
     536      213859 :             if (sub_app == static_cast<unsigned int>(-1))
     537      127560 :               continue;
     538             : 
     539       86299 :             const auto & from_transform = *_from_transforms[sub_app];
     540             :             const auto & user_object =
     541       86299 :                 getFromMultiApp()->appUserObjectBase(sub_app, _user_object_name);
     542             : 
     543       86299 :             dof_id_type dof = elem->dof_number(to_sys_num, to_var_num, offset++);
     544             : 
     545       86299 :             Real from_value = 0;
     546             :             {
     547       86299 :               Moose::ScopedCommSwapper swapper(getFromMultiApp()->comm());
     548       86299 :               from_value = user_object.spatialValue(from_transform.mapBack(point));
     549       86299 :             }
     550             : 
     551       86299 :             if (from_value == std::numeric_limits<Real>::infinity())
     552           0 :               mooseError("MultiAppUserObjectTransfer: Point corresponding to element's centroid (",
     553             :                          point,
     554             :                          ") not found in sub application.");
     555             : 
     556       86299 :             to_solution->set(dof, from_value);
     557             :           }
     558         132 :         }
     559         132 :       }
     560             : 
     561         329 :       to_solution->close();
     562         329 :       to_sys.update();
     563             : 
     564         329 :       break;
     565             :     }
     566             :   }
     567             : 
     568         565 :   postExecute();
     569         565 : }
     570             : 
     571             : bool
     572      472546 : MultiAppUserObjectTransfer::blockRestricted() const
     573             : {
     574      472546 :   return !_blk_ids.empty();
     575             : }
     576             : 
     577             : bool
     578      472378 : MultiAppUserObjectTransfer::boundaryRestricted() const
     579             : {
     580      472378 :   return !_bnd_ids.empty();
     581             : }
     582             : 
     583             : bool
     584         184 : MultiAppUserObjectTransfer::hasBlocks(const Elem * elem) const
     585             : {
     586         184 :   return _blk_ids.find(elem->subdomain_id()) != _blk_ids.end();
     587             : }
     588             : 
     589             : bool
     590         228 : MultiAppUserObjectTransfer::hasBlocks(const MooseMesh * mesh, const Node * node) const
     591             : {
     592         228 :   const auto & node_blk_ids = mesh->getNodeBlockIds(*node);
     593         228 :   std::set<SubdomainID> u;
     594         228 :   std::set_intersection(_blk_ids.begin(),
     595             :                         _blk_ids.end(),
     596             :                         node_blk_ids.begin(),
     597             :                         node_blk_ids.end(),
     598             :                         std::inserter(u, u.begin()));
     599         456 :   return !u.empty();
     600         228 : }
     601             : 
     602             : bool
     603        2400 : MultiAppUserObjectTransfer::isBoundaryNode(const MooseMesh * mesh, const Node * node) const
     604             : {
     605        4200 :   for (auto & bid : _bnd_ids)
     606        2400 :     if (mesh->isBoundaryNode(node->id(), bid))
     607         600 :       return true;
     608        1800 :   return false;
     609             : }
     610             : 
     611             : bool
     612         972 : MultiAppUserObjectTransfer::isBoundaryElem(const MooseMesh * mesh, const Elem * elem) const
     613             : {
     614        1620 :   for (auto & bid : _bnd_ids)
     615         972 :     if (mesh->isBoundaryElem(elem->id(), bid))
     616         324 :       return true;
     617         648 :   return false;
     618             : }
     619             : 
     620             : unsigned int
     621      459230 : MultiAppUserObjectTransfer::findSubAppToTransferFrom(const Point & p)
     622             : {
     623             :   // Just find the nearest app to this point
     624      459230 :   if (_nearest_sub_app)
     625             :   {
     626      224393 :     unsigned int closest_app = 0;
     627      224393 :     Real closest_distance = std::numeric_limits<Real>::max();
     628             : 
     629             :     mooseAssert(_multi_app->numGlobalApps() > 0, "No Multiapps To Transfer From");
     630             : 
     631      673179 :     for (unsigned int i = 0; i < _multi_app->numGlobalApps(); i++)
     632             :     {
     633             :       // Obtain the possibly transformed app position by querying the transform with the origin
     634      448786 :       const auto app_position = _multi_app->runningInPosition()
     635      448786 :                                     ? (*_from_transforms[i])(_multi_app->position(i))
     636      448786 :                                     : (*_from_transforms[i])(Point(0));
     637             : 
     638      448786 :       auto distance = (p - app_position).norm();
     639             : 
     640      448786 :       if (distance < closest_distance)
     641             :       {
     642      335686 :         closest_app = i;
     643      335686 :         closest_distance = distance;
     644             :       }
     645             :     }
     646             : 
     647             :     // We can only get the value if we have this app
     648             :     // otherwise - another processor will set it
     649      224393 :     if (_multi_app->hasLocalApp(closest_app))
     650      172610 :       return closest_app;
     651             :     else
     652       51783 :       return -1;
     653             :   }
     654             : 
     655             :   // Find the app that contains this point...
     656             : 
     657             :   // This loop counts _down_ so that it can preserve legacy behavior of the
     658             :   // last sub-app "winning" to be able to set the value at this point
     659      678616 :   for (int i = _multi_app->numGlobalApps() - 1; i >= 0; i--)
     660             :   {
     661      457566 :     if (!_multi_app->hasLocalApp(i))
     662      103339 :       continue;
     663             : 
     664             :     BoundingBox app_box =
     665      354227 :         _multi_app->getBoundingBox(i, _displaced_source_mesh, _from_transforms[i].get());
     666             : 
     667      354227 :     if (_skip_bbox_check || app_box.contains_point(p))
     668       13787 :       return static_cast<unsigned int>(i);
     669             :   }
     670             : 
     671      221050 :   return -1;
     672             : }

Generated by: LCOV version 1.14