LCOV - code coverage report
Current view: top level - src/userobjects - InverseMapping.C (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: f45d79 Lines: 70 87 80.5 %
Date: 2025-07-25 05:00:46 Functions: 4 4 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 "InverseMapping.h"
      11             : #include "SubProblem.h"
      12             : #include "Assembly.h"
      13             : #include "NonlinearSystemBase.h"
      14             : 
      15             : #include "libmesh/sparse_matrix.h"
      16             : 
      17             : registerMooseObject("StochasticToolsApp", InverseMapping);
      18             : 
      19             : InputParameters
      20         120 : InverseMapping::validParams()
      21             : {
      22         120 :   InputParameters params = GeneralUserObject::validParams();
      23             : 
      24         120 :   params.addClassDescription("Evaluates surrogate models and maps the results back to a full "
      25             :                              "solution field for given variables.");
      26         120 :   params.addParam<std::vector<UserObjectName>>(
      27         120 :       "surrogate", std::vector<UserObjectName>(), "The names of the surrogates for each variable.");
      28         240 :   params.addRequiredParam<UserObjectName>(
      29             :       "mapping", "The name of the mapping object which provides the inverse mapping function.");
      30         240 :   params.addRequiredParam<std::vector<VariableName>>(
      31             :       "variable_to_fill",
      32             :       "The names of the variables that this object is supposed to populate with the "
      33             :       "reconstructed results.");
      34         240 :   params.addRequiredParam<std::vector<VariableName>>(
      35             :       "variable_to_reconstruct",
      36             :       "The names of the variables in the nonlinear system which we would like to approximate. This "
      37             :       "is important for DoF information.");
      38         240 :   params.addRequiredParam<std::vector<Real>>(
      39             :       "parameters",
      40             :       "The input parameters for the surrogate. If no surrogate is supplied these are assumed to be "
      41             :       "the coordinates in the latent space.");
      42         240 :   params.declareControllable("parameters");
      43             : 
      44         120 :   return params;
      45           0 : }
      46             : 
      47          60 : InverseMapping::InverseMapping(const InputParameters & parameters)
      48             :   : GeneralUserObject(parameters),
      49             :     MappingInterface(this),
      50             :     SurrogateModelInterface(this),
      51          60 :     _var_names_to_fill(getParam<std::vector<VariableName>>("variable_to_fill")),
      52         120 :     _var_names_to_reconstruct(getParam<std::vector<VariableName>>("variable_to_reconstruct")),
      53         120 :     _surrogate_model_names(getParam<std::vector<UserObjectName>>("surrogate")),
      54         180 :     _input_parameters(getParam<std::vector<Real>>("parameters"))
      55             : {
      56          60 :   if (_var_names_to_fill.size() != _var_names_to_reconstruct.size())
      57           0 :     paramError("variable_to_fill",
      58             :                "The number of variables to fill should be the same as the number of entries in "
      59             :                "`variable_to_reconstruct`");
      60          60 :   if (_surrogate_model_names.size())
      61             :   {
      62          40 :     if (_var_names_to_fill.size() != _surrogate_model_names.size())
      63           0 :       paramError("surrogate",
      64             :                  "The number of surrogates should match the number of variables which need to be "
      65             :                  "reconstructed!");
      66             :   }
      67          60 : }
      68             : 
      69             : void
      70          60 : InverseMapping::initialSetup()
      71             : {
      72          60 :   _mapping = &getMapping("mapping");
      73             : 
      74          60 :   _surrogate_models.clear();
      75         140 :   for (const auto & name : _surrogate_model_names)
      76          80 :     _surrogate_models.push_back(&getSurrogateModelByName(name));
      77             : 
      78          60 :   _variable_to_fill.clear();
      79          60 :   _variable_to_reconstruct.clear();
      80             : 
      81          60 :   const auto & mapping_variable_names = _mapping->getVariableNames();
      82             : 
      83             :   // We query the links to the MooseVariables mentioned in the input parameters
      84         180 :   for (const auto & var_i : index_range(_var_names_to_reconstruct))
      85             :   {
      86         120 :     if (std::find(mapping_variable_names.begin(),
      87             :                   mapping_variable_names.end(),
      88         120 :                   _var_names_to_reconstruct[var_i]) == mapping_variable_names.end())
      89           0 :       paramError("variable_to_reconstruct",
      90           0 :                  "Couldn't find mapping for " + _var_names_to_reconstruct[var_i] +
      91             :                      "! Double check the training process and make sure that the mapping includes "
      92             :                      "the given variable!");
      93             : 
      94         120 :     _variable_to_fill.push_back(&_fe_problem.getVariable(_tid, _var_names_to_fill[var_i]));
      95             :     _variable_to_reconstruct.push_back(
      96         120 :         &_fe_problem.getVariable(_tid, _var_names_to_reconstruct[var_i]));
      97             : 
      98         120 :     auto & fe_type_reconstruct = _variable_to_reconstruct.back()->feType();
      99         120 :     auto & fe_type_fill = _variable_to_fill.back()->feType();
     100             : 
     101             :     if (fe_type_reconstruct != fe_type_fill)
     102           0 :       paramError("variable_to_fill",
     103             :                  "The FEtype should match the ones defined for `variable_to_reconstruct`");
     104             : 
     105         120 :     if (fe_type_reconstruct.family == SCALAR)
     106           0 :       paramError("variable_to_fill", "InverseMapping does not support SCALAR variables!");
     107             :   }
     108          60 : }
     109             : 
     110             : void
     111          60 : InverseMapping::execute()
     112             : {
     113         180 :   for (auto var_i : index_range(_var_names_to_reconstruct))
     114             :   {
     115         120 :     MooseVariableFieldBase & var_to_fill = *_variable_to_fill[var_i];
     116         120 :     MooseVariableFieldBase & var_to_reconstruct = *_variable_to_reconstruct[var_i];
     117             : 
     118             :     // We create a temporary solution vector that will store the reconstructed solution.
     119             :     std::unique_ptr<NumericVector<Number>> temporary_vector =
     120         120 :         var_to_reconstruct.sys().solution().zero_clone();
     121             : 
     122             :     std::vector<Real> reduced_coefficients;
     123         120 :     if (_surrogate_models.size())
     124          80 :       _surrogate_models[var_i]->evaluate(_input_parameters, reduced_coefficients);
     125             :     else
     126          40 :       reduced_coefficients = _input_parameters;
     127             : 
     128             :     // The sanity check on the size of reduced_coefficients is happening in the inverse mapping
     129             :     // function
     130         120 :     DenseVector<Real> reconstructed_solution;
     131         120 :     _mapping->inverse_map(
     132         120 :         _var_names_to_reconstruct[var_i], reduced_coefficients, reconstructed_solution);
     133             : 
     134             :     // We set the global DoF indices of the requested variable. The underlying assumption here is
     135             :     // that we are reconstructing the solution in an application which has the same ordering in the
     136             :     // extracted `dofs` vector as the one which was used to serialize the solution vectors in
     137             :     // `SerializedSolutionTransfer`.
     138         120 :     var_to_reconstruct.sys().setVariableGlobalDoFs(_var_names_to_reconstruct[var_i]);
     139             : 
     140             :     // Get the DoF indices
     141             :     const auto & dofs = var_to_reconstruct.sys().getVariableGlobalDoFs();
     142             : 
     143             :     // Get the dof map to be able to determine the local dofs easily
     144         120 :     const auto & dof_map = var_to_reconstruct.sys().system().get_dof_map();
     145             :     dof_id_type local_dof_begin = dof_map.first_dof();
     146             :     dof_id_type local_dof_end = dof_map.end_dof();
     147             : 
     148             :     // Populate the temporary vector with the reconstructed solution
     149        1440 :     for (const auto & dof_i : index_range(dofs))
     150        1320 :       if (dofs[dof_i] >= local_dof_begin && dofs[dof_i] < local_dof_end)
     151         660 :         temporary_vector->set(dofs[dof_i], reconstructed_solution(dof_i));
     152             : 
     153             :     // Get the system and variable numbers for the dof objects
     154         120 :     unsigned int to_sys_num = _variable_to_fill[var_i]->sys().system().number();
     155         120 :     unsigned int to_var_num = var_to_fill.sys().system().variable_number(_var_names_to_fill[var_i]);
     156             : 
     157         120 :     unsigned int from_sys_num = var_to_reconstruct.sys().system().number();
     158             :     unsigned int from_var_num =
     159         120 :         var_to_reconstruct.sys().system().variable_number(_var_names_to_reconstruct[var_i]);
     160             : 
     161             :     // Get a link to the mesh for the loops over dof objects
     162         120 :     const MeshBase & to_mesh = _fe_problem.mesh().getMesh();
     163             : 
     164             :     // First, we cover nodal degrees of freedom.
     165        1560 :     for (const auto & node : to_mesh.local_node_ptr_range())
     166             :     {
     167         660 :       const auto n_dofs = node->n_dofs(to_sys_num, to_var_num);
     168             :       // We have nothing to do if we don't have dofs at this node
     169         660 :       if (n_dofs < 1)
     170           0 :         continue;
     171             : 
     172             :       // For special cases we might have multiple dofs for the same node (hierarchic types)
     173        1320 :       for (auto dof_i : make_range(n_dofs))
     174             :       {
     175             :         // Get the dof ids for the from/to variables
     176         660 :         const auto & to_dof_id = node->dof_number(to_sys_num, to_var_num, dof_i);
     177         660 :         const auto & from_dof_id = node->dof_number(from_sys_num, from_var_num, dof_i);
     178             : 
     179             :         // Fill the dof of the variable using the dof of the temporary variable
     180         660 :         var_to_fill.sys().solution().set(to_dof_id, (*temporary_vector)(from_dof_id));
     181             :       }
     182         120 :     }
     183             : 
     184             :     // Then we move on to element-based degrees of freedom. Considering that we don't
     185             :     // support scalar variables at the moment, this should take care of every remaining
     186             :     // local entry. Of course, we'll only need this part if the variable is not nodal.
     187         120 :     if (!_variable_to_reconstruct[var_i]->isNodal())
     188           0 :       for (auto & elem : as_range(to_mesh.local_elements_begin(), to_mesh.local_elements_end()))
     189             :       {
     190             :         // Check how many dofs we have on the current element, if none we have nothing to do
     191           0 :         const auto n_dofs = elem->n_dofs(to_sys_num, to_var_num);
     192           0 :         if (n_dofs < 1)
     193           0 :           continue;
     194             : 
     195             :         // Loop over the dofs and populate the variable
     196           0 :         for (auto dof_i : make_range(n_dofs))
     197             :         {
     198             :           // Get the dof for the from/to variables
     199           0 :           const auto & to_dof_id = elem->dof_number(to_sys_num, to_var_num, dof_i);
     200           0 :           const auto & from_dof_id = elem->dof_number(from_sys_num, from_var_num, dof_i);
     201             : 
     202           0 :           var_to_fill.sys().solution().set(to_dof_id, (*temporary_vector)(from_dof_id));
     203             :         }
     204           0 :       }
     205             : 
     206             :     // Close the solution to make sure we can output the variable
     207         120 :     var_to_fill.sys().solution().close();
     208         120 :   }
     209          60 : }

Generated by: LCOV version 1.14