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