LCOV - code coverage report
Current view: top level - src/transfers - MultiAppVariableValueSamplePostprocessorTransfer.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 158 166 95.2 %
Date: 2025-07-17 01:28:37 Functions: 8 9 88.9 %
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 "MultiAppVariableValueSamplePostprocessorTransfer.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "FEProblem.h"
      14             : #include "MooseMesh.h"
      15             : #include "MooseTypes.h"
      16             : #include "MooseVariableFE.h"
      17             : #include "MultiApp.h"
      18             : #include "AuxiliarySystem.h"
      19             : #include "MooseUtils.h"
      20             : #include "MooseAppCoordTransform.h"
      21             : 
      22             : #include "libmesh/meshfree_interpolation.h"
      23             : #include "libmesh/system.h"
      24             : 
      25             : #include "timpi/parallel_sync.h"
      26             : 
      27             : using namespace libMesh;
      28             : 
      29             : registerMooseObject("MooseApp", MultiAppVariableValueSamplePostprocessorTransfer);
      30             : 
      31             : InputParameters
      32       14941 : MultiAppVariableValueSamplePostprocessorTransfer::validParams()
      33             : {
      34       14941 :   InputParameters params = MultiAppTransfer::validParams();
      35       14941 :   params.addClassDescription(
      36             :       "Samples the value of a variable within the main application at each sub-application "
      37             :       "position and transfers the value to a postprocessor on the sub-application(s) when "
      38             :       "performing the to-multiapp transfer. Reconstructs the value of a CONSTANT MONOMIAL "
      39             :       "variable associating the value of each element to the value of the postprocessor "
      40             :       "in the closest sub-application whem performing the from-multiapp transfer.");
      41       14941 :   params.addRequiredParam<PostprocessorName>(
      42             :       "postprocessor",
      43             :       "The name of the postprocessor in the MultiApp to transfer the value to.  "
      44             :       "This should most likely be a Reciever Postprocessor.");
      45       14941 :   params.addRequiredParam<VariableName>("source_variable", "The variable to transfer from.");
      46       44823 :   params.addParam<unsigned int>(
      47             :       "source_variable_component",
      48       29882 :       0,
      49             :       "The component of source variable, may be non-zero for array variables.");
      50       44823 :   params.addParam<bool>(
      51             :       "map_array_variable_components_to_child_apps",
      52       29882 :       false,
      53             :       "When true, groups of sub-applications will be associated with different components of the "
      54             :       "supplied array variable in 'source_variable'. For instance, if there are 9 sub-applications "
      55             :       "and 3 components in the variable, sub-apps 0-2 will go to component 0, 3-5 will go to 1, "
      56             :       "and 6-8 will go to 2.");
      57       14941 :   return params;
      58           0 : }
      59             : 
      60         340 : MultiAppVariableValueSamplePostprocessorTransfer::MultiAppVariableValueSamplePostprocessorTransfer(
      61         340 :     const InputParameters & parameters)
      62             :   : MultiAppTransfer(parameters),
      63             :     MeshChangedInterface(parameters),
      64         340 :     _postprocessor_name(getParam<PostprocessorName>("postprocessor")),
      65         340 :     _var_name(getParam<VariableName>("source_variable")),
      66         340 :     _comp(getParam<unsigned int>("source_variable_component")),
      67         340 :     _var(_fe_problem.getVariable(0, _var_name)),
      68         680 :     _map_comp_to_child(getParam<bool>("map_array_variable_components_to_child_apps"))
      69             : {
      70         340 :   if (_directions.size() != 1)
      71           0 :     paramError("direction", "This transfer is only unidirectional");
      72             : 
      73         340 :   if (_directions.isValueSet("from_multiapp"))
      74             :   {
      75             :     // Check that the variable is a CONSTANT MONOMIAL.
      76         116 :     auto & fe_type = _var.feType();
      77         116 :     if (fe_type.order != CONSTANT || fe_type.family != MONOMIAL)
      78           0 :       paramError("source_variable",
      79             :                  "Variable must be in CONSTANT MONOMIAL when transferring from a postprocessor "
      80             :                  "from sub-apps.");
      81             : 
      82         116 :     if (!_fe_problem.getAuxiliarySystem().hasVariable(_var_name))
      83           4 :       paramError("source_variable", "Variable must be an auxiliary variable");
      84             :   }
      85         224 :   else if (_directions.isValueSet("between_multiapp"))
      86           0 :     mooseError("MultiAppVariableValueSamplePostprocessorTransfer has not been made to support "
      87             :                "sibling transfers");
      88             : 
      89         336 :   if (_map_comp_to_child && !_var.isArray())
      90           4 :     paramError("map_array_variable_components_to_child_apps",
      91             :                "'source_variable' must be an array variable when mapping array variable components "
      92             :                "to child applications.");
      93         332 :   if (_map_comp_to_child && parameters.isParamSetByUser("source_variable_component"))
      94           4 :     paramError("map_array_variable_components_to_child_apps",
      95             :                "'source_variable_component' is invalid when mapping array variable components to "
      96             :                "child applications.");
      97         328 : }
      98             : 
      99             : void
     100         304 : MultiAppVariableValueSamplePostprocessorTransfer::setupPostprocessorCommunication()
     101             : {
     102         304 :   if (!_directions.isValueSet("from_multiapp"))
     103         204 :     return;
     104             : 
     105         100 :   const auto num_global_apps = getFromMultiApp()->numGlobalApps();
     106             : 
     107             :   // Setup the communication pattern
     108         200 :   _postprocessor_to_processor_id.resize(num_global_apps,
     109         100 :                                         std::numeric_limits<processor_id_type>::max());
     110        1380 :   for (const auto i : make_range(num_global_apps))
     111        1280 :     if (getFromMultiApp()->hasLocalApp(i))
     112         968 :       _postprocessor_to_processor_id[i] = this->processor_id();
     113             : 
     114         100 :   _communicator.min(_postprocessor_to_processor_id);
     115             : #ifdef DEBUG
     116             :   for (const auto i : make_range(num_global_apps))
     117             :   {
     118             :     mooseAssert(_postprocessor_to_processor_id[i] != std::numeric_limits<processor_id_type>::max(),
     119             :                 "Every element in the vector should have been set.");
     120             :     if (getFromMultiApp()->hasLocalApp(i))
     121             :       mooseAssert(_postprocessor_to_processor_id[i] == this->processor_id(),
     122             :                   "If I owned this app, then the processor id value should be my own");
     123             :   }
     124             : #endif
     125             : }
     126             : 
     127             : void
     128         304 : MultiAppVariableValueSamplePostprocessorTransfer::cacheElemToPostprocessorData()
     129             : {
     130         304 :   if (!_directions.isValueSet("from_multiapp"))
     131         204 :     return;
     132             : 
     133             :   // Cache the Multiapp position ID for every element.
     134         100 :   auto & mesh = _fe_problem.mesh().getMesh();
     135         100 :   unsigned int multiapp_pos_id = 0;
     136        3924 :   for (auto & elem : as_range(mesh.active_local_elements_begin(), mesh.active_local_elements_end()))
     137             :     // Exclude the elements without dofs.
     138        1916 :     if (_var.hasBlocks(elem->subdomain_id()))
     139             :     {
     140             :       // The next two loops will loop through all the sub-applications
     141             :       // The first loop is over each component of the source variable we are transferring to/from
     142        1772 :       unsigned int j = 0; // Indicates sub-app index
     143        4584 :       for (unsigned int g = 0; g < getFromMultiApp()->numGlobalApps() / _apps_per_component; ++g)
     144             :       {
     145        2816 :         Real distance = std::numeric_limits<Real>::max();
     146        2816 :         unsigned int count = 0;
     147             :         // The second loop is over all the sub-apps the given component is associated with
     148       22752 :         for (unsigned int c = 0; c < _apps_per_component; ++c, ++j)
     149             :         {
     150       19936 :           Real current_distance = (getFromMultiApp()->position(j) - elem->true_centroid()).norm();
     151       19936 :           if (MooseUtils::absoluteFuzzyLessThan(current_distance, distance))
     152             :           {
     153        8014 :             distance = current_distance;
     154        8014 :             multiapp_pos_id = j;
     155        8014 :             count = 0;
     156             :           }
     157       11922 :           else if (MooseUtils::absoluteFuzzyEqual(current_distance, distance))
     158         530 :             ++count;
     159             :         }
     160        2816 :         if (count > 0)
     161           8 :           mooseWarning(
     162             :               "The distances of an element to more than one sub-applications are too close "
     163             :               " in transfer '",
     164           4 :               name(),
     165             :               "'. The code chooses the sub-application with the smallest ID to set "
     166             :               "the variable on the element, which may created undesired variable solutions."
     167             :               "\nHaving different positions for sub-applications, "
     168             :               "a centroid-based MultiApp or adding block restriction to the variable can "
     169             :               "be used to resolve this warning.");
     170             : 
     171             :         // Note: in case of count>0, the sub-application with smallest id will be used for the
     172             :         //       transfer.
     173        2812 :         _cached_multiapp_pos_ids.push_back(multiapp_pos_id);
     174        2812 :         _needed_postprocessors.insert(multiapp_pos_id);
     175             :       }
     176          96 :     }
     177             : }
     178             : 
     179             : void
     180         312 : MultiAppVariableValueSamplePostprocessorTransfer::initialSetup()
     181             : {
     182         312 :   MultiAppTransfer::initialSetup();
     183             : 
     184         616 :   unsigned int num_apps = _directions.isValueSet("from_multiapp")
     185         616 :                               ? getFromMultiApp()->numGlobalApps()
     186         308 :                               : getToMultiApp()->numGlobalApps();
     187         308 :   if (_map_comp_to_child && num_apps % _var.count() != 0)
     188           4 :     paramError("map_array_variable_components_to_child_apps",
     189             :                "The number of sub-applications (",
     190             :                num_apps,
     191             :                ") is not divisible by the number of components in '",
     192           4 :                _var_name,
     193             :                "' (",
     194           4 :                _var.count(),
     195             :                ").");
     196         304 :   _apps_per_component = _map_comp_to_child ? num_apps / _var.count() : num_apps;
     197             : 
     198         304 :   setupPostprocessorCommunication();
     199         304 :   cacheElemToPostprocessorData();
     200         300 : }
     201             : 
     202             : void
     203           0 : MultiAppVariableValueSamplePostprocessorTransfer::meshChanged()
     204             : {
     205           0 :   cacheElemToPostprocessorData();
     206           0 : }
     207             : 
     208             : void
     209         512 : MultiAppVariableValueSamplePostprocessorTransfer::execute()
     210             : {
     211         512 :   TIME_SECTION("MultiAppVariableValueSamplePostprocessorTransfer::execute()",
     212             :                5,
     213             :                "Transferring a variable to a postprocessor through sampling");
     214             : 
     215         512 :   switch (_current_direction)
     216             :   {
     217         292 :     case TO_MULTIAPP:
     218             :     {
     219         292 :       const ArrayMooseVariable * array_var = nullptr;
     220         292 :       const MooseVariableField<Real> * standard_var = nullptr;
     221         292 :       if (_var.isArray())
     222          44 :         array_var = &_fe_problem.getArrayVariable(0, _var_name);
     223         248 :       else if (!_var.isVector())
     224         248 :         standard_var = static_cast<MooseVariableField<Real> *>(&_var);
     225             :       else
     226           0 :         mooseError("MultiAppVariableValueSamplePostprocessorTransfer does not support transfer of "
     227             :                    "vector variables");
     228             : 
     229         292 :       auto active_tags = _fe_problem.getActiveFEVariableCoupleableVectorTags(/*thread_id=*/0);
     230         292 :       std::set<unsigned int> solution_tag = {_fe_problem.getVectorTagID(Moose::SOLUTION_TAG)};
     231             : 
     232         292 :       _fe_problem.setActiveFEVariableCoupleableVectorTags(solution_tag, /*thread_id=*/0);
     233             : 
     234         292 :       MooseMesh & from_mesh = _fe_problem.mesh();
     235             : 
     236         292 :       std::unique_ptr<PointLocatorBase> pl = from_mesh.getPointLocator();
     237             : 
     238         292 :       pl->enable_out_of_mesh_mode();
     239             : 
     240        3074 :       for (unsigned int i = 0; i < getToMultiApp()->numGlobalApps(); i++)
     241             :       {
     242        2782 :         Real value = -std::numeric_limits<Real>::max();
     243             : 
     244             :         { // Get the value of the variable at the point where this multiapp is in the master domain
     245             : 
     246        2782 :           Point multi_app_position = getToMultiApp()->position(i);
     247             : 
     248        2782 :           std::vector<Point> point_vec(1, multi_app_position);
     249             : 
     250             :           // First find the element the hit lands in
     251        2782 :           const Elem * elem = (*pl)(multi_app_position);
     252             : 
     253        2782 :           if (elem && elem->processor_id() == from_mesh.processor_id())
     254             :           {
     255        1966 :             _fe_problem.setCurrentSubdomainID(elem, 0);
     256        1966 :             _fe_problem.reinitElemPhys(elem, point_vec, 0);
     257             : 
     258        1966 :             if (array_var)
     259             :             {
     260         352 :               value = array_var->sln()[0](getVariableComponent(i));
     261             :               mooseAssert(
     262             :                   getVariableComponent(i) < array_var->count(),
     263             :                   "Component must be smaller than the number of components of array variable!");
     264             :               mooseAssert(array_var->sln().size() == 1, "No values in u!");
     265             :             }
     266             :             else
     267             :             {
     268        1614 :               value = standard_var->sln()[0];
     269             :               mooseAssert(standard_var->sln().size() == 1, "No values in u!");
     270             :             }
     271             :           }
     272             : 
     273        2782 :           _communicator.max(value);
     274        2782 :         }
     275             : 
     276        2782 :         if (getToMultiApp()->hasLocalApp(i))
     277        1966 :           getToMultiApp()->appProblemBase(i).setPostprocessorValueByName(_postprocessor_name,
     278             :                                                                          value);
     279             :       }
     280             : 
     281         292 :       _fe_problem.setActiveFEVariableCoupleableVectorTags(active_tags, /*thread_id=*/0);
     282             : 
     283         292 :       break;
     284         292 :     }
     285         220 :     case FROM_MULTIAPP:
     286             :     {
     287         220 :       auto & mesh = _fe_problem.mesh().getMesh();
     288         220 :       auto & solution = _var.sys().solution();
     289             : 
     290             :       // Get the required postprocessor values
     291         220 :       const unsigned int n_subapps = getFromMultiApp()->numGlobalApps();
     292         220 :       std::vector<Real> pp_values(n_subapps, std::numeric_limits<Real>::max());
     293        2772 :       for (const auto i : make_range(n_subapps))
     294        2552 :         if (getFromMultiApp()->hasLocalApp(i))
     295        1856 :           pp_values[i] = getFromMultiApp()->appPostprocessorValue(i, _postprocessor_name);
     296             : 
     297             :       // Gather all the multiapps postprocessor values that we need
     298         220 :       std::unordered_map<processor_id_type, std::vector<unsigned int>> postprocessor_queries;
     299        2156 :       for (const auto needed_postprocessor : _needed_postprocessors)
     300             :       {
     301        1936 :         const auto proc_id = _postprocessor_to_processor_id[needed_postprocessor];
     302        1936 :         if (proc_id != this->processor_id())
     303         454 :           postprocessor_queries[proc_id].push_back(needed_postprocessor);
     304             :       }
     305             : 
     306          96 :       auto gather_data = [&pp_values
     307             : #ifndef NDEBUG
     308             :                           ,
     309             :                           this
     310             : #endif
     311             :       ](processor_id_type libmesh_dbg_var(pid),
     312             :                          const std::vector<unsigned int> & postprocessor_ids,
     313         454 :                          std::vector<Real> & postprocessor_values)
     314             :       {
     315             :         mooseAssert(pid != this->processor_id(), "Should not be pulling from self");
     316          96 :         postprocessor_values.resize(postprocessor_ids.size());
     317         550 :         for (const auto i : index_range(postprocessor_ids))
     318             :         {
     319         454 :           const auto pp_id = postprocessor_ids[i];
     320         454 :           const auto pp_value = pp_values[pp_id];
     321             :           mooseAssert(
     322             :               pp_value != std::numeric_limits<Real>::max(),
     323             :               "If we are getting queried for postprocessor data, then we better have a valid"
     324             :               "postprocesor value.");
     325         454 :           postprocessor_values[i] = pp_value;
     326             :         }
     327          96 :       };
     328             : 
     329          96 :       auto act_on_data = [&pp_values
     330             : #ifndef NDEBUG
     331             :                           ,
     332             :                           this
     333             : #endif
     334             :       ](processor_id_type libmesh_dbg_var(pid),
     335             :                          const std::vector<unsigned int> & postprocessor_ids,
     336         454 :                          const std::vector<Real> & postprocessor_values)
     337             :       {
     338             :         mooseAssert(pid != this->processor_id(), "Should not be returning a query from self");
     339             :         mooseAssert(postprocessor_ids.size() == postprocessor_values.size(),
     340             :                     "should be a 1-to-1 query-to-response");
     341         550 :         for (const auto i : index_range(postprocessor_ids))
     342             :         {
     343         454 :           const auto pp_id = postprocessor_ids[i];
     344         454 :           const auto pp_value = postprocessor_values[i];
     345             :           mooseAssert(pp_value != std::numeric_limits<Real>::max(),
     346             :                       "If we are returning postprocessor data, then we better have a valid"
     347             :                       "postprocesor value.");
     348         454 :           pp_values[pp_id] = pp_value;
     349             :         }
     350          96 :       };
     351             : 
     352         220 :       constexpr Real example = 0;
     353         220 :       TIMPI::pull_parallel_vector_data(
     354         220 :           _communicator, postprocessor_queries, gather_data, act_on_data, &example);
     355             : 
     356             :       // Assign the multiapps postprocessor values to the local elements.
     357         220 :       unsigned int i = 0;
     358         220 :       for (auto & elem :
     359        6904 :            as_range(mesh.active_local_elements_begin(), mesh.active_local_elements_end()))
     360             :       {
     361             :         // Exclude the elements without dofs
     362        3232 :         if (_var.hasBlocks(elem->subdomain_id()))
     363             :         {
     364        2848 :           std::vector<dof_id_type> dof_indices;
     365        2848 :           _var.getDofIndices(elem, dof_indices);
     366             :           mooseAssert(dof_indices.size() == 1,
     367             :                       "The variable must be a constant monomial with one DoF on an element");
     368             :           mooseAssert(pp_values[_cached_multiapp_pos_ids[i]] != std::numeric_limits<Real>::max(),
     369             :                       "We should have pulled all the data we needed.");
     370        6624 :           for (unsigned int c = 0; c < n_subapps / _apps_per_component; ++c)
     371             :           {
     372        3776 :             solution.set(dof_indices[0] + getVariableComponent(_cached_multiapp_pos_ids[i]),
     373        3776 :                          pp_values[_cached_multiapp_pos_ids[i]]);
     374        3776 :             ++i;
     375             :           }
     376        2848 :         }
     377         220 :       }
     378         220 :       solution.close();
     379         220 :       break;
     380         220 :     }
     381             :   }
     382         512 : }

Generated by: LCOV version 1.14