https://mooseframework.inl.gov
Loading...
Searching...
No Matches
POD.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 "MooseError.h"
11#include "POD.h"
12
13namespace StochasticTools
14{
15
16#if PETSC_VERSION_LESS_THAN(3, 14, 0)
17
18POD::POD(const ParallelSolutionStorage * const, const std::string &, const Parallel::Communicator &)
19{
20 mooseError("PETSc-3.14.0 or higher is required for using StochasticTools::POD.");
21}
22
23#else
24
25POD::POD(const ParallelSolutionStorage * const parallel_storage,
26 const std::string & extra_slepc_options,
27 const Parallel::Communicator & comm)
28 : _parallel_storage(parallel_storage),
29 _extra_slepc_options(extra_slepc_options),
30 _communicator(comm)
31{
32}
33
34#endif
35
36void
37POD::computePOD(const VariableName & vname,
38 std::vector<DenseVector<Real>> & left_basis_functions,
39 std::vector<DenseVector<Real>> & right_basis_functions,
40 std::vector<Real> & singular_values,
41 const dof_id_type num_modes,
42 const Real energy) const
43{
44
45#if !PETSC_VERSION_LESS_THAN(3, 14, 0)
46
47 // Define the petsc matrix which needs and SVD, we will populate it using the snapshots
48 Mat mat;
49
50 // We make sure every rank knows how many global and local samples we have and how long the
51 // snapshots are. At this point we assume that the snapshots are the same size so we don't
52 // need to map them to a reference domain.
53 dof_id_type local_rows = 0;
54 dof_id_type snapshot_size = 0;
55 dof_id_type global_rows = 0;
56 if (_parallel_storage->getStorage().size())
57 {
58 for (const auto & row : _parallel_storage->getStorage(vname))
59 {
60 local_rows += row.second.size();
61 if (row.second.size())
62 snapshot_size = row.second[0].size();
63 }
64 global_rows = local_rows;
65 }
66
67 _communicator.sum(global_rows);
68 _communicator.max(snapshot_size);
69
70 // Generally snapshot matrices are dense.
71 LibmeshPetscCallA(
72 _communicator.get(),
73 MatCreateDense(
74 _communicator.get(), local_rows, PETSC_DECIDE, global_rows, snapshot_size, NULL, &mat));
75
76 // Check where the local rows begin in the matrix, we use these to convert from local to
77 // global indices
78 dof_id_type local_beg = 0;
79 dof_id_type local_end = 0;
80 LibmeshPetscCallA(_communicator.get(),
81 MatGetOwnershipRange(mat,
83 libMesh::numeric_petsc_cast(&local_end)));
84
85 unsigned int counter = 0;
86 if (local_rows)
87 for (const auto & row : _parallel_storage->getStorage(vname))
88 {
89 // Adds each snap individually. For problems with multiple snaps per run.
90 for (const auto & snap : row.second)
91 {
92 std::vector<PetscInt> rows(snapshot_size, (counter++) + local_beg);
93
94 // Fill the column indices with 0,1,...,snapshot_size-1
95 std::vector<PetscInt> columns(snapshot_size);
96 std::iota(std::begin(columns), std::end(columns), 0);
97
98 // Set the rows in the "sparse" matrix
99 LibmeshPetscCallA(_communicator.get(),
100 MatSetValues(mat,
101 1,
102 rows.data(),
103 snapshot_size,
104 columns.data(),
105 snap.get_values().data(),
106 INSERT_VALUES));
107 }
108 }
109
110 // Assemble the matrix
111 LibmeshPetscCallA(_communicator.get(), MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
112 LibmeshPetscCallA(_communicator.get(), MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
113
114 SVD svd;
115 LibmeshPetscCallA(_communicator.get(), SVDCreate(_communicator.get(), &svd));
116 // Now we set the operators for our SVD objects
117 LibmeshPetscCallA(_communicator.get(), SVDSetOperators(svd, mat, NULL));
118
119 // Set the parallel operation mode to "DISTRIBUTED", default is "REDUNDANT"
120 DS ds;
121 LibmeshPetscCallA(_communicator.get(), SVDGetDS(svd, &ds));
122 LibmeshPetscCallA(_communicator.get(), DSSetParallel(ds, DS_PARALLEL_DISTRIBUTED));
123
124 // We want the Lanczos method, might give the choice to the user
125 // at some point
126 LibmeshPetscCallA(_communicator.get(), SVDSetType(svd, SVDTRLANCZOS));
127
128 // Default is the transpose is explicitly created. This method is less efficient
129 // computationally but better for storage
130 LibmeshPetscCallA(_communicator.get(), SVDSetImplicitTranspose(svd, PETSC_TRUE));
131
132 LibmeshPetscCallA(_communicator.get(),
133 PetscOptionsInsertString(NULL, _extra_slepc_options.c_str()));
134
135 // Set the subspace size for the Lanczos method, we take twice as many
136 // basis vectors as the requested number of POD modes. This guarantees in most of the case the
137 // convergence of the singular triplets.
138 LibmeshPetscCallA(_communicator.get(),
139 SVDSetDimensions(svd,
140 num_modes,
141 std::min(2 * num_modes, global_rows),
142 std::min(2 * num_modes, global_rows)));
143
144 // Gives the user the ability to override any option set before the solve.
145 LibmeshPetscCallA(_communicator.get(), SVDSetFromOptions(svd));
146
147 // Compute the singular value triplets
148 LibmeshPetscCallA(_communicator.get(), SVDSolve(svd));
149
150 // Check how many singular triplets converged
151 PetscInt nconv;
152 LibmeshPetscCallA(_communicator.get(), SVDGetConverged(svd, &nconv));
153
154 // We start extracting the basis functions and the singular values.
155
156 // Find the local size needed for u
157 dof_id_type local_snapsize = 0;
158 LibmeshPetscCallA(_communicator.get(),
159 MatGetLocalSize(mat, NULL, libMesh::numeric_petsc_cast(&local_snapsize)));
160
161 PetscVector<Real> u(_communicator);
162 u.init(snapshot_size, local_snapsize, false, PARALLEL);
163
164 PetscVector<Real> v(_communicator);
165 v.init(global_rows, local_rows, false, PARALLEL);
166
167 left_basis_functions.clear();
168 right_basis_functions.clear();
169 singular_values.clear();
170
171 singular_values.resize(nconv);
172 // Fetch the singular value triplet and immediately save the singular value
173 for (PetscInt j = 0; j < nconv; ++j)
174 LibmeshPetscCallA(_communicator.get(),
175 SVDGetSingularTriplet(svd, j, &singular_values[j], NULL, NULL));
176
177 // Determine how many modes we need
178 unsigned int num_requested_modes = determineNumberOfModes(singular_values, num_modes, energy);
179 // Only save the basis functions which are needed. We serialize the modes
180 // on every processor so all of them have access to every mode.
181 left_basis_functions.resize(num_requested_modes);
182 right_basis_functions.resize(num_requested_modes);
183 for (PetscInt j = 0; j < cast_int<PetscInt>(num_requested_modes); ++j)
184 {
185 LibmeshPetscCallA(_communicator.get(), SVDGetSingularTriplet(svd, j, NULL, v.vec(), u.vec()));
186 u.localize(left_basis_functions[j].get_values());
187 v.localize(right_basis_functions[j].get_values());
188 }
189 LibmeshPetscCallA(_communicator.get(), MatDestroy(&mat));
190 LibmeshPetscCallA(_communicator.get(), SVDDestroy(&svd));
191#else
192 // These variables would otherwise be unused
193 libmesh_ignore(vname);
194 libmesh_ignore(left_basis_functions);
195 libmesh_ignore(right_basis_functions);
196 libmesh_ignore(singular_values);
197 libmesh_ignore(num_modes);
198 libmesh_ignore(energy);
199#endif
200}
201
202dof_id_type
203POD::determineNumberOfModes(const std::vector<Real> & singular_values,
204 const dof_id_type num_modes_compute,
205 const Real energy) const
206{
207 dof_id_type num_modes = 0;
208 // We either use the number of modes defined by the user or the maximum number of converged
209 // modes. We don't want to use modes which are unconverged.
210 std::size_t num_requested_modes =
211 std::min((std::size_t)num_modes_compute, singular_values.size());
212 // Grab a cumulative sum of singular value squared
213 std::vector<Real> ev_sum(singular_values.begin(), singular_values.begin() + num_requested_modes);
214 std::partial_sum(ev_sum.cbegin(),
215 ev_sum.cend(),
216 ev_sum.begin(),
217 [](Real sum, Real ev) { return sum + ev * ev; });
218
219 // Find the first element that satisfies the threshold
220 const Real threshold = energy;
221 for (num_modes = 0; num_modes < ev_sum.size(); ++num_modes)
222 if (ev_sum[num_modes] / ev_sum.back() > 1 - threshold)
223 break;
224
225 return num_modes + 1;
226}
227}
const double v
void mooseError(Args &&... args)
A Reporter which stores serialized solution fields for given variables in a distributed fashion.
std::unordered_map< unsigned int, std::vector< DenseVector< Real > > > & getStorage(const VariableName &variable)
Get the stored solution vectors for a given variable.
const std::string & _extra_slepc_options
Additional options for the singular value solver.
Definition POD.h:63
const ParallelSolutionStorage *const _parallel_storage
The container where the snapshots are stored.
Definition POD.h:61
const Parallel::Communicator & _communicator
The communicator for parallel routines.
Definition POD.h:65
dof_id_type determineNumberOfModes(const std::vector< Real > &singular_values, const dof_id_type num_modes_compute, const Real energy) const
Determine the number of basis functions needed for a given variable based on the information on the s...
Definition POD.C:203
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:37
POD(const ParallelSolutionStorage *const parallel_storage, const std::string &extra_slepc_options, const Parallel::Communicator &comm)
Definition POD.C:18
Enum for batch type in stochastic tools MultiApp.
PetscInt * numeric_petsc_cast(const numeric_index_type *p)