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

Generated by: LCOV version 1.14