LCOV - code coverage report
Current view: top level - src/transfers - MultiAppNearestNodeTransfer.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 345 388 88.9 %
Date: 2025-07-17 01:28:37 Functions: 9 9 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 "MultiAppNearestNodeTransfer.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "DisplacedProblem.h"
      14             : #include "FEProblem.h"
      15             : #include "MooseMesh.h"
      16             : #include "MooseTypes.h"
      17             : #include "MooseVariableFE.h"
      18             : #include "MooseAppCoordTransform.h"
      19             : 
      20             : #include "libmesh/system.h"
      21             : #include "libmesh/mesh_tools.h"
      22             : #include "libmesh/id_types.h"
      23             : #include "libmesh/parallel_algebra.h"
      24             : #include "libmesh/dof_object.h"
      25             : 
      26             : // TIMPI includes
      27             : #include "timpi/parallel_sync.h"
      28             : 
      29             : registerMooseObjectDeprecated("MooseApp", MultiAppNearestNodeTransfer, "12/31/2024 24:00");
      30             : 
      31             : InputParameters
      32       15529 : MultiAppNearestNodeTransfer::validParams()
      33             : {
      34       15529 :   InputParameters params = MultiAppConservativeTransfer::validParams();
      35       15529 :   params.addClassDescription(
      36             :       "Transfer the value to the target domain from the nearest node in the source domain.");
      37             : 
      38       15529 :   params.addParam<BoundaryName>(
      39             :       "source_boundary",
      40             :       "The boundary we are transferring from (if not specified, whole domain is used).");
      41       15529 :   params.addParam<std::vector<BoundaryName>>(
      42             :       "target_boundary",
      43             :       "The boundary we are transferring to (if not specified, whole domain is used).");
      44       46587 :   params.addParam<bool>("fixed_meshes",
      45       31058 :                         false,
      46             :                         "Set to true when the meshes are not changing (ie, "
      47             :                         "no movement or adaptivity).  This will cache "
      48             :                         "nearest node neighbors to greatly speed up the "
      49             :                         "transfer.");
      50             : 
      51       15529 :   MultiAppTransfer::addBBoxFactorParam(params);
      52       15529 :   return params;
      53           0 : }
      54             : 
      55         628 : MultiAppNearestNodeTransfer::MultiAppNearestNodeTransfer(const InputParameters & parameters)
      56             :   : MultiAppConservativeTransfer(parameters),
      57         628 :     _fixed_meshes(getParam<bool>("fixed_meshes")),
      58         628 :     _node_map(declareRestartableData<std::map<dof_id_type, Node *>>("node_map")),
      59         628 :     _distance_map(declareRestartableData<std::map<dof_id_type, Real>>("distance_map")),
      60         628 :     _neighbors_cached(declareRestartableData<bool>("neighbors_cached", false)),
      61         628 :     _cached_froms(declareRestartableData<std::map<processor_id_type, std::vector<unsigned int>>>(
      62             :         "cached_froms")),
      63         628 :     _cached_dof_ids(declareRestartableData<std::map<processor_id_type, std::vector<dof_id_type>>>(
      64             :         "cached_dof_ids")),
      65         628 :     _cached_from_inds(
      66         628 :         declareRestartableData<std::map<std::pair<unsigned int, dof_id_type>, unsigned int>>(
      67             :             "cached_from_ids")),
      68         628 :     _cached_qp_inds(
      69         628 :         declareRestartableData<std::map<std::pair<unsigned int, dof_id_type>, unsigned int>>(
      70        1256 :             "cached_qp_inds"))
      71             : {
      72         628 :   mooseDeprecated("MultiAppNearestNodeTransfer is deprecated. Use "
      73             :                   "MultiAppGeneralFieldNearestNodeTransfer instead and adapt the parameters");
      74             : 
      75         628 :   if (_to_var_names.size() != 1)
      76           0 :     paramError("variable", " Support single to-variable only");
      77             : 
      78         628 :   if (_from_var_names.size() != 1)
      79           0 :     paramError("source_variable", " Support single from-variable only");
      80         628 : }
      81             : 
      82             : void
      83        1308 : MultiAppNearestNodeTransfer::execute()
      84             : {
      85        1308 :   TIME_SECTION(
      86             :       "MultiAppNearestNodeTransfer::execute()", 5, "Transferring variables based on nearest nodes");
      87             : 
      88             :   // Get the bounding boxes for the "from" domains.
      89        1308 :   std::vector<BoundingBox> bboxes;
      90        1308 :   if (isParamValid("source_boundary"))
      91             :   {
      92          93 :     if (_from_meshes.size())
      93             :     {
      94          93 :       const auto & sb = getParam<BoundaryName>("source_boundary");
      95          93 :       if (!MooseMeshUtils::hasBoundaryName(_from_meshes[0]->getMesh(), sb))
      96           8 :         paramError("source_boundary", "The boundary '", sb, "' was not found in the mesh");
      97             : 
      98          85 :       bboxes = getFromBoundingBoxes(_from_meshes[0]->getBoundaryID(sb));
      99             :     }
     100             :     else
     101           0 :       bboxes = getFromBoundingBoxes(Moose::INVALID_BOUNDARY_ID);
     102             :   }
     103             :   else
     104        1215 :     bboxes = getFromBoundingBoxes();
     105             : 
     106             :   // Figure out how many "from" domains each processor owns.
     107        1300 :   std::vector<unsigned int> froms_per_proc = getFromsPerProc();
     108             : 
     109             :   ////////////////////
     110             :   // For every point in the local "to" domain, figure out which "from" domains
     111             :   // might contain its nearest neighbor, and send that point to the processors
     112             :   // that own those "from" domains.
     113             :   //
     114             :   // How do we know which "from" domains might contain the nearest neighbor, you
     115             :   // ask?  Well, consider two "from" domains, A and B.  If every point in A is
     116             :   // closer than every point in B, then we know that B cannot possibly contain
     117             :   // the nearest neighbor.  Hence, we'll only check A for the nearest neighbor.
     118             :   // We'll use the functions bboxMaxDistance and bboxMinDistance to figure out
     119             :   // if every point in A is closer than every point in B.
     120             :   ////////////////////
     121             : 
     122             :   // outgoing_qps = nodes/centroids we'll send to other processors.
     123             :   // Map processor to quadrature points. We will send these points to remote processors
     124        1300 :   std::map<processor_id_type, std::vector<Point>> outgoing_qps;
     125             :   // When we get results back, node_index_map will tell us which results go with
     126             :   // which points
     127             :   // <processor, <system_id, node_i>> --> point_id
     128             :   std::map<processor_id_type, std::map<std::pair<unsigned int, dof_id_type>, dof_id_type>>
     129        1300 :       node_index_map;
     130             : 
     131        1300 :   if (!_neighbors_cached)
     132             :   {
     133        2811 :     for (unsigned int i_to = 0; i_to < _to_problems.size(); i_to++)
     134             :     {
     135        1596 :       System * to_sys = find_sys(*_to_es[i_to], _to_var_name);
     136        1596 :       unsigned int sys_num = to_sys->number();
     137        1596 :       unsigned int var_num = to_sys->variable_number(_to_var_name);
     138        1596 :       MeshBase * to_mesh = &_to_meshes[i_to]->getMesh();
     139             :       const auto to_global_num =
     140        1596 :           _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
     141        1596 :       const auto & to_transform = *_to_transforms[to_global_num];
     142        1596 :       auto & fe_type = to_sys->variable_type(var_num);
     143        1596 :       bool is_constant = fe_type.order == CONSTANT;
     144        1596 :       bool is_to_nodal = fe_type.family == LAGRANGE;
     145             : 
     146             :       // We support L2_LAGRANGE elemental variable with the first order
     147        1596 :       if (fe_type.order > FIRST && !is_to_nodal)
     148           0 :         mooseError("We don't currently support second order or higher elemental variable ");
     149             : 
     150        1596 :       if (!is_to_nodal && isParamValid("target_boundary"))
     151           0 :         paramWarning("target_boundary",
     152             :                      "Setting a target boundary is only valid for receiving "
     153             :                      "variables of the LAGRANGE basis");
     154             : 
     155        1596 :       if (is_to_nodal)
     156             :       {
     157        1059 :         const std::vector<Node *> & target_local_nodes = getTargetLocalNodes(i_to);
     158             : 
     159             :         // For error checking: keep track of all target_local_nodes
     160             :         // which are successfully mapped to at least one domain where
     161             :         // the nearest neighbor might be found.
     162        1051 :         std::set<Node *> local_nodes_found;
     163             : 
     164      174853 :         for (const auto & node : target_local_nodes)
     165             :         {
     166             :           // Skip this node if the variable has no dofs at it.
     167      173802 :           if (node->n_dofs(sys_num, var_num) < 1)
     168        7040 :             continue;
     169             : 
     170      166762 :           const auto transformed_node = to_transform(*node);
     171             : 
     172             :           // Find which bboxes might have the nearest node to this point.
     173      166762 :           Real nearest_max_distance = std::numeric_limits<Real>::max();
     174      470950 :           for (const auto & bbox : bboxes)
     175             :           {
     176      304188 :             Real distance = bboxMaxDistance(transformed_node, bbox);
     177      304188 :             if (distance < nearest_max_distance)
     178      226756 :               nearest_max_distance = distance;
     179             :           }
     180             : 
     181      166762 :           unsigned int from0 = 0;
     182      448426 :           for (processor_id_type i_proc = 0; i_proc < n_processors();
     183      281664 :                from0 += froms_per_proc[i_proc], i_proc++)
     184             :           {
     185      585852 :             for (unsigned int i_from = from0; i_from < from0 + froms_per_proc[i_proc]; i_from++)
     186             :             {
     187      304188 :               Real distance = bboxMinDistance(transformed_node, bboxes[i_from]);
     188             : 
     189      315085 :               if (distance <= nearest_max_distance ||
     190       10897 :                   bboxes[i_from].contains_point(transformed_node))
     191             :               {
     192      293291 :                 std::pair<unsigned int, dof_id_type> key(i_to, node->id());
     193             :                 // Record a local ID for each quadrature point
     194      293291 :                 node_index_map[i_proc][key] = outgoing_qps[i_proc].size();
     195      293291 :                 outgoing_qps[i_proc].push_back(transformed_node);
     196      293291 :                 local_nodes_found.insert(node);
     197             :               }
     198             :             }
     199             :           }
     200             :         }
     201             : 
     202             :         // By the time we get to here, we should have found at least
     203             :         // one candidate BoundingBox for every node in the
     204             :         // target_local_nodes array that has dofs for the current
     205             :         // variable in the current System.
     206      174853 :         for (const auto & node : target_local_nodes)
     207      173802 :           if (node->n_dofs(sys_num, var_num) && !local_nodes_found.count(node))
     208           0 :             mooseError("In ",
     209           0 :                        name(),
     210             :                        ": No candidate BoundingBoxes found for node ",
     211           0 :                        node->id(),
     212             :                        " at position ",
     213           0 :                        to_transform(*node));
     214        1051 :       }
     215             :       else // Elemental
     216             :       {
     217             :         // For error checking: keep track of all local elements
     218             :         // which are successfully mapped to at least one domain where
     219             :         // the nearest neighbor might be found.
     220         537 :         std::set<Elem *> local_elems_found;
     221         537 :         std::vector<Point> points;
     222         537 :         std::vector<dof_id_type> point_ids;
     223       28935 :         for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
     224             :         {
     225             :           // Skip this element if the variable has no dofs at it.
     226       14199 :           if (elem->n_dofs(sys_num, var_num) < 1)
     227           0 :             continue;
     228             : 
     229       14199 :           points.clear();
     230       14199 :           point_ids.clear();
     231             :           // For constant monomial, we take the centroid of element
     232       14199 :           if (is_constant)
     233             :           {
     234       14199 :             points.push_back(to_transform(elem->vertex_average()));
     235       14199 :             point_ids.push_back(elem->id());
     236             :           }
     237             : 
     238             :           // For L2_LAGRANGE, we take all the nodes of element
     239             :           else
     240           0 :             for (auto & node : elem->node_ref_range())
     241             :             {
     242           0 :               points.push_back(to_transform(node));
     243           0 :               point_ids.push_back(node.id());
     244             :             }
     245             : 
     246       14199 :           unsigned int offset = 0;
     247       28398 :           for (auto & point : points)
     248             :           {
     249             :             // Find which bboxes might have the nearest node to this point.
     250       14199 :             Real nearest_max_distance = std::numeric_limits<Real>::max();
     251       42387 :             for (const auto & bbox : bboxes)
     252             :             {
     253       28188 :               Real distance = bboxMaxDistance(point, bbox);
     254       28188 :               if (distance < nearest_max_distance)
     255       18295 :                 nearest_max_distance = distance;
     256             :             }
     257             : 
     258       14199 :             unsigned int from0 = 0;
     259       33737 :             for (processor_id_type i_proc = 0; i_proc < n_processors();
     260       19538 :                  from0 += froms_per_proc[i_proc], i_proc++)
     261             :             {
     262       47726 :               for (unsigned int i_from = from0; i_from < from0 + froms_per_proc[i_proc]; i_from++)
     263             :               {
     264       28188 :                 Real distance = bboxMinDistance(point, bboxes[i_from]);
     265       28188 :                 if (distance <= nearest_max_distance || bboxes[i_from].contains_point(point))
     266             :                 {
     267             :                   std::pair<unsigned int, dof_id_type> key(
     268             :                       i_to,
     269       25260 :                       point_ids[offset]); // Create an unique ID
     270             :                   // If this point already exist, we skip it
     271       25260 :                   if (node_index_map[i_proc].find(key) != node_index_map[i_proc].end())
     272        6052 :                     continue;
     273       19208 :                   node_index_map[i_proc][key] = outgoing_qps[i_proc].size();
     274       19208 :                   outgoing_qps[i_proc].push_back(point);
     275       19208 :                   local_elems_found.insert(elem);
     276             :                 } // if distance
     277             :               }   // for i_from
     278             :             }     // for i_proc
     279       14199 :             offset++;
     280             :           } // point
     281         537 :         }   // for elem
     282             : 
     283             :         // Verify that we found at least one candidate bounding
     284             :         // box for each local element with dofs for the current
     285             :         // variable in the current System.
     286       28935 :         for (auto & elem : as_range(to_mesh->local_elements_begin(), to_mesh->local_elements_end()))
     287       14199 :           if (elem->n_dofs(sys_num, var_num) && !local_elems_found.count(elem))
     288           0 :             mooseError("In ",
     289           0 :                        name(),
     290             :                        ": No candidate BoundingBoxes found for Elem ",
     291           0 :                        elem->id(),
     292             :                        ", centroid = ",
     293         537 :                        to_transform(elem->vertex_average()));
     294         537 :       }
     295             :     }
     296             :   }
     297             : 
     298             :   ////////////////////
     299             :   // Send local node/centroid positions off to the other processors and take
     300             :   // care of points sent to this processor.  We'll need to check the points
     301             :   // against all of the "from" domains that this processor owns.  For each
     302             :   // point, we'll find the nearest node, then we'll send the value at that node
     303             :   // and the distance between the node and the point back to the processor that
     304             :   // requested that point.
     305             :   ////////////////////
     306             : 
     307        1292 :   std::map<processor_id_type, std::vector<Real>> incoming_evals;
     308             : 
     309             :   // Create these here so that they live the entire life of this function
     310             :   // and are NOT reused per processor.
     311        1292 :   std::map<processor_id_type, std::vector<Real>> processor_outgoing_evals;
     312             : 
     313        1292 :   if (!_neighbors_cached)
     314             :   {
     315             :     // Build an array of pointers to all of this processor's local entities (nodes or
     316             :     // elements).  We need to do this to avoid the expense of using LibMesh iterators.
     317             :     // This step also takes care of limiting the search to boundary nodes, if
     318             :     // applicable.
     319             :     std::vector<std::vector<std::pair<Point, DofObject *>>> local_entities(
     320        1215 :         froms_per_proc[processor_id()]);
     321             : 
     322        1215 :     std::vector<std::vector<unsigned int>> local_comps(froms_per_proc[processor_id()]);
     323             : 
     324             :     // Local array of all from Variable references
     325        1215 :     std::vector<std::reference_wrapper<MooseVariableFEBase>> _from_vars;
     326             : 
     327        2868 :     for (unsigned int i_local_from = 0; i_local_from < froms_per_proc[processor_id()];
     328             :          i_local_from++)
     329             :     {
     330        1653 :       MooseVariableFEBase & from_var = _from_problems[i_local_from]->getVariable(
     331             :           0, _from_var_name, Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_STANDARD);
     332        1653 :       auto & from_fe_type = from_var.feType();
     333        1653 :       bool is_constant = from_fe_type.order == CONSTANT;
     334        1653 :       bool is_to_nodal = from_fe_type.family == LAGRANGE;
     335             : 
     336             :       // We support L2_LAGRANGE elemental variable with the first order
     337        1653 :       if (from_fe_type.order > FIRST && !is_to_nodal)
     338           0 :         mooseError("We don't currently support second order or higher elemental variable ");
     339             : 
     340        1653 :       _from_vars.emplace_back(from_var);
     341        1653 :       getLocalEntitiesAndComponents(_from_meshes[i_local_from],
     342        1653 :                                     local_entities[i_local_from],
     343        1653 :                                     local_comps[i_local_from],
     344             :                                     is_to_nodal,
     345             :                                     is_constant);
     346             :     }
     347             : 
     348             :     // Quadrature points I will receive from remote processors
     349        1215 :     std::map<processor_id_type, std::vector<Point>> incoming_qps;
     350        1860 :     auto qps_action_functor = [&incoming_qps](processor_id_type pid, const std::vector<Point> & qps)
     351             :     {
     352             :       // Quadrature points from processor 'pid'
     353        1860 :       auto & incoming_qps_from_pid = incoming_qps[pid];
     354             :       // Store data for late use
     355        1860 :       incoming_qps_from_pid.reserve(incoming_qps_from_pid.size() + qps.size());
     356        1860 :       std::copy(qps.begin(), qps.end(), std::back_inserter(incoming_qps_from_pid));
     357        3075 :     };
     358             : 
     359        1215 :     Parallel::push_parallel_vector_data(comm(), outgoing_qps, qps_action_functor);
     360             : 
     361        3075 :     for (auto & qps : incoming_qps)
     362             :     {
     363        1860 :       const processor_id_type pid = qps.first;
     364             : 
     365        1860 :       if (_fixed_meshes)
     366             :       {
     367          51 :         auto & froms = _cached_froms[pid];
     368          51 :         froms.resize(qps.second.size());
     369          51 :         std::fill(froms.begin(), froms.end(), libMesh::invalid_uint);
     370             : 
     371          51 :         auto & dof_ids = _cached_dof_ids[pid];
     372          51 :         dof_ids.resize(qps.second.size());
     373          51 :         std::fill(dof_ids.begin(), dof_ids.end(), DofObject::invalid_id);
     374             :       }
     375             : 
     376        1860 :       std::vector<Real> & outgoing_evals = processor_outgoing_evals[pid];
     377             :       // Resize this vector to two times the size of the incoming_qps
     378             :       // vector because we are going to store both the value from the nearest
     379             :       // local node *and* the distance between the incoming_qp and that node
     380             :       // for later comparison purposes.
     381        1860 :       outgoing_evals.resize(2 * qps.second.size());
     382             : 
     383      314359 :       for (std::size_t qp = 0; qp < qps.second.size(); qp++)
     384             :       {
     385      312499 :         const Point & qpt = qps.second[qp];
     386      312499 :         outgoing_evals[2 * qp] = std::numeric_limits<Real>::max();
     387      768575 :         for (unsigned int i_local_from = 0; i_local_from < froms_per_proc[processor_id()];
     388             :              i_local_from++)
     389             :         {
     390      456076 :           MooseVariableFEBase & from_var = _from_vars[i_local_from];
     391      456076 :           System & from_sys = from_var.sys().system();
     392      456076 :           unsigned int from_sys_num = from_sys.number();
     393      456076 :           unsigned int from_var_num = from_sys.variable_number(from_var.name());
     394             :           const auto from_global_num =
     395      456076 :               _current_direction == TO_MULTIAPP ? 0 : _from_local2global_map[i_local_from];
     396      456076 :           const auto & from_transform = *_from_transforms[from_global_num];
     397             : 
     398    32138490 :           for (unsigned int i_node = 0; i_node < local_entities[i_local_from].size(); i_node++)
     399             :           {
     400             :             // Compute distance between the current incoming_qp to local node i_node. No need to
     401             :             // transform the 'to' because we already did it
     402             :             Real current_distance =
     403    31682414 :                 (qpt - from_transform(local_entities[i_local_from][i_node].first)).norm();
     404             : 
     405             :             // If an incoming_qp is equally close to two or more local nodes, then
     406             :             // the first one we test will "win", even though any of the others could
     407             :             // also potentially be chosen instead... there's no way to decide among
     408             :             // the set of all equidistant points.
     409             :             //
     410             :             // outgoing_evals[2 * qp] is the current closest distance between a local point and
     411             :             // the incoming_qp.
     412    31682414 :             if (current_distance < outgoing_evals[2 * qp])
     413             :             {
     414             :               // Assuming LAGRANGE!
     415     9696267 :               if (local_entities[i_local_from][i_node].second->n_dofs(from_sys_num, from_var_num) >
     416             :                   0)
     417             :               {
     418    19342024 :                 dof_id_type from_dof = local_entities[i_local_from][i_node].second->dof_number(
     419     9671012 :                     from_sys_num, from_var_num, local_comps[i_local_from][i_node]);
     420             : 
     421             :                 // The indexing of the outgoing_evals vector looks
     422             :                 // like [(distance, value), (distance, value), ...]
     423             :                 // for each incoming_qp. We only keep the value from
     424             :                 // the node with the smallest distance to the
     425             :                 // incoming_qp, and then we compare across all
     426             :                 // processors later and pick the closest one.
     427     9671012 :                 outgoing_evals[2 * qp] = current_distance;
     428     9671012 :                 outgoing_evals[2 * qp + 1] = (*from_sys.solution)(from_dof);
     429             : 
     430     9671012 :                 if (_fixed_meshes)
     431             :                 {
     432             :                   // Cache the nearest nodes.
     433       50000 :                   _cached_froms[pid][qp] = i_local_from;
     434       50000 :                   _cached_dof_ids[pid][qp] = from_dof;
     435             :                 }
     436             :               }
     437             :             }
     438             :           }
     439             :         }
     440             :       }
     441             :     }
     442        1215 :   }
     443             : 
     444             :   else // We've cached the nearest nodes.
     445             :   {
     446         196 :     for (auto & problem_from : _cached_froms)
     447             :     {
     448         119 :       const processor_id_type pid = problem_from.first;
     449         119 :       std::vector<Real> & outgoing_evals = processor_outgoing_evals[pid];
     450         119 :       outgoing_evals.resize(problem_from.second.size());
     451             : 
     452        8864 :       for (unsigned int qp = 0; qp < outgoing_evals.size(); qp++)
     453             :       {
     454        8745 :         const auto from_problem = problem_from.second[qp];
     455        8745 :         if (from_problem == libMesh::invalid_uint)
     456             :         {
     457             :           mooseAssert(_cached_dof_ids[pid][qp] == DofObject::invalid_id,
     458             :                       "The state of the from problem and dof id should match.");
     459           0 :           continue;
     460             :         }
     461             : 
     462             :         MooseVariableFEBase & from_var =
     463        8745 :             _from_problems[from_problem]->getVariable(0,
     464             :                                                       _from_var_name,
     465             :                                                       Moose::VarKindType::VAR_ANY,
     466             :                                                       Moose::VarFieldType::VAR_FIELD_STANDARD);
     467        8745 :         System & from_sys = from_var.sys().system();
     468        8745 :         dof_id_type from_dof = _cached_dof_ids[pid][qp];
     469        8745 :         outgoing_evals[qp] = (*from_sys.solution)(from_dof);
     470             :       }
     471             :     }
     472             :   }
     473             : 
     474             :   auto evals_action_functor =
     475        1979 :       [&incoming_evals](processor_id_type pid, const std::vector<Real> & evals)
     476             :   {
     477             :     // evals for processor 'pid'
     478        1979 :     auto & incoming_evals_for_pid = incoming_evals[pid];
     479             :     // Copy evals for late use
     480        1979 :     incoming_evals_for_pid.reserve(incoming_evals_for_pid.size() + evals.size());
     481        1979 :     std::copy(evals.begin(), evals.end(), std::back_inserter(incoming_evals_for_pid));
     482        3271 :   };
     483             : 
     484        1292 :   Parallel::push_parallel_vector_data(comm(), processor_outgoing_evals, evals_action_functor);
     485             : 
     486             :   ////////////////////
     487             :   // Gather all of the evaluations, find the nearest one for each node/element,
     488             :   // and apply the values.
     489             :   ////////////////////
     490             : 
     491        3042 :   for (unsigned int i_to = 0; i_to < _to_problems.size(); i_to++)
     492             :   {
     493             :     // Loop over the master nodes and set the value of the variable
     494        1750 :     System * to_sys = find_sys(*_to_es[i_to], _to_var_name);
     495             : 
     496        1750 :     unsigned int sys_num = to_sys->number();
     497        1750 :     unsigned int var_num = to_sys->variable_number(_to_var_name);
     498             : 
     499        1750 :     NumericVector<Real> * solution = nullptr;
     500        1750 :     switch (_current_direction)
     501             :     {
     502        1020 :       case TO_MULTIAPP:
     503        1020 :         solution = &getTransferVector(i_to, _to_var_name);
     504        1020 :         break;
     505         730 :       case FROM_MULTIAPP:
     506         730 :         solution = to_sys->solution.get();
     507         730 :         break;
     508           0 :       default:
     509           0 :         mooseError("Unknown direction");
     510             :     }
     511             : 
     512        1750 :     const MeshBase & to_mesh = _to_meshes[i_to]->getMesh();
     513             : 
     514        1750 :     auto & fe_type = to_sys->variable_type(var_num);
     515        1750 :     bool is_constant = fe_type.order == CONSTANT;
     516        1750 :     bool is_to_nodal = fe_type.family == LAGRANGE;
     517             : 
     518             :     // We support L2_LAGRANGE elemental variable with the first order
     519        1750 :     if (fe_type.order > FIRST && !is_to_nodal)
     520           0 :       mooseError("We don't currently support second order or higher elemental variable ");
     521             : 
     522        1750 :     if (is_to_nodal)
     523             :     {
     524        1180 :       const std::vector<Node *> & target_local_nodes = getTargetLocalNodes(i_to);
     525             : 
     526      178942 :       for (const auto & node : target_local_nodes)
     527             :       {
     528             :         // Skip this node if the variable has no dofs at it.
     529      177762 :         if (node->n_dofs(sys_num, var_num) < 1)
     530        7040 :           continue;
     531             : 
     532             :         // If nothing is in the node_index_map for a given local node,
     533             :         // it will get the value 0.
     534      170722 :         Real best_val = 0;
     535      170722 :         if (!_neighbors_cached)
     536             :         {
     537             :           // Search through all the incoming evaluation points from
     538             :           // different processors for the one with the closest
     539             :           // point. If there are multiple values from other processors
     540             :           // which are equidistant, the first one we check will "win".
     541      166762 :           Real min_dist = std::numeric_limits<Real>::max();
     542      448282 :           for (auto & evals : incoming_evals)
     543             :           {
     544             :             // processor Id
     545      281520 :             const processor_id_type pid = evals.first;
     546      281520 :             std::pair<unsigned int, dof_id_type> key(i_to, node->id());
     547      281520 :             if (node_index_map[pid].find(key) == node_index_map[pid].end())
     548       61119 :               continue;
     549      278469 :             unsigned int qp_ind = node_index_map[pid][key];
     550             :             // Distances
     551      278469 :             if (evals.second[2 * qp_ind] >= min_dist)
     552       58068 :               continue;
     553             : 
     554             :             // If we made it here, we are going to set a new value and
     555             :             // distance because we found one that was closer.
     556      220401 :             min_dist = evals.second[2 * qp_ind];
     557      220401 :             best_val = evals.second[2 * qp_ind + 1];
     558             : 
     559      220401 :             if (_fixed_meshes)
     560             :             {
     561             :               // Cache these indices.
     562        2365 :               _cached_from_inds[std::make_pair(i_to, node->id())] = pid;
     563        2365 :               _cached_qp_inds[std::make_pair(i_to, node->id())] = qp_ind;
     564             :             }
     565             :           }
     566             :         }
     567             : 
     568             :         else
     569             :         {
     570        3960 :           best_val = incoming_evals[_cached_from_inds[std::make_pair(i_to, node->id())]]
     571        3960 :                                    [_cached_qp_inds[std::make_pair(i_to, node->id())]];
     572             :         }
     573             : 
     574      170722 :         dof_id_type dof = node->dof_number(sys_num, var_num, 0);
     575      170722 :         solution->set(dof, best_val);
     576             :       }
     577             :     }
     578             :     else // Elemental
     579             :     {
     580         570 :       std::vector<Point> points;
     581         570 :       std::vector<dof_id_type> point_ids;
     582       33768 :       for (auto & elem : to_mesh.active_local_element_ptr_range())
     583             :       {
     584             :         // Skip this element if the variable has no dofs at it.
     585       16599 :         if (elem->n_dofs(sys_num, var_num) < 1)
     586           0 :           continue;
     587             : 
     588       16599 :         points.clear();
     589       16599 :         point_ids.clear();
     590             :         // grap sample points
     591             :         // for constant shape function, we take the element centroid
     592       16599 :         if (is_constant)
     593             :         {
     594       16599 :           points.push_back(elem->vertex_average());
     595       16599 :           point_ids.push_back(elem->id());
     596             :         }
     597             :         // for higher order method, we take all nodes of element
     598             :         // this works for the first order L2 Lagrange. Might not work
     599             :         // with something higher than the first order
     600             :         else
     601           0 :           for (auto & node : elem->node_ref_range())
     602             :           {
     603           0 :             points.push_back(node);
     604           0 :             point_ids.push_back(node.id());
     605             :           }
     606             : 
     607       16599 :         unsigned int n_comp = elem->n_comp(sys_num, var_num);
     608             :         // We assume each point corresponds to one component of elemental variable
     609       16599 :         if (points.size() != n_comp)
     610           0 :           mooseError(" Number of points ",
     611           0 :                      points.size(),
     612             :                      " does not equal to number of variable components ",
     613             :                      n_comp);
     614             : 
     615       33198 :         for (MooseIndex(points) offset = 0; offset < points.size(); offset++)
     616             :         {
     617       16599 :           dof_id_type point_id = point_ids[offset];
     618       16599 :           Real best_val = 0;
     619       16599 :           if (!_neighbors_cached)
     620             :           {
     621       14199 :             Real min_dist = std::numeric_limits<Real>::max();
     622       33737 :             for (auto & evals : incoming_evals)
     623             :             {
     624       19538 :               const processor_id_type pid = evals.first;
     625             : 
     626       19538 :               std::pair<unsigned int, dof_id_type> key(i_to, point_id);
     627       19538 :               if (node_index_map[pid].find(key) == node_index_map[pid].end())
     628        3321 :                 continue;
     629             : 
     630       19208 :               unsigned int qp_ind = node_index_map[pid][key];
     631       19208 :               if (evals.second[2 * qp_ind] >= min_dist)
     632        2991 :                 continue;
     633             : 
     634       16217 :               min_dist = evals.second[2 * qp_ind];
     635       16217 :               best_val = evals.second[2 * qp_ind + 1];
     636             : 
     637       16217 :               if (_fixed_meshes)
     638             :               {
     639             :                 // Cache these indices.
     640         920 :                 _cached_from_inds[std::make_pair(i_to, point_id)] = pid;
     641         920 :                 _cached_qp_inds[std::make_pair(i_to, point_id)] = qp_ind;
     642             :               } // if _fixed_meshes
     643             :             }   // i_from
     644             :           }     //
     645             :           else
     646             :           {
     647        2400 :             best_val = incoming_evals[_cached_from_inds[std::make_pair(i_to, point_id)]]
     648        2400 :                                      [_cached_qp_inds[std::make_pair(i_to, point_id)]];
     649             :           }
     650       16599 :           dof_id_type dof = elem->dof_number(sys_num, var_num, offset);
     651       16599 :           solution->set(dof, best_val);
     652             :         } // for offset
     653         570 :       }
     654         570 :     }
     655        1750 :     solution->close();
     656        1750 :     to_sys->update();
     657             :   }
     658             : 
     659        1292 :   if (_fixed_meshes)
     660         110 :     _neighbors_cached = true;
     661             : 
     662        1292 :   postExecute();
     663        1292 : }
     664             : 
     665             : Real
     666      332376 : MultiAppNearestNodeTransfer::bboxMaxDistance(const Point & p, const BoundingBox & bbox)
     667             : {
     668      332376 :   std::array<Point, 2> source_points = {{bbox.first, bbox.second}};
     669             : 
     670      332376 :   std::array<Point, 8> all_points;
     671      997128 :   for (unsigned int x = 0; x < 2; x++)
     672     1994256 :     for (unsigned int y = 0; y < 2; y++)
     673     3988512 :       for (unsigned int z = 0; z < 2; z++)
     674     2659008 :         all_points[x + 2 * y + 4 * z] =
     675     5318016 :             Point(source_points[x](0), source_points[y](1), source_points[z](2));
     676             : 
     677      332376 :   Real max_distance = 0.;
     678             : 
     679     2991384 :   for (unsigned int i = 0; i < 8; i++)
     680             :   {
     681     2659008 :     Real distance = (p - all_points[i]).norm();
     682     2659008 :     if (distance > max_distance)
     683      578088 :       max_distance = distance;
     684             :   }
     685             : 
     686      332376 :   return max_distance;
     687             : }
     688             : 
     689             : Real
     690      332376 : MultiAppNearestNodeTransfer::bboxMinDistance(const Point & p, const BoundingBox & bbox)
     691             : {
     692      332376 :   std::array<Point, 2> source_points = {{bbox.first, bbox.second}};
     693             : 
     694      332376 :   std::array<Point, 8> all_points;
     695      997128 :   for (unsigned int x = 0; x < 2; x++)
     696     1994256 :     for (unsigned int y = 0; y < 2; y++)
     697     3988512 :       for (unsigned int z = 0; z < 2; z++)
     698     2659008 :         all_points[x + 2 * y + 4 * z] =
     699     5318016 :             Point(source_points[x](0), source_points[y](1), source_points[z](2));
     700             : 
     701      332376 :   Real min_distance = std::numeric_limits<Real>::max();
     702             : 
     703     2991384 :   for (unsigned int i = 0; i < 8; i++)
     704             :   {
     705     2659008 :     Real distance = (p - all_points[i]).norm();
     706     2659008 :     if (distance < min_distance)
     707      572339 :       min_distance = distance;
     708             :   }
     709             : 
     710      332376 :   return min_distance;
     711             : }
     712             : 
     713             : void
     714        1653 : MultiAppNearestNodeTransfer::getLocalEntitiesAndComponents(
     715             :     MooseMesh * mesh,
     716             :     std::vector<std::pair<Point, DofObject *>> & local_entities,
     717             :     std::vector<unsigned int> & local_comps,
     718             :     bool is_nodal,
     719             :     bool is_constant)
     720             : {
     721             :   mooseAssert(mesh, "mesh should not be a nullptr");
     722             :   mooseAssert(local_entities.empty(), "local_entities should be empty");
     723        1653 :   MeshBase & mesh_base = mesh->getMesh();
     724             : 
     725        1653 :   if (isParamValid("source_boundary"))
     726             :   {
     727          87 :     const auto & sb = getParam<BoundaryName>("source_boundary");
     728          87 :     BoundaryID src_bnd_id = mesh->getBoundaryID(sb);
     729          87 :     if (!MooseMeshUtils::hasBoundaryName(mesh_base, sb))
     730           0 :       paramError("source_boundary", "The boundary '", sb, "' was not found in the mesh");
     731             : 
     732          87 :     if (is_nodal)
     733             :     {
     734          87 :       const ConstBndNodeRange & bnd_nodes = *mesh->getBoundaryNodeRange();
     735        4849 :       for (const auto & bnode : bnd_nodes)
     736             :       {
     737        4762 :         unsigned int comp = 0;
     738        5950 :         if (bnode->_bnd_id == src_bnd_id &&
     739        1188 :             bnode->_node->processor_id() == mesh_base.processor_id())
     740             :         {
     741        1144 :           local_entities.emplace_back(*bnode->_node, bnode->_node);
     742        1144 :           local_comps.push_back(comp++);
     743             :         }
     744             :       }
     745             :     }
     746             :     else
     747             :     {
     748           0 :       const ConstBndElemRange & bnd_elems = *mesh->getBoundaryElementRange();
     749           0 :       for (const auto & belem : bnd_elems)
     750             :       {
     751           0 :         unsigned int comp = 0;
     752           0 :         if (belem->_bnd_id == src_bnd_id &&
     753           0 :             belem->_elem->processor_id() == mesh_base.processor_id())
     754             :         {
     755             :           // CONSTANT Monomial
     756           0 :           if (is_constant)
     757             :           {
     758           0 :             local_entities.emplace_back(belem->_elem->vertex_average(), belem->_elem);
     759           0 :             local_comps.push_back(comp++);
     760             :           }
     761             :           // L2_LAGRANGE
     762             :           else
     763             :           {
     764           0 :             for (auto & node : belem->_elem->node_ref_range())
     765             :             {
     766           0 :               local_entities.emplace_back(node, belem->_elem);
     767           0 :               local_comps.push_back(comp++);
     768             :             }
     769             :           }
     770             :         }
     771             :       }
     772             :     }
     773             :   }
     774             :   else
     775             :   {
     776        1566 :     if (is_nodal)
     777             :     {
     778        1276 :       local_entities.reserve(mesh_base.n_local_nodes());
     779      114328 :       for (auto & node : mesh_base.local_node_ptr_range())
     780             :       {
     781      113052 :         unsigned int comp = 0;
     782      113052 :         local_entities.emplace_back(*node, node);
     783      113052 :         local_comps.push_back(comp++);
     784        1276 :       }
     785             :     }
     786             :     else
     787             :     {
     788         290 :       local_entities.reserve(mesh_base.n_local_elem());
     789       17730 :       for (auto & elem : mesh_base.active_local_element_ptr_range())
     790             :       {
     791        8720 :         unsigned int comp = 0;
     792             :         // CONSTANT Monomial
     793        8720 :         if (is_constant)
     794             :         {
     795        8720 :           local_entities.emplace_back(elem->vertex_average(), elem);
     796        8720 :           local_comps.push_back(comp++);
     797             :         }
     798             :         // L2_LAGRANGE
     799             :         else
     800             :         {
     801           0 :           for (auto & node : elem->node_ref_range())
     802             :           {
     803           0 :             local_entities.emplace_back(node, elem);
     804           0 :             local_comps.push_back(comp++);
     805             :           }
     806             :         }
     807         290 :       }
     808             :     }
     809             :   }
     810        1653 : }
     811             : 
     812             : const std::vector<Node *> &
     813        2239 : MultiAppNearestNodeTransfer::getTargetLocalNodes(const unsigned int to_problem_id)
     814             : {
     815        2239 :   _target_local_nodes.clear();
     816        2239 :   MeshBase & to_mesh = _to_meshes[to_problem_id]->getMesh();
     817             : 
     818        2239 :   if (isParamValid("target_boundary"))
     819             :   {
     820             :     const std::vector<BoundaryName> & target_boundaries =
     821         128 :         getParam<std::vector<BoundaryName>>("target_boundary");
     822         292 :     for (const auto & b : target_boundaries)
     823         172 :       if (!MooseMeshUtils::hasBoundaryName(to_mesh, b))
     824           8 :         paramError("target_boundary", "The boundary '", b, "' was not found in the mesh");
     825             : 
     826         120 :     ConstBndNodeRange & bnd_nodes = *(_to_meshes[to_problem_id])->getBoundaryNodeRange();
     827             : 
     828         284 :     for (const auto & t : target_boundaries)
     829             :     {
     830         164 :       BoundaryID target_bnd_id = _to_meshes[to_problem_id]->getBoundaryID(t);
     831             : 
     832        8564 :       for (const auto & bnode : bnd_nodes)
     833       10510 :         if (bnode->_bnd_id == target_bnd_id &&
     834        2110 :             bnode->_node->processor_id() == _to_meshes[to_problem_id]->processor_id())
     835        1952 :           _target_local_nodes.push_back(bnode->_node);
     836             :     }
     837             :   }
     838             :   else
     839             :   {
     840        2111 :     _target_local_nodes.resize(to_mesh.n_local_nodes());
     841        2111 :     unsigned int i = 0;
     842      351723 :     for (auto & node : to_mesh.local_node_ptr_range())
     843      351723 :       _target_local_nodes[i++] = node;
     844             :   }
     845             : 
     846        2231 :   return _target_local_nodes;
     847             : }

Generated by: LCOV version 1.14