LCOV - code coverage report
Current view: top level - src/transfers - SCMSolutionTransfer.C (source / functions) Hit Total Coverage
Test: idaholab/moose subchannel: #33358 (34476d) with base 4fcf7b Lines: 97 103 94.2 %
Date: 2026-07-17 19:56:42 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "SCMSolutionTransfer.h"
      11             : #include "MultiApp.h"
      12             : #include "FEProblemBase.h"
      13             : #include "DisplacedProblem.h"
      14             : #include "SubChannelMesh.h"
      15             : 
      16             : namespace
      17             : {
      18             : bool
      19          65 : isDeprecatedPinTransferType(const InputParameters & parameters)
      20             : {
      21          65 :   const auto & type = parameters.getObjectType();
      22          65 :   return type == "SCMPinSolutionTransfer" || type == "PinSolutionTransfer";
      23             : }
      24             : }
      25             : 
      26             : registerMooseObject("SubChannelApp", SCMSolutionTransfer);
      27             : registerMooseObjectRenamed("SubChannelApp",
      28             :                            SolutionTransfer,
      29             :                            "06/30/2027 24:00",
      30             :                            SCMSolutionTransfer);
      31             : registerMooseObjectRenamed("SubChannelApp",
      32             :                            SCMPinSolutionTransfer,
      33             :                            "06/30/2027 24:00",
      34             :                            SCMSolutionTransfer);
      35             : registerMooseObjectRenamed("SubChannelApp",
      36             :                            PinSolutionTransfer,
      37             :                            "06/30/2027 24:00",
      38             :                            SCMSolutionTransfer);
      39             : 
      40             : InputParameters
      41         286 : SCMSolutionTransfer::validParams()
      42             : {
      43         286 :   InputParameters params = MultiAppTransfer::validParams();
      44         572 :   params.addRequiredParam<std::vector<AuxVariableName>>("variable",
      45             :                                                         "The auxiliary variables to transfer.");
      46         572 :   MooseEnum transfer_type("subchannel pin", "subchannel");
      47         572 :   params.addParam<MooseEnum>("transfer_type",
      48             :                              transfer_type,
      49             :                              "Whether to transfer subchannel-centered or pin-centered fields.");
      50         286 :   params.addClassDescription(
      51             :       "Transfers subchannel or pin solutions from a SubChannel mesh onto a visualization mesh.");
      52         286 :   return params;
      53         286 : }
      54             : 
      55         143 : SCMSolutionTransfer::SCMSolutionTransfer(const InputParameters & parameters)
      56             :   : MultiAppTransfer(parameters),
      57         143 :     _var_names(getParam<std::vector<AuxVariableName>>("variable")),
      58         143 :     _pin_transfer(
      59         376 :         getParam<MooseEnum>("transfer_type") == "pin" ||
      60         524 :         (!parameters.isParamSetByUser("transfer_type") && isDeprecatedPinTransferType(parameters)))
      61             : {
      62         143 :   if (_directions.contains(Transfer::FROM_MULTIAPP))
      63           0 :     paramError("from_multiapp", "This transfer works only into multi-app.");
      64         143 : }
      65             : 
      66             : void
      67         130 : SCMSolutionTransfer::initialSetup()
      68             : {
      69         130 :   MultiAppTransfer::initialSetup();
      70         735 :   for (std::size_t var_index = 0; var_index < _var_names.size(); ++var_index)
      71             :   {
      72         605 :     if (_to_problems.empty())
      73          20 :       continue;
      74             : 
      75         585 :     MooseVariableFieldBase & from_var = _subproblem.getVariable(
      76             :         0, _var_names[var_index], Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_ANY);
      77             :     const auto & fe_type = from_var.feType();
      78             : 
      79         585 :     if (fe_type.family != LAGRANGE || fe_type.order != FIRST)
      80           0 :       paramError("variable",
      81             :                  "This transfer requires a first order Lagrange variable for the source variable");
      82             : 
      83         585 :     MooseVariableFieldBase & to_var = _to_problems[0]->getVariable(
      84         585 :         0, _var_names[var_index], Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_ANY);
      85             :     const auto & fe_type_target = to_var.feType();
      86             : 
      87         585 :     if (fe_type_target.family != LAGRANGE || fe_type_target.order != FIRST)
      88           0 :       paramError("variable",
      89             :                  "This transfer requires a first order Lagrange variable for the source variable");
      90             :   }
      91         130 : }
      92             : 
      93             : void
      94         254 : SCMSolutionTransfer::execute()
      95             : {
      96         508 :   TIME_SECTION(
      97             :       "MultiAppDetailedSolutionBaseTransfer::execute()", 5, "Transferring subchannel solutions");
      98         254 :   getAppInfo();
      99             : 
     100         254 :   switch (_current_direction)
     101             :   {
     102         254 :     case TO_MULTIAPP:
     103             :     case BETWEEN_MULTIAPP:
     104         254 :       transferToMultiApps();
     105             :       break;
     106             : 
     107             :     default:
     108             :       break;
     109             :   }
     110         252 : }
     111             : 
     112             : void
     113         254 : SCMSolutionTransfer::transferToMultiApps()
     114             : {
     115             :   mooseAssert(_from_meshes.size() == 1, "Only one source mesh can be active in this transfer.");
     116         254 :   auto * from_mesh = dynamic_cast<SubChannelMesh *>(_from_meshes[0]);
     117         254 :   if (from_mesh == nullptr)
     118           0 :     mooseError("This transfer works only with SubChannelMesh classes.");
     119         254 :   if (_pin_transfer && !from_mesh->pinMeshExist())
     120           0 :     mooseError(
     121             :         "This transfer was configured for pin variables, but the source mesh has no pin mesh.");
     122             : 
     123        1012 :   for (unsigned int i = 0; i < getToMultiApp()->numGlobalApps(); i++)
     124         508 :     if (getToMultiApp()->hasLocalApp(i))
     125         242 :       transferVarsToApp(i);
     126         252 : }
     127             : 
     128             : void
     129         242 : SCMSolutionTransfer::transferVarsToApp(unsigned int app_idx)
     130             : {
     131         242 :   transferNodalVars(app_idx);
     132         240 : }
     133             : 
     134             : void
     135         242 : SCMSolutionTransfer::transferNodalVars(unsigned int app_idx)
     136             : {
     137         242 :   Moose::ScopedCommSwapper swapper(getToMultiApp()->comm());
     138             : 
     139         242 :   FEProblemBase & to_problem = getToMultiApp()->appProblemBase(app_idx);
     140             :   MooseMesh * mesh = NULL;
     141         242 :   if (_displaced_target_mesh && to_problem.getDisplacedProblem())
     142           0 :     mesh = &to_problem.getDisplacedProblem()->mesh();
     143             :   else
     144         242 :     mesh = &to_problem.mesh();
     145             : 
     146         242 :   const SubChannelMesh & from_mesh = dynamic_cast<SubChannelMesh &>(*_from_meshes[0]);
     147         242 :   FEProblemBase & from_problem = *_from_problems[0];
     148         242 :   validateVariableLocations(from_mesh, to_problem);
     149             : 
     150     7775136 :   for (auto & node : mesh->getMesh().local_node_ptr_range())
     151             :   {
     152     3887328 :     if (processor_id() != 0)
     153      881254 :       continue;
     154     3006074 :     Node * from_node = getFromNode(from_mesh, *node);
     155             : 
     156    19042297 :     for (auto & var_name : _var_names)
     157             :     {
     158    16036223 :       System * to_sys = find_sys(to_problem.es(), var_name);
     159             :       unsigned int to_sys_num = to_sys->number();
     160    16036223 :       unsigned int to_var_num = to_sys->variable_number(var_name);
     161             : 
     162    16036223 :       if (node->n_dofs(to_sys_num, to_var_num) > 0)
     163             :       {
     164     9526568 :         System * from_sys = find_sys(from_problem.es(), var_name);
     165             :         unsigned int from_sys_num = from_sys->number();
     166     9526568 :         unsigned int from_var_num = from_sys->variable_number(var_name);
     167             : 
     168             :         // Return to parent app MPI communicator to get solution dof
     169             :         swapper.forceSwap();
     170             :         NumericVector<Real> * from_solution = from_sys->solution.get();
     171     9526568 :         dof_id_type from_dof = from_node->dof_number(from_sys_num, from_var_num, 0);
     172     9526568 :         Real from_value = (*from_solution)(from_dof);
     173             :         swapper.forceSwap();
     174             : 
     175    19053136 :         NumericVector<Real> & to_solution = getToMultiApp()->appTransferVector(app_idx, var_name);
     176     9526568 :         dof_id_type to_dof = node->dof_number(to_sys_num, to_var_num, 0);
     177     9526568 :         to_solution.set(to_dof, from_value);
     178             :       }
     179             :     }
     180         240 :   }
     181             : 
     182        1425 :   for (auto & var_name : _var_names)
     183             :   {
     184        2370 :     getToMultiApp()->appTransferVector(app_idx, var_name).close();
     185        1185 :     find_sys(to_problem.es(), var_name)->update();
     186             :   }
     187         240 : }
     188             : 
     189             : void
     190         242 : SCMSolutionTransfer::validateVariableLocations(const SubChannelMesh & from_mesh,
     191             :                                                FEProblemBase & to_problem)
     192             : {
     193         242 :   if (processor_id() != 0)
     194             :     return;
     195             : 
     196         182 :   Node * from_node = _pin_transfer ? from_mesh.getPinNode(0, 0) : from_mesh.getChannelNode(0, 0);
     197             :   mooseAssert(from_node, "The representative source node must be non-null.");
     198             : 
     199         182 :   FEProblemBase & from_problem = *_from_problems[0];
     200         182 :   const auto transfer_type = _pin_transfer ? "pin" : "subchannel";
     201         182 :   const auto other_transfer_type = _pin_transfer ? "subchannel" : "pin";
     202         182 :   const auto expected_block = _pin_transfer ? "fuel_pins" : "subchannel";
     203         182 :   const auto source_block_id = from_mesh.getSubdomainID(expected_block);
     204         364 :   const auto target_block_id = to_problem.mesh().getSubdomainID(expected_block);
     205             : 
     206        1055 :   for (const auto & var_name : _var_names)
     207             :   {
     208         875 :     MooseVariableFieldBase & from_var = from_problem.getVariable(
     209             :         0, var_name, Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_ANY);
     210         875 :     MooseVariableFieldBase & to_var = to_problem.getVariable(
     211             :         0, var_name, Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_ANY);
     212         875 :     System * from_sys = find_sys(from_problem.es(), var_name);
     213             :     const auto from_sys_num = from_sys->number();
     214         875 :     const auto from_var_num = from_sys->variable_number(var_name);
     215             : 
     216        1748 :     if (!from_var.activeOnSubdomain(source_block_id) ||
     217        1748 :         !to_var.activeOnSubdomain(target_block_id) ||
     218         873 :         from_node->n_dofs(from_sys_num, from_var_num) == 0)
     219           2 :       paramError("variable",
     220             :                  "The variable '",
     221             :                  var_name,
     222             :                  "' does not have DOFs on ",
     223             :                  transfer_type,
     224             :                  " nodes. Use a separate SCMSolutionTransfer with transfer_type = '",
     225             :                  other_transfer_type,
     226             :                  "' for variables centered on ",
     227             :                  other_transfer_type,
     228             :                  " nodes.");
     229             :   }
     230             : }
     231             : 
     232             : Node *
     233     3006074 : SCMSolutionTransfer::getFromNode(const SubChannelMesh & from_mesh, const Point & src_node)
     234             : {
     235             :   unsigned int sch_idx =
     236     3006074 :       _pin_transfer ? from_mesh.pinIndex(src_node) : from_mesh.channelIndex(src_node);
     237     3006074 :   unsigned iz = from_mesh.getZIndex(src_node);
     238     3006074 :   return _pin_transfer ? from_mesh.getPinNode(sch_idx, iz) : from_mesh.getChannelNode(sch_idx, iz);
     239             : }

Generated by: LCOV version 1.14