https://mooseframework.inl.gov
PODMapping.C
Go to the documentation of this file.
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 "PODMapping.h"
11 #include <slepcsvd.h>
12 #include "MooseTypes.h"
13 #include <petscdmda.h>
14 
15 registerMooseObject("StochasticToolsApp", PODMapping);
16 
19 {
21  params.addClassDescription("Class which provides a Proper Orthogonal Decomposition-based mapping "
22  "between full-order and reduced-order spaces.");
23  params.addParam<UserObjectName>(
24  "solution_storage", "The name of the storage reporter where the snapshots are located.");
25  params.addParam<std::vector<dof_id_type>>(
26  "num_modes_to_compute",
27  "The number of modes that this object should compute. "
28  "Modes with 0 eigenvalues are filtered out, so the real number of modes "
29  "might be lower than this. This is also used for setting the "
30  "subspace sizes for distributed singular value solves. By default, the subspace used for the "
31  "SVD is twice as big as the number of requested vectors. For more information see the SLEPc "
32  "manual. If not specified, only one mode is computed per variable.");
33  params.addParam<std::vector<Real>>(
34  "energy_threshold",
35  std::vector<Real>(),
36  "The energy threshold for the automatic truncation of the number of modes. In general, the "
37  "lower this number is the more information is retained about the system by keeping more POD "
38  "modes.");
39  params.addParam<std::string>("extra_slepc_options",
40  "",
41  "Additional options for the singular/eigenvalue solvers in SLEPc.");
42  return params;
43 }
44 
46  : VariableMappingBase(parameters),
47  UserObjectInterface(this),
48  _num_modes(isParamValid("num_modes_to_compute")
49  ? getParam<std::vector<dof_id_type>>("num_modes_to_compute")
50  : std::vector<dof_id_type>(_variable_names.size(), 1)),
51  _energy_threshold(getParam<std::vector<Real>>("energy_threshold")),
52  _left_basis_functions(declareModelData<std::map<VariableName, std::vector<DenseVector<Real>>>>(
53  "left_basis_functions")),
54  _right_basis_functions(declareModelData<std::map<VariableName, std::vector<DenseVector<Real>>>>(
55  "right_basis_functions")),
56  _singular_values(
57  declareModelData<std::map<VariableName, std::vector<Real>>>("singular_values")),
58  _extra_slepc_options(getParam<std::string>("extra_slepc_options")),
59  _parallel_storage(isParamValid("solution_storage")
60  ? &getUserObject<ParallelSolutionStorage>("solution_storage")
61  : nullptr),
62  _pod(StochasticTools::POD(_parallel_storage, _extra_slepc_options, _communicator))
63 {
64  if (!isParamValid("filename"))
65  {
66  if (_num_modes.size() != _variable_names.size())
67  paramError("num_modes", "The number of modes should be defined for each variable!");
68 
69  for (const auto & mode : _num_modes)
70  if (!mode)
71  paramError("num_modes", "The number of modes should always be a positive integer!");
72 
73  if (_energy_threshold.size())
74  {
75  if (_energy_threshold.size() != _variable_names.size())
76  paramError("energy_threshold",
77  "The energy thresholds should be defined for each variable!");
78 
79  for (const auto & threshold : _energy_threshold)
80  if (threshold < 0 || threshold >= 1)
81  paramError("energy_threshold",
82  "The energy thresholds should always be in the [0,1) range!");
83  }
84 
85 #if PETSC_VERSION_LESS_THAN(3, 14, 0)
86  mooseError("PODMapping is not supported with PETSc version below 3.14!");
87 #else
88  for (const auto & vname : _variable_names)
89  {
90  _singular_values.emplace(vname, std::vector<Real>());
91  _left_basis_functions.emplace(vname, std::vector<DenseVector<Real>>());
92  _right_basis_functions.emplace(vname, std::vector<DenseVector<Real>>());
93  _mapping_ready_to_use.emplace(vname, false);
94  }
95 #endif
96  }
97 }
98 
99 void
100 PODMapping::buildMapping(const VariableName &
101 #if !PETSC_VERSION_LESS_THAN(3, 14, 0)
102  vname
103 #endif
104 )
105 {
106 #if !PETSC_VERSION_LESS_THAN(3, 14, 0)
107  if (!_parallel_storage)
108  paramError(
109  "solution_storage",
110  "The parallel storage reporter is not supplied! We cannot build a mapping without data!");
111 
112  if (_mapping_ready_to_use.find(vname) != _mapping_ready_to_use.end() &&
113  !_mapping_ready_to_use[vname])
114  {
115  auto it = std::find(_variable_names.begin(), _variable_names.end(), vname);
116  mooseAssert(it != _variable_names.end(), "Variable " + vname + " is not in PODMapping!");
117  unsigned int var_i = std::distance(_variable_names.begin(), it);
118  // Clear storage containers for the basis functions and singular values.
119  _left_basis_functions[vname].clear();
120  _right_basis_functions[vname].clear();
121  _singular_values[vname].clear();
122  // Find the number of modes that we would want to compute
123  std::size_t num_modes_compute = (std::size_t)_num_modes[var_i];
124  // Find the first element that satisfies the threshold
125  const Real threshold = (_energy_threshold.empty() ? 0.0 : _energy_threshold[var_i]) +
126  std::numeric_limits<Real>::epsilon();
127  // Use POD class to compute for a variable
128  _pod.computePOD(vname,
129  _left_basis_functions[vname],
130  _right_basis_functions[vname],
131  _singular_values[vname],
132  num_modes_compute,
133  threshold);
134  _mapping_ready_to_use[vname] = true;
135  }
136 
137 #endif
138 }
139 
140 void
141 PODMapping::map(const VariableName & vname,
142  const unsigned int global_sample_i,
143  std::vector<Real> & reduced_order_vector) const
144 {
145  mooseAssert(_parallel_storage, "We need the parallel solution storage for this operation.");
146  mooseAssert(_left_basis_functions.find(vname) != _left_basis_functions.end(),
147  "The bases for the requested variable are not available!");
148  checkIfReadyToUse(vname);
149 
150  // This takes the 0th snapshot because we only support steady-state simulations
151  // at the moment.
152  const auto & snapshot = _parallel_storage->getGlobalSample(global_sample_i, vname)[0];
153 
154  const auto & bases = _left_basis_functions[vname];
155 
156  reduced_order_vector.clear();
157  reduced_order_vector.resize(bases.size());
158  for (auto base_i : index_range(bases))
159  reduced_order_vector[base_i] = bases[base_i].dot(snapshot);
160 }
161 
162 void
163 PODMapping::map(const VariableName & vname,
164  const DenseVector<Real> & full_order_vector,
165  std::vector<Real> & reduced_order_vector) const
166 {
167  checkIfReadyToUse(vname);
168 
169  mooseAssert(_left_basis_functions.find(vname) != _left_basis_functions.end(),
170  "The bases for the requested variable are not available!");
171 
172  const auto & bases = _left_basis_functions[vname];
173 
174  reduced_order_vector.clear();
175  reduced_order_vector.resize(bases.size());
176  for (auto base_i : index_range(bases))
177  reduced_order_vector[base_i] = bases[base_i].dot(full_order_vector);
178 }
179 
180 void
181 PODMapping::inverse_map(const VariableName & vname,
182  const std::vector<Real> & reduced_order_vector,
183  DenseVector<Real> & full_order_vector) const
184 {
185  mooseAssert(std::find(_variable_names.begin(), _variable_names.end(), vname) !=
186  _variable_names.end(),
187  "Variable " + vname + " is not in PODMapping!");
188 
189  checkIfReadyToUse(vname);
190 
191  if (reduced_order_vector.size() != _left_basis_functions[vname].size())
192  mooseError("The number of supplied reduced-order coefficients (",
193  reduced_order_vector.size(),
194  ") is not the same as the number of basis functions (",
195  _left_basis_functions[vname].size(),
196  ") for variable ",
197  vname,
198  "!");
199  // This zeros the DenseVector too
200  full_order_vector.resize(_left_basis_functions[vname][0].size());
201 
202  for (auto base_i : index_range(reduced_order_vector))
203  for (unsigned int dof_i = 0; dof_i < _left_basis_functions[vname][base_i].size(); ++dof_i)
204  full_order_vector(dof_i) +=
205  reduced_order_vector[base_i] * _left_basis_functions[vname][base_i](dof_i);
206 }
207 
208 const DenseVector<Real> &
209 PODMapping::leftBasisFunction(const VariableName & vname, const unsigned int base_i)
210 {
211  mooseAssert(std::find(_variable_names.begin(), _variable_names.end(), vname) !=
212  _variable_names.end(),
213  "Variable " + vname + " is not in PODMapping!");
214 
215  checkIfReadyToUse(vname);
216 
217  mooseAssert(base_i < _left_basis_functions[vname].size(),
218  "The POD for " + vname + " only has " +
219  std::to_string(_left_basis_functions[vname].size()) + " left modes!");
220 
221  return _left_basis_functions[vname][base_i];
222 }
223 
224 const DenseVector<Real> &
225 PODMapping::rightBasisFunction(const VariableName & vname, const unsigned int base_i)
226 {
227  mooseAssert(std::find(_variable_names.begin(), _variable_names.end(), vname) !=
228  _variable_names.end(),
229  "Variable " + vname + " is not in PODMapping!");
230 
231  checkIfReadyToUse(vname);
232 
233  mooseAssert(base_i < _right_basis_functions[vname].size(),
234  "The POD for " + vname + " only has " +
235  std::to_string(_right_basis_functions[vname].size()) + " right modes!");
236 
237  return _right_basis_functions[vname][base_i];
238 }
239 
240 const std::vector<DenseVector<Real>> &
241 PODMapping::leftBasis(const VariableName & vname)
242 {
243  checkIfReadyToUse(vname);
244  if (_left_basis_functions.find(vname) == _left_basis_functions.end())
245  mooseError("We are trying to access container for variable '",
246  vname,
247  "' but we don't have it in the POD mapping!");
248  return _left_basis_functions[vname];
249 }
250 
251 const std::vector<DenseVector<Real>> &
252 PODMapping::rightBasis(const VariableName & vname)
253 {
254  checkIfReadyToUse(vname);
255  if (_right_basis_functions.find(vname) == _right_basis_functions.end())
256  mooseError("We are trying to access container for variable '",
257  vname,
258  "' but we don't have it in the POD mapping!");
259  return _right_basis_functions[vname];
260 }
261 
262 const std::vector<Real> &
263 PODMapping::singularValues(const VariableName & vname)
264 {
265  checkIfReadyToUse(vname);
266  if (_singular_values.find(vname) == _singular_values.end())
267  mooseError("We are trying to access container for variable '",
268  vname,
269  "' but we don't have it in the POD mapping!");
270  return _singular_values[vname];
271 }
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
std::map< VariableName, bool > & _mapping_ready_to_use
Bool to decide if we already have the mapping built or not to make sure it is not computed multiple t...
const std::vector< Real > & singularValues(const VariableName &vname)
Return all of the singular values for a given variable.
Definition: PODMapping.C:263
const std::vector< DenseVector< Real > > & leftBasis(const VariableName &vname)
Return all of the left basis functions for a given variable.
Definition: PODMapping.C:241
static InputParameters validParams()
Definition: PODMapping.C:18
std::map< VariableName, std::vector< DenseVector< Real > > > & _left_basis_functions
Restartable container holding the basis functions for each variable.
Definition: PODMapping.h:107
virtual void buildMapping(const VariableName &vname) override
Abstract function for building mapping for a given variable.
Definition: PODMapping.C:100
void computePOD(const VariableName &vname, std::vector< DenseVector< Real >> &left_basis_functions, std::vector< DenseVector< Real >> &right_basis_functions, std::vector< Real > &singular_values, const dof_id_type num_modes, const Real energy) const
Definition: POD.C:40
bool isParamValid(const std::string &name) const
void checkIfReadyToUse(const VariableName &libmesh_dbg_var(vname)) const
Check if we have a mapping for the variable and if it is ready to be used.
Enum for batch type in stochastic tools MultiApp.
PODMapping(const InputParameters &parameters)
Definition: PODMapping.C:45
std::map< VariableName, std::vector< Real > > & _singular_values
Restartable container holding the singular values.
Definition: PODMapping.h:113
void paramError(const std::string &param, Args... args) const
void inverse_map(const VariableName &vname, const std::vector< Real > &reduced_order_vector, DenseVector< Real > &full_order_vector) const override
Method used for mapping reduced-order solutions for a given variable onto the full-order space...
Definition: PODMapping.C:181
const std::vector< dof_id_type > _num_modes
The number of modes which need to be computed.
Definition: PODMapping.h:101
const ParallelSolutionStorage *const _parallel_storage
Link to the parallel storage which holds the solution fields that are used for the SVD...
Definition: PODMapping.h:120
void map(const VariableName &vname, const DenseVector< Real > &full_order_vector, std::vector< Real > &reduced_order_vector) const override
Method used for mapping full-order solutions for a given variable onto a latent space.
Definition: PODMapping.C:163
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const DenseVector< Real > & rightBasisFunction(const VariableName &vname, const unsigned int base_i)
Get the base_i-th right basis function for a given variable.
Definition: PODMapping.C:225
std::map< VariableName, std::vector< DenseVector< Real > > > & _right_basis_functions
Restartable container holding the basis functions for each variable.
Definition: PODMapping.h:110
Class which provides a Proper Orthogonal Decomposition (POD)-based mapping between full-order and red...
Definition: PODMapping.h:23
registerMooseObject("StochasticToolsApp", PODMapping)
void mooseError(Args &&... args) const
const std::vector< DenseVector< Real > > & getGlobalSample(unsigned int global_sample_i, const VariableName &variable) const
Get the serialized solution field which is associated with a given global sample index and variable...
void addClassDescription(const std::string &doc_string)
const DenseVector< Real > & leftBasisFunction(const VariableName &vname, const unsigned int base_i)
Get the base_i-th left basis function for a given variable.
Definition: PODMapping.C:209
const std::vector< VariableName > & _variable_names
Storage for the names of the variables this mapping can handle.
This is an abstract base class for objects that provide mapping between a full-order and a latent spa...
const std::vector< DenseVector< Real > > & rightBasis(const VariableName &vname)
Return all of the right basis functions for a given variable.
Definition: PODMapping.C:252
static InputParameters validParams()
const StochasticTools::POD _pod
The POD object which can be used to compute the basis functions/vectors.
Definition: PODMapping.h:123
auto index_range(const T &sizable)
A Reporter which stores serialized solution fields for given variables in a distributed fashion...
const std::vector< Real > & _energy_threshold
The energy thresholds for truncation of the number of modes, defined by the user. ...
Definition: PODMapping.h:104
uint8_t dof_id_type