LCOV - code coverage report
Current view: top level - src/transfers - MultiAppProjectionTransfer.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 235 238 98.7 %
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 "MultiAppProjectionTransfer.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "AddVariableAction.h"
      14             : #include "FEProblem.h"
      15             : #include "MooseMesh.h"
      16             : #include "MooseVariableFE.h"
      17             : #include "SystemBase.h"
      18             : #include "MooseAppCoordTransform.h"
      19             : 
      20             : #include "libmesh/dof_map.h"
      21             : #include "libmesh/linear_implicit_system.h"
      22             : #include "libmesh/mesh_function.h"
      23             : #include "libmesh/mesh_tools.h"
      24             : #include "libmesh/numeric_vector.h"
      25             : #include "libmesh/parallel_algebra.h"
      26             : #include "libmesh/quadrature_gauss.h"
      27             : #include "libmesh/sparse_matrix.h"
      28             : #include "libmesh/string_to_enum.h"
      29             : #include "libmesh/default_coupling.h"
      30             : 
      31             : // TIMPI includes
      32             : #include "timpi/parallel_sync.h"
      33             : 
      34             : using namespace libMesh;
      35             : 
      36             : void
      37         544 : assemble_l2(EquationSystems & es, const std::string & system_name)
      38             : {
      39             :   MultiAppProjectionTransfer * transfer =
      40         544 :       es.parameters.get<MultiAppProjectionTransfer *>("transfer");
      41         544 :   transfer->assembleL2(es, system_name);
      42         544 : }
      43             : 
      44             : registerMooseObject("MooseApp", MultiAppProjectionTransfer);
      45             : 
      46             : InputParameters
      47       14817 : MultiAppProjectionTransfer::validParams()
      48             : {
      49       14817 :   InputParameters params = MultiAppConservativeTransfer::validParams();
      50       14817 :   params.addClassDescription(
      51             :       "Perform a projection between a master and sub-application mesh of a field variable.");
      52             : 
      53       14817 :   MooseEnum proj_type("l2", "l2");
      54       14817 :   params.addParam<MooseEnum>("proj_type", proj_type, "The type of the projection.");
      55             : 
      56       44451 :   params.addParam<bool>("fixed_meshes",
      57       29634 :                         false,
      58             :                         "Set to true when the meshes are not changing (ie, "
      59             :                         "no movement or adaptivity).  This will cache some "
      60             :                         "information to speed up the transfer.");
      61             : 
      62             :   // Need one layer of ghosting
      63       14817 :   params.addRelationshipManager("ElementSideNeighborLayers",
      64             :                                 Moose::RelationshipManagerType::GEOMETRIC |
      65             :                                     Moose::RelationshipManagerType::ALGEBRAIC);
      66       14817 :   MultiAppTransfer::addBBoxFactorParam(params);
      67       29634 :   return params;
      68       14817 : }
      69             : 
      70         276 : MultiAppProjectionTransfer::MultiAppProjectionTransfer(const InputParameters & parameters)
      71             :   : MultiAppConservativeTransfer(parameters),
      72         276 :     _proj_type(getParam<MooseEnum>("proj_type")),
      73         276 :     _compute_matrix(true),
      74         276 :     _fixed_meshes(getParam<bool>("fixed_meshes")),
      75         828 :     _qps_cached(false)
      76             : {
      77         276 :   if (_to_var_names.size() != 1)
      78           0 :     paramError("variable", " Support single to-variable only ");
      79             : 
      80         276 :   if (_from_var_names.size() != 1)
      81           0 :     paramError("source_variable", " Support single from-variable only ");
      82         276 : }
      83             : 
      84             : void
      85         276 : MultiAppProjectionTransfer::initialSetup()
      86             : {
      87         276 :   MultiAppConservativeTransfer::initialSetup();
      88             : 
      89         276 :   _proj_sys.resize(_to_problems.size(), NULL);
      90             : 
      91         596 :   for (unsigned int i_to = 0; i_to < _to_problems.size(); i_to++)
      92             :   {
      93         320 :     FEProblemBase & to_problem = *_to_problems[i_to];
      94         320 :     EquationSystems & to_es = to_problem.es();
      95             : 
      96             :     // Add the projection system.
      97             :     FEType fe_type = to_problem
      98         320 :                          .getVariable(0,
      99             :                                       _to_var_name,
     100             :                                       Moose::VarKindType::VAR_ANY,
     101             :                                       Moose::VarFieldType::VAR_FIELD_STANDARD)
     102         320 :                          .feType();
     103             : 
     104         320 :     LinearImplicitSystem & proj_sys = to_es.add_system<LinearImplicitSystem>("proj-sys-" + name());
     105             : 
     106         640 :     proj_sys.get_dof_map().add_coupling_functor(
     107         320 :         proj_sys.get_dof_map().default_coupling(),
     108             :         false); // The false keeps it from getting added to the mesh
     109             : 
     110         320 :     _proj_var_num = proj_sys.add_variable("var", fe_type);
     111         320 :     proj_sys.attach_assemble_function(assemble_l2);
     112         320 :     _proj_sys[i_to] = &proj_sys;
     113             : 
     114             :     // Prevent the projection system from being written to checkpoint
     115             :     // files.  In the event of a recover or restart, we'll read the checkpoint
     116             :     // before this initialSetup method is called.  As a result, we'll find
     117             :     // systems in the checkpoint (the projection systems) that we don't know
     118             :     // what to do with, and there will be a crash.  We could fix this by making
     119             :     // the systems in the constructor, except we don't know how many sub apps
     120             :     // there are at the time of construction.  So instead, we'll just nuke the
     121             :     // projection system and rebuild it from scratch every recover/restart.
     122         320 :     proj_sys.hide_output() = true;
     123             : 
     124             :     // Reinitialize EquationSystems since we added a system.
     125         320 :     to_es.reinit();
     126             :   }
     127         276 : }
     128             : 
     129             : void
     130         544 : MultiAppProjectionTransfer::assembleL2(EquationSystems & es, const std::string & system_name)
     131             : {
     132             :   // Get the system and mesh from the input arguments.
     133         544 :   LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>(system_name);
     134         544 :   MeshBase & to_mesh = es.get_mesh();
     135             : 
     136             :   // Get the meshfunction evaluations and the map that was stashed in the es.
     137         544 :   std::vector<Real> & final_evals = *es.parameters.get<std::vector<Real> *>("final_evals");
     138             :   std::map<dof_id_type, unsigned int> & element_map =
     139         544 :       *es.parameters.get<std::map<dof_id_type, unsigned int> *>("element_map");
     140             : 
     141             :   // Setup system vectors and matrices.
     142         544 :   FEType fe_type = system.variable_type(0);
     143         544 :   std::unique_ptr<FEBase> fe(FEBase::build(to_mesh.mesh_dimension(), fe_type));
     144         544 :   QGauss qrule(to_mesh.mesh_dimension(), fe_type.default_quadrature_order());
     145         544 :   fe->attach_quadrature_rule(&qrule);
     146         544 :   const DofMap & dof_map = system.get_dof_map();
     147         544 :   DenseMatrix<Number> Ke;
     148         544 :   DenseVector<Number> Fe;
     149         544 :   std::vector<dof_id_type> dof_indices;
     150         544 :   const std::vector<Real> & JxW = fe->get_JxW();
     151         544 :   const std::vector<std::vector<Real>> & phi = fe->get_phi();
     152         544 :   auto & system_matrix = system.get_system_matrix();
     153             : 
     154       35184 :   for (const auto & elem : to_mesh.active_local_element_ptr_range())
     155             :   {
     156       17320 :     fe->reinit(elem);
     157             : 
     158       17320 :     dof_map.dof_indices(elem, dof_indices);
     159       17320 :     Ke.resize(dof_indices.size(), dof_indices.size());
     160       17320 :     Fe.resize(dof_indices.size());
     161             : 
     162       72528 :     for (unsigned int qp = 0; qp < qrule.n_points(); qp++)
     163             :     {
     164       55208 :       Real meshfun_eval = 0.;
     165       55208 :       if (element_map.find(elem->id()) != element_map.end())
     166             :       {
     167             :         // We have evaluations for this element.
     168       44568 :         meshfun_eval = final_evals[element_map[elem->id()] + qp];
     169             :       }
     170             : 
     171             :       // Now compute the element matrix and RHS contributions.
     172      263312 :       for (unsigned int i = 0; i < phi.size(); i++)
     173             :       {
     174             :         // RHS
     175      208104 :         Fe(i) += JxW[qp] * (meshfun_eval * phi[i][qp]);
     176             : 
     177      208104 :         if (_compute_matrix)
     178     1046160 :           for (unsigned int j = 0; j < phi.size(); j++)
     179             :           {
     180             :             // The matrix contribution
     181      838056 :             Ke(i, j) += JxW[qp] * (phi[i][qp] * phi[j][qp]);
     182             :           }
     183             :       }
     184       55208 :       dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices);
     185             : 
     186       55208 :       if (_compute_matrix)
     187       55208 :         system_matrix.add_matrix(Ke, dof_indices);
     188       55208 :       system.rhs->add_vector(Fe, dof_indices);
     189             :     }
     190         544 :   }
     191         544 : }
     192             : 
     193             : void
     194         443 : MultiAppProjectionTransfer::execute()
     195             : {
     196         443 :   TIME_SECTION(
     197             :       "MultiAppProjectionTransfer::execute()", 5, "Transferring variables through projection");
     198             : 
     199             :   ////////////////////
     200             :   // We are going to project the solutions by solving some linear systems.  In
     201             :   // order to assemble the systems, we need to evaluate the "from" domain
     202             :   // solutions at quadrature points in the "to" domain.  Some parallel
     203             :   // communication is necessary because each processor doesn't necessarily have
     204             :   // all the "from" information it needs to set its "to" values.  We don't want
     205             :   // to use a bunch of big all-to-all broadcasts, so we'll use bounding boxes to
     206             :   // figure out which processors have the information we need and only
     207             :   // communicate with those processors.
     208             :   //
     209             :   // Each processor will
     210             :   // 1. Check its local quadrature points in the "to" domains to see which
     211             :   //    "from" domains they might be in.
     212             :   // 2. Send quadrature points to the processors with "from" domains that might
     213             :   //    contain those points.
     214             :   // 3. Recieve quadrature points from other processors, evaluate its mesh
     215             :   //    functions at those points, and send the values back to the proper
     216             :   //    processor
     217             :   // 4. Recieve mesh function evaluations from all relevant processors and
     218             :   //    decide which one to use at every quadrature point (the lowest global app
     219             :   //    index always wins)
     220             :   // 5. And use the mesh function evaluations to assemble and solve an L2
     221             :   //    projection system on its local elements.
     222             :   ////////////////////
     223             : 
     224             :   ////////////////////
     225             :   // For every combination of global "from" problem and local "to" problem, find
     226             :   // which "from" bounding boxes overlap with which "to" elements.  Keep track
     227             :   // of which processors own bounding boxes that overlap with which elements.
     228             :   // Build vectors of quadrature points to send to other processors for mesh
     229             :   // function evaluations.
     230             :   ////////////////////
     231             : 
     232             :   // Get the bounding boxes for the "from" domains.
     233         443 :   std::vector<BoundingBox> bboxes = getFromBoundingBoxes();
     234             : 
     235             :   // Figure out how many "from" domains each processor owns.
     236         443 :   std::vector<unsigned int> froms_per_proc = getFromsPerProc();
     237             : 
     238         443 :   std::map<processor_id_type, std::vector<Point>> outgoing_qps;
     239             :   std::map<processor_id_type, std::map<std::pair<unsigned int, unsigned int>, unsigned int>>
     240         443 :       element_index_map;
     241             :   // element_index_map[i_to, element_id] = index
     242             :   // outgoing_qps[index] is the first quadrature point in element
     243             : 
     244         443 :   if (!_qps_cached)
     245             :   {
     246         907 :     for (unsigned int i_to = 0; i_to < _to_problems.size(); i_to++)
     247             :     {
     248             :       // Indexing into the coordinate transforms vector
     249             :       const auto to_global_num =
     250         504 :           _current_direction == FROM_MULTIAPP ? 0 : _to_local2global_map[i_to];
     251         504 :       MeshBase & to_mesh = _to_meshes[i_to]->getMesh();
     252             : 
     253         504 :       LinearImplicitSystem & system = *_proj_sys[i_to];
     254             : 
     255         504 :       FEType fe_type = system.variable_type(0);
     256         504 :       std::unique_ptr<FEBase> fe(FEBase::build(to_mesh.mesh_dimension(), fe_type));
     257         504 :       QGauss qrule(to_mesh.mesh_dimension(), fe_type.default_quadrature_order());
     258         504 :       fe->attach_quadrature_rule(&qrule);
     259         504 :       const std::vector<Point> & xyz = fe->get_xyz();
     260             : 
     261         504 :       unsigned int from0 = 0;
     262        1230 :       for (processor_id_type i_proc = 0; i_proc < n_processors();
     263         726 :            from0 += froms_per_proc[i_proc], i_proc++)
     264             :       {
     265         726 :         for (const auto & elem :
     266       45712 :              as_range(to_mesh.local_elements_begin(), to_mesh.local_elements_end()))
     267             :         {
     268       22130 :           fe->reinit(elem);
     269             : 
     270       22130 :           bool qp_hit = false;
     271       45700 :           for (unsigned int i_from = 0; i_from < froms_per_proc[i_proc] && !qp_hit; i_from++)
     272             :           {
     273       69067 :             for (unsigned int qp = 0; qp < qrule.n_points() && !qp_hit; qp++)
     274             :             {
     275       45497 :               Point qpt = xyz[qp];
     276       45497 :               if (bboxes[from0 + i_from].contains_point((*_to_transforms[to_global_num])(qpt)))
     277       13177 :                 qp_hit = true;
     278             :             }
     279             :           }
     280             : 
     281       22130 :           if (qp_hit)
     282             :           {
     283             :             // The selected processor's bounding box contains at least one
     284             :             // quadrature point from this element.  Send all qps from this element
     285             :             // and remember where they are in the array using the map.
     286       13177 :             std::pair<unsigned int, unsigned int> key(i_to, elem->id());
     287       13177 :             element_index_map[i_proc][key] = outgoing_qps[i_proc].size();
     288       56643 :             for (unsigned int qp = 0; qp < qrule.n_points(); qp++)
     289             :             {
     290       43466 :               Point qpt = xyz[qp];
     291       43466 :               outgoing_qps[i_proc].push_back((*_to_transforms[to_global_num])(qpt));
     292             :             }
     293             :           }
     294         726 :         }
     295             :       }
     296         504 :     }
     297             : 
     298         403 :     if (_fixed_meshes)
     299          48 :       _cached_index_map = element_index_map;
     300             :   }
     301             :   else
     302             :   {
     303          40 :     element_index_map = _cached_index_map;
     304             :   }
     305             : 
     306             :   ////////////////////
     307             :   // Request quadrature point evaluations from other processors and handle
     308             :   // requests sent to this processor.
     309             :   ////////////////////
     310             : 
     311             :   // Get the local bounding boxes.
     312         443 :   std::vector<BoundingBox> local_bboxes(froms_per_proc[processor_id()]);
     313             :   {
     314             :     // Find the index to the first of this processor's local bounding boxes.
     315         443 :     unsigned int local_start = 0;
     316         566 :     for (processor_id_type i_proc = 0; i_proc < n_processors() && i_proc != processor_id();
     317             :          i_proc++)
     318         123 :       local_start += froms_per_proc[i_proc];
     319             : 
     320             :     // Extract the local bounding boxes.
     321         906 :     for (unsigned int i_from = 0; i_from < froms_per_proc[processor_id()]; i_from++)
     322         463 :       local_bboxes[i_from] = bboxes[local_start + i_from];
     323             :   }
     324             : 
     325             :   // Setup the local mesh functions.
     326         443 :   std::vector<MeshFunction> local_meshfuns;
     327         906 :   for (unsigned int i_from = 0; i_from < _from_problems.size(); i_from++)
     328             :   {
     329         463 :     FEProblemBase & from_problem = *_from_problems[i_from];
     330         463 :     MooseVariableFEBase & from_var = from_problem.getVariable(
     331             :         0, _from_var_name, Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_STANDARD);
     332         463 :     System & from_sys = from_var.sys().system();
     333         463 :     unsigned int from_var_num = from_sys.variable_number(from_var.name());
     334             : 
     335         926 :     local_meshfuns.emplace_back(
     336         463 :         from_problem.es(), *from_sys.current_local_solution, from_sys.get_dof_map(), from_var_num);
     337         463 :     local_meshfuns.back().init();
     338         463 :     local_meshfuns.back().enable_out_of_mesh_mode(OutOfMeshValue);
     339             :   }
     340             : 
     341             :   // Recieve quadrature points from other processors, evaluate mesh frunctions
     342             :   // at those points, and send the values back.
     343         443 :   std::map<processor_id_type, std::vector<std::pair<Real, unsigned int>>> outgoing_evals_ids;
     344             : 
     345             :   // If there is no cached data, we need to do communication
     346             :   // Quadrature points I will receive from remote processors
     347         443 :   std::map<processor_id_type, std::vector<Point>> incoming_qps;
     348         443 :   if (!_qps_cached)
     349             :   {
     350         486 :     auto qps_action_functor = [&incoming_qps](processor_id_type pid, const std::vector<Point> & qps)
     351             :     {
     352             :       // Quadrature points from processor 'pid'
     353         486 :       auto & incoming_qps_from_pid = incoming_qps[pid];
     354             :       // Store data for late use
     355         486 :       incoming_qps_from_pid.reserve(incoming_qps_from_pid.size() + qps.size());
     356         486 :       std::copy(qps.begin(), qps.end(), std::back_inserter(incoming_qps_from_pid));
     357         889 :     };
     358             : 
     359         403 :     Parallel::push_parallel_vector_data(comm(), outgoing_qps, qps_action_functor);
     360             :   }
     361             : 
     362             :   // Cache data
     363         443 :   if (!_qps_cached)
     364         403 :     _cached_qps = incoming_qps;
     365             : 
     366         969 :   for (auto & qps : _cached_qps)
     367             :   {
     368         526 :     const processor_id_type pid = qps.first;
     369             : 
     370        1052 :     outgoing_evals_ids[pid].resize(qps.second.size(),
     371         526 :                                    std::make_pair(OutOfMeshValue, libMesh::invalid_uint));
     372             : 
     373       47492 :     for (unsigned int qp = 0; qp < qps.second.size(); qp++)
     374             :     {
     375       46966 :       Point qpt = qps.second[qp];
     376             : 
     377             :       // Loop until we've found the lowest-ranked app that actually contains
     378             :       // the quadrature point.
     379       94832 :       for (unsigned int i_from = 0; i_from < _from_problems.size(); i_from++)
     380             :       {
     381       47866 :         if (local_bboxes[i_from].contains_point(qpt))
     382             :         {
     383             :           const auto from_global_num =
     384       46966 :               _current_direction == TO_MULTIAPP ? 0 : _from_local2global_map[i_from];
     385       46966 :           outgoing_evals_ids[pid][qp].first =
     386       46966 :               (local_meshfuns[i_from])(_from_transforms[from_global_num]->mapBack(qpt));
     387       46966 :           if (_current_direction == FROM_MULTIAPP)
     388       10592 :             outgoing_evals_ids[pid][qp].second = from_global_num;
     389             :         }
     390             :       }
     391             :     }
     392             :   }
     393             : 
     394             :   ////////////////////
     395             :   // Gather all of the qp evaluations and pick out the best ones for each qp.
     396             :   ////////////////////
     397             : 
     398             :   // Values back from remote processors for my local quadrature points
     399         443 :   std::map<processor_id_type, std::vector<std::pair<Real, unsigned int>>> incoming_evals_ids;
     400             : 
     401             :   auto evals_action_functor =
     402         526 :       [&incoming_evals_ids](processor_id_type pid,
     403         526 :                             const std::vector<std::pair<Real, unsigned int>> & evals)
     404             :   {
     405             :     // evals for processor 'pid'
     406         526 :     auto & incoming_evals_ids_for_pid = incoming_evals_ids[pid];
     407             :     // Copy evals for late use
     408         526 :     incoming_evals_ids_for_pid.reserve(incoming_evals_ids_for_pid.size() + evals.size());
     409         526 :     std::copy(evals.begin(), evals.end(), std::back_inserter(incoming_evals_ids_for_pid));
     410         969 :   };
     411             : 
     412         443 :   Parallel::push_parallel_vector_data(comm(), outgoing_evals_ids, evals_action_functor);
     413             : 
     414         443 :   std::vector<std::vector<Real>> final_evals(_to_problems.size());
     415         443 :   std::vector<std::map<dof_id_type, unsigned int>> trimmed_element_maps(_to_problems.size());
     416             : 
     417         987 :   for (unsigned int i_to = 0; i_to < _to_problems.size(); i_to++)
     418             :   {
     419         544 :     MeshBase & to_mesh = _to_meshes[i_to]->getMesh();
     420         544 :     LinearImplicitSystem & system = *_proj_sys[i_to];
     421             : 
     422         544 :     FEType fe_type = system.variable_type(0);
     423         544 :     std::unique_ptr<FEBase> fe(FEBase::build(to_mesh.mesh_dimension(), fe_type));
     424         544 :     QGauss qrule(to_mesh.mesh_dimension(), fe_type.default_quadrature_order());
     425             : 
     426       35184 :     for (const auto & elem : to_mesh.active_local_element_ptr_range())
     427             :     {
     428       17320 :       qrule.init(*elem);
     429             : 
     430       17320 :       bool element_is_evaled = false;
     431       17320 :       std::vector<Real> evals(qrule.n_points(), 0.);
     432             : 
     433       72528 :       for (unsigned int qp = 0; qp < qrule.n_points(); qp++)
     434             :       {
     435       55208 :         unsigned int lowest_app_rank = libMesh::invalid_uint;
     436      123510 :         for (auto & values_ids : incoming_evals_ids)
     437             :         {
     438             :           // Current processor id
     439       68302 :           const processor_id_type pid = values_ids.first;
     440             : 
     441             :           // Ignore the selected processor if the element wasn't found in it's
     442             :           // bounding box.
     443             :           std::map<std::pair<unsigned int, unsigned int>, unsigned int> & map =
     444       68302 :               element_index_map[pid];
     445       68302 :           std::pair<unsigned int, unsigned int> key(i_to, elem->id());
     446       68302 :           if (map.find(key) == map.end())
     447       22142 :             continue;
     448       46966 :           unsigned int qp0 = map[key];
     449             : 
     450             :           // Ignore the selected processor if it's app has a higher rank than the
     451             :           // previously found lowest app rank.
     452       46966 :           if (_current_direction == FROM_MULTIAPP)
     453       10592 :             if (values_ids.second[qp0 + qp].second >= lowest_app_rank)
     454           0 :               continue;
     455             : 
     456             :           // Ignore the selected processor if the qp was actually outside the
     457             :           // processor's subapp's mesh.
     458       46966 :           if (values_ids.second[qp0 + qp].first == OutOfMeshValue)
     459         806 :             continue;
     460             : 
     461             :           // This is the best meshfunction evaluation so far, save it.
     462       46160 :           element_is_evaled = true;
     463       46160 :           evals[qp] = values_ids.second[qp0 + qp].first;
     464             :         }
     465             :       }
     466             : 
     467             :       // If we found good evaluations for any of the qps in this element, save
     468             :       // those evaluations for later.
     469       17320 :       if (element_is_evaled)
     470             :       {
     471       13904 :         trimmed_element_maps[i_to][elem->id()] = final_evals[i_to].size();
     472       58472 :         for (unsigned int qp = 0; qp < qrule.n_points(); qp++)
     473       44568 :           final_evals[i_to].push_back(evals[qp]);
     474             :       }
     475       17864 :     }
     476         544 :   }
     477             : 
     478             :   ////////////////////
     479             :   // We now have just one or zero mesh function values at all of our local
     480             :   // quadrature points.  Stash those values (and a map linking them to element
     481             :   // ids) in the equation systems parameters and project the solution.
     482             :   ////////////////////
     483             : 
     484         987 :   for (unsigned int i_to = 0; i_to < _to_problems.size(); i_to++)
     485             :   {
     486         544 :     _to_es[i_to]->parameters.set<std::vector<Real> *>("final_evals") = &final_evals[i_to];
     487         544 :     _to_es[i_to]->parameters.set<std::map<dof_id_type, unsigned int> *>("element_map") =
     488         544 :         &trimmed_element_maps[i_to];
     489         544 :     projectSolution(i_to);
     490         544 :     _to_es[i_to]->parameters.set<std::vector<Real> *>("final_evals") = NULL;
     491         544 :     _to_es[i_to]->parameters.set<std::map<dof_id_type, unsigned int> *>("element_map") = NULL;
     492             :   }
     493             : 
     494         443 :   if (_fixed_meshes)
     495          88 :     _qps_cached = true;
     496             : 
     497         443 :   postExecute();
     498         443 : }
     499             : 
     500             : void
     501         544 : MultiAppProjectionTransfer::projectSolution(unsigned int i_to)
     502             : {
     503         544 :   FEProblemBase & to_problem = *_to_problems[i_to];
     504         544 :   EquationSystems & proj_es = to_problem.es();
     505         544 :   LinearImplicitSystem & ls = *_proj_sys[i_to];
     506             :   // activate the current transfer
     507         544 :   proj_es.parameters.set<MultiAppProjectionTransfer *>("transfer") = this;
     508             : 
     509             :   // TODO: specify solver params in an input file
     510             :   // solver tolerance
     511         544 :   Real tol = proj_es.parameters.get<Real>("linear solver tolerance");
     512         544 :   proj_es.parameters.set<Real>("linear solver tolerance") = 1e-10; // set our tolerance
     513             :   // solve it
     514         544 :   ls.solve();
     515         544 :   proj_es.parameters.set<Real>("linear solver tolerance") = tol; // restore the original tolerance
     516             : 
     517             :   // copy projected solution into target es
     518         544 :   MeshBase & to_mesh = proj_es.get_mesh();
     519             : 
     520         544 :   MooseVariableFEBase & to_var = to_problem.getVariable(
     521             :       0, _to_var_name, Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_STANDARD);
     522         544 :   System & to_sys = to_var.sys().system();
     523         544 :   NumericVector<Number> * to_solution = to_sys.solution.get();
     524             : 
     525       46792 :   for (const auto & node : to_mesh.local_node_ptr_range())
     526             :   {
     527       39738 :     for (unsigned int comp = 0; comp < node->n_comp(to_sys.number(), to_var.number()); comp++)
     528             :     {
     529       16614 :       const dof_id_type proj_index = node->dof_number(ls.number(), _proj_var_num, comp);
     530       16614 :       const dof_id_type to_index = node->dof_number(to_sys.number(), to_var.number(), comp);
     531       16614 :       to_solution->set(to_index, (*ls.solution)(proj_index));
     532             :     }
     533         544 :   }
     534       35184 :   for (const auto & elem : to_mesh.active_local_element_ptr_range())
     535       22776 :     for (unsigned int comp = 0; comp < elem->n_comp(to_sys.number(), to_var.number()); comp++)
     536             :     {
     537        5456 :       const dof_id_type proj_index = elem->dof_number(ls.number(), _proj_var_num, comp);
     538        5456 :       const dof_id_type to_index = elem->dof_number(to_sys.number(), to_var.number(), comp);
     539        5456 :       to_solution->set(to_index, (*ls.solution)(proj_index));
     540         544 :     }
     541             : 
     542         544 :   to_solution->close();
     543         544 :   to_sys.update();
     544         544 : }

Generated by: LCOV version 1.14