https://mooseframework.inl.gov
MappingReporter.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 // Stocastic Tools Includes
11 #include "MappingReporter.h"
12 #include "NonlinearSystemBase.h"
13 #include "libmesh/parallel_sync.h"
14 #include "VectorPacker.h"
15 
16 #include "Sampler.h"
17 
18 registerMooseObject("StochasticToolsApp", MappingReporter);
19 
22 {
26  params.addClassDescription(
27  "A reporter which can map full solution fields to a latent space for given variables.");
28  params.addRequiredParam<UserObjectName>("mapping", "Name of the mapping object.");
29  params.addRequiredParam<std::vector<VariableName>>(
30  "variables", "The names of the variables which need to be mapped to the latent space.");
31  params.addParam<std::string>("parallel_storage",
32  "The storage space where the snapshots are stored. These snapshots "
33  "are used to build the mapping. If this parameter is not specified, "
34  "the reporter will fetch the variable from the nonlinear system.");
35  params.addParam<SamplerName>(
36  "sampler",
37  "Sampler be able to identify how the samples are distributed among "
38  "the processes. Only needed if parallel storage is defined. It is important to have the "
39  "same sampler here as the one used to prepare the snapshots in the parallel storage.");
40  return params;
41 }
42 
44  : StochasticReporter(parameters),
45  MappingInterface(this),
46  _parallel_storage(isParamValid("parallel_storage")
47  ? &getUserObject<ParallelSolutionStorage>("parallel_storage")
48  : nullptr),
49  _sampler(isParamValid("sampler") ? &getSampler("sampler") : nullptr),
50  _mapping_name(getParam<UserObjectName>("mapping")),
51  _variable_names(getParam<std::vector<VariableName>>("variables"))
52 {
54  {
55  if (_sampler)
56  {
57  // Declaring the reporters for every variable. This is a collection of vectors which describe
58  // the coordinates of the solution fields in the latent space.
60  for (auto var_i : index_range(_variable_names))
61  {
62  _vector_real_values_parallel_storage[var_i] = &declareStochasticReporter<std::vector<Real>>(
63  _variable_names[var_i] + "_" + _mapping_name, *_sampler);
64  }
65  }
66  else
67  paramError("sampler",
68  "We need a sampler object if the parallel storage is supplied! The sampler object "
69  "shall be the same as the one used to generate the solution fields in the "
70  "parallel storage object.");
71  }
72  else
73  {
74  // Declaring the reporters for every variable. This is a collection of vectors which describe
75  // the coordinates of the solution fields in the latent space.
76  _vector_real_values.resize(_variable_names.size());
77  for (auto var_i : index_range(_variable_names))
78  _vector_real_values[var_i] = &declareValueByName<std::vector<Real>>(
80  }
81 }
82 
83 void
85 {
87 }
88 
89 void
91 {
92  // We have two execution modes. If the parallel storage is supplied we loop over the snapshots in
93  // the parallel storage, and project them to obtain their coefficients.
96  // The alternative option is to just fetch the variables and reduce them
97  else
99 }
100 
101 void
103 {
104  // If the mapping is not built yet, we shall build it using the solutions in the parallel
105  // storage. The conditional process is decided in the mapping object.
106  for (const auto & var : _variable_names)
107  _mapping->buildMapping(var);
108 
109  // Since the solution fields can be distributed among the processes of each sub-application
110  // (unlike samples and reporter values which are just on the root process), we need to use the
111  // information in the sampler to check which solution is where.
112  const auto rank_config = _sampler->getRankConfig(true);
113 
114  // We need to do some parallel communication in case we have snapshots on processes other than
115  // the roots of the sub-applications. This will collect the vectors that we need to send in the
116  // following format:
117  // <to which processor, (variable index, global sample index, solution field)>
118  std::unordered_map<processor_id_type,
119  std::vector<std::tuple<unsigned int, unsigned int, std::vector<Real>>>>
120  send_map;
121 
122  // We need to use the rank config here to ensure we are looping on the non-root processors
123  // too
124  for (const auto sample_i : make_range(rank_config.num_local_sims))
125  {
126  std::vector<Real> data = _sampler->getNextLocalRow();
127 
128  // Converting the local indexing to global sample indices
129  const unsigned int global_i = sample_i + _sampler->getLocalRowBegin();
130 
131  for (const auto var_i : index_range(_variable_names))
132  {
133  // Create a temporary storage for the coordinates in the latent space
134  std::vector<Real> local_vector;
135 
136  // Check if the current process has this global sample
137  if (_parallel_storage->hasGlobalSample(global_i, _variable_names[var_i]))
138  {
139  // Fetch the solution vector for the given sample index and variable
140  const auto & full_vector =
142 
143  // At the moment we only support simulations which have only one solution field
144  // per sample. This is typically a steady-state simulation.
145  if (full_vector.size() != 1)
146  mooseError("MappingReporter is only supported for simulations with one solution "
147  "field per run!");
148 
149  // We use the mapping object to generate the coordinates in the latent space
150  _mapping->map(_variable_names[var_i], global_i, local_vector);
151 
152  // If we are on the root processor of the sub-application, we simply insert the result
153  // into the reporter storage space. Othervise we will send it to the root process.
154  if (rank_config.is_first_local_rank)
155  (*_vector_real_values_parallel_storage[var_i])[sample_i] = local_vector;
156  else
157  send_map[rank_config.my_first_rank].emplace_back(
158  var_i, sample_i, std::move(local_vector));
159  }
160  }
161  }
162 
163  // This functor describes what we do when we receive the samples from other processes
164  auto receive_functor =
165  [this](processor_id_type /*pid*/,
166  const std::vector<std::tuple<unsigned int, unsigned int, std::vector<Real>>> & vectors)
167  {
168  // We unpack the tuples and insert the values into the reporter
169  for (const auto & [var_i, sample_i, vector] : vectors)
170  (*_vector_real_values_parallel_storage[var_i])[sample_i] = std::move(vector);
171  };
172 
173  // We send the results from the non-root processors to the root processors
174  Parallel::push_parallel_packed_range(_communicator, send_map, (void *)nullptr, receive_functor);
175 }
176 
177 void
179 {
180  NonlinearSystemBase & nl = _fe_problem.getNonlinearSystemBase(/*nl_sys_num=*/0);
181 
182  for (unsigned int var_i = 0; var_i < _variable_names.size(); ++var_i)
183  {
184  // Getting the corresponding DoF indices for the variable.
186 
187  // We need to serialize the solution on the root process first.
188  DenseVector<Real> serialized_solution;
189  nl.solution().localize(serialized_solution.get_values(),
190  processor_id() == 0 ? nl.getVariableGlobalDoFs()
191  : std::vector<dof_id_type>());
192  // We map the solution into the latent space and save the solutions in one go
193  if (processor_id() == 0)
194  _mapping->map(_variable_names[var_i], serialized_solution, *_vector_real_values[var_i]);
195  }
196 }
T & declareValueByName(const ReporterValueName &value_name, Args &&... args)
Sampler *const _sampler
We only need the sampler to check which coefficients would go to which processor in case a ParallelSo...
virtual void execute() override
static InputParameters validParams()
VariableMappingBase * _mapping
Link to the mapping object, we need this to be a pointer due to the fact that we can only fetch this ...
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
NumericVector< Number > & solution()
const ReporterMode REPORTER_MODE_ROOT
static InputParameters validParams()
A tool to reduce solution fields to coordinates in the latent space.
std::vector< Real > getNextLocalRow()
const std::vector< VariableName > & _variable_names
The variables we would like to map.
dof_id_type getLocalRowBegin() const
const Parallel::Communicator & _communicator
MappingReporter(const InputParameters &parameters)
const UserObjectName & _mapping_name
The name of the mapping object we would like to use.
void addRequiredParam(const std::string &name, const std::string &doc_string)
const ParallelSolutionStorage *const _parallel_storage
If we already have solution fields stored from previous runs, we can use their ParallelStorageObject ...
uint8_t processor_id_type
virtual void buildMapping(const VariableName &vname)=0
Abstract function for building mapping for a given variable.
registerMooseObject("StochasticToolsApp", MappingReporter)
VariableMappingBase & getMappingByName(const UserObjectName &name) const
Get the mapping by supplying the name of the object in the warehouse.
const std::vector< dof_id_type > & getVariableGlobalDoFs()
NonlinearSystemBase & getNonlinearSystemBase(const unsigned int sys_num)
bool hasGlobalSample(unsigned int global_sample_i, const VariableName &variable) const
Determine if we have the solution vector with a given global sample index for a given variable...
void paramError(const std::string &param, Args... args) const
const LocalRankConfig & getRankConfig(bool batch_mode) const
void mapParallelStorageData()
Map the available data in a parallel storage into the latent space.
virtual void map(const VariableName &vname, const DenseVector< Real > &full_order_vector, std::vector< Real > &reduced_order_vector) const =0
Map a full-order solution vector (in DenseVector format) for a given variable into a reduced-order ve...
An interface class that helps getting access to Mapping objects.
FEProblemBase & _fe_problem
static InputParameters validParams()
IntRange< T > make_range(T beg, T end)
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)
std::vector< std::vector< Real > * > _vector_real_values
std::vector< std::vector< std::vector< Real > > * > _vector_real_values_parallel_storage
void mapVariableData()
Map the available data in variables into the latent space.
void setVariableGlobalDoFs(const std::string &var_name)
processor_id_type processor_id() const
static InputParameters validParams()
void initialSetup() override
auto index_range(const T &sizable)
A Reporter which stores serialized solution fields for given variables in a distributed fashion...
virtual void localize(std::vector< Number > &v_local) const =0