https://mooseframework.inl.gov
Loading...
Searching...
No Matches
Public Member Functions | Private Member Functions | Private Attributes | List of all members
StochasticTools::POD Class Reference

Class which computes a Proper Orthogonal Decomposition (POD) for snapshots stored in ParallelSolutionStorage. More...

#include <POD.h>

Public Member Functions

 POD (const ParallelSolutionStorage *const parallel_storage, const std::string &extra_slepc_options, const Parallel::Communicator &comm)
 
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
 

Private Member Functions

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 singular values.
 

Private Attributes

const ParallelSolutionStorage *const _parallel_storage
 The container where the snapshots are stored.
 
const std::string & _extra_slepc_options
 Additional options for the singular value solver.
 
const Parallel::Communicator & _communicator
 The communicator for parallel routines.
 

Detailed Description

Class which computes a Proper Orthogonal Decomposition (POD) for snapshots stored in ParallelSolutionStorage.

Definition at line 25 of file POD.h.

Constructor & Destructor Documentation

◆ POD()

StochasticTools::POD::POD ( const ParallelSolutionStorage *const  parallel_storage,
const std::string &  extra_slepc_options,
const Parallel::Communicator &  comm 
)

Definition at line 18 of file POD.C.

19{
20 mooseError("PETSc-3.14.0 or higher is required for using StochasticTools::POD.");
21}
void mooseError(Args &&... args)

Member Function Documentation

◆ computePOD()

void StochasticTools::POD::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
Parameters
vnameVariable name to extract snapshot data
left_basis_functionsVector for left basis functions
right_basis_functionsVector for right basis functions
singular_valuesVector for singular values
num_modesMax number of modes to compute
energyEnergy threshold to determine the minimum number of modes

Definition at line 37 of file POD.C.

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}
const double v
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 libmesh_ignore(const Args &...)
PetscInt * numeric_petsc_cast(const numeric_index_type *p)
uint8_t dof_id_type

Referenced by PODMapping::buildMapping().

◆ determineNumberOfModes()

dof_id_type StochasticTools::POD::determineNumberOfModes ( const std::vector< Real > &  singular_values,
const dof_id_type  num_modes_compute,
const Real  energy 
) const
private

Determine the number of basis functions needed for a given variable based on the information on the singular values.

Parameters
singular_valuesVector of singular values
num_modes_computeMax number of modes to compute
energyEnergy threshold to determine the minimum number of modes
Returns
dof_id_type Number of modes to extract

Definition at line 203 of file POD.C.

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}
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

Referenced by computePOD().

Member Data Documentation

◆ _communicator

const Parallel::Communicator& StochasticTools::POD::_communicator
private

The communicator for parallel routines.

Definition at line 65 of file POD.h.

Referenced by computePOD().

◆ _extra_slepc_options

const std::string& StochasticTools::POD::_extra_slepc_options
private

Additional options for the singular value solver.

Definition at line 63 of file POD.h.

Referenced by computePOD().

◆ _parallel_storage

const ParallelSolutionStorage* const StochasticTools::POD::_parallel_storage
private

The container where the snapshots are stored.

Definition at line 61 of file POD.h.

Referenced by computePOD().


The documentation for this class was generated from the following files: