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 : }