Teko Version of the Day
Loading...
Searching...
No Matches
Teko_AdaptivePreconditionerFactory.cpp
1// @HEADER
2// *****************************************************************************
3// Teko: A package for block and physics based preconditioning
4//
5// Copyright 2010 NTESS and the Teko contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#include <ios>
11#include <utility>
12#include "Teko_AdaptivePreconditionerFactory.hpp"
13#include "Teko_ImplicitLinearOp.hpp"
14#include "Teko_PreconditionerState.hpp"
15#include "Teko_Utilities.hpp"
16#include "Teuchos_ENull.hpp"
17
18namespace Teko {
19
20using SizeOrdinalType = AdaptivePreconditionerFactory::SizeOrdinalType;
21
22namespace {
23
24LinearOp buildInverse(const InverseFactory &invFact, const Teuchos::RCP<InverseFactory> &precFact,
25 const LinearOp &matrix, PreconditionerState &state,
26 const std::string &opPrefix, size_t i) {
27 std::stringstream ss;
28 ss << opPrefix << "_" << i;
29
30 ModifiableLinearOp &invOp = state.getModifiableOp(ss.str());
31 ModifiableLinearOp &precOp = state.getModifiableOp("prec_" + ss.str());
32
33 if (precFact != Teuchos::null) {
34 if (precOp == Teuchos::null) {
35 precOp = precFact->buildInverse(matrix);
36 state.addModifiableOp("prec_" + ss.str(), precOp);
37 } else {
38 rebuildInverse(*precFact, matrix, precOp);
39 }
40 }
41
42 if (invOp == Teuchos::null) {
43 if (precOp.is_null()) {
44 invOp = buildInverse(invFact, matrix);
45 } else {
46 invOp = buildInverse(invFact, matrix, precOp);
47 }
48 } else {
49 if (precOp.is_null()) {
50 rebuildInverse(invFact, matrix, invOp);
51 } else {
52 rebuildInverse(invFact, matrix, precOp, invOp);
53 }
54 }
55
56 return invOp;
57}
58
59class AdaptiveLinearOp : public ImplicitLinearOp {
60 public:
61 AdaptiveLinearOp(LinearOp A_, const std::vector<Teuchos::RCP<InverseFactory>> &inverses_,
62 const std::vector<Teuchos::RCP<InverseFactory>> &preconditioners_,
63 const std::vector<SizeOrdinalType> &maximumSizes_, PreconditionerState &state_,
64 AdaptiveSolverSettings settings_)
65 : A(std::move(A_)),
66 inverses(inverses_),
67 preconditioners(preconditioners_),
68 maximumSizes(maximumSizes_),
69 state(state_),
70 settings(settings_) {
71 subblockSize = A->range()->dim();
72 }
73
74 ~AdaptiveLinearOp() override = default;
75
76 VectorSpace range() const override { return A->range(); }
77 VectorSpace domain() const override { return A->domain(); }
78
79 void implicitApply(const MultiVector &x, MultiVector &y, const double alpha = 1.0,
80 const double beta = 0.0) const override {
81 bool converged = true;
82 bool additionalIterationsRequired = false;
83 double residualReduction = 0.0;
84 bool try_next_solver = true;
85
86 do {
87 applyOp(A_inv, x, y, alpha, beta);
88
89 residualReduction = computeMaxRelativeNorm(x, y);
90
91 converged = residualReduction <= settings.targetResidualReduction;
92 if (converged) continue;
93
94 if (!has_another_solver()) break;
95
96 try_next_solver = setup_next_solver();
97
98 additionalIterationsRequired = true;
99
100 } while (!converged && try_next_solver);
101
102 successfulApplications++;
103 if (additionalIterationsRequired || !converged) successfulApplications = 0;
104
105 // revert back to initial solver in chain
106 if (successfulApplications >= settings.numAppliesCycle && index != 0) {
107 index = 0;
108 successfulApplications = 0;
109 setup_solver();
110 }
111 }
112
113 void describe(Teuchos::FancyOStream &out_arg,
114 const Teuchos::EVerbosityLevel verbLevel) const override {
115 A->describe(out_arg, verbLevel);
116 }
117
118 void initialize_step() const { setup_solver(); }
119
120 private:
121 bool has_another_solver() const { return (index + 1) < inverses.size(); }
122
123 bool setup_solver() const {
124 if (subblockSize > maximumSizes.at(index)) {
125 return false;
126 }
127
128 try {
129 A_inv = buildInverse(*inverses.at(index), preconditioners.at(index), A, state, prefix, index);
130 return true;
131 } catch (std::exception &exc) {
132 return false;
133 }
134 }
135
136 bool setup_next_solver() const {
137 if (!has_another_solver()) return false;
138
139 const auto currentSolverIndex = index;
140 while (has_another_solver()) {
141 index++;
142 const auto success = setup_solver();
143 if (success) return true;
144 }
145
146 index = currentSolverIndex;
147 return false;
148 }
149
150 double computeMaxRelativeNorm(const MultiVector &x, MultiVector &y) const {
151 const double alpha = 1.0;
152 std::vector<double> norms(y->domain()->dim());
153 std::vector<double> rhsNorms(x->domain()->dim());
154
155 auto residual = deepcopy(x);
156 applyOp(A, y, residual, -1.0, alpha);
157
158 Thyra::norms_2<double>(*residual, Teuchos::arrayViewFromVector(norms));
159 Thyra::norms_2<double>(*x, Teuchos::arrayViewFromVector(rhsNorms));
160
161 double maxRelRes = 0.0;
162 for (auto i = 0U; i < norms.size(); ++i) {
163 const auto relRes = rhsNorms[i] == 0 ? norms[i] : norms[i] / rhsNorms[i];
164 maxRelRes = std::max(relRes, maxRelRes);
165 }
166
167 return maxRelRes;
168 }
169
170 LinearOp A;
171 std::vector<Teuchos::RCP<InverseFactory>> inverses;
172 std::vector<Teuchos::RCP<InverseFactory>> preconditioners;
173 std::vector<SizeOrdinalType> maximumSizes;
174 PreconditionerState &state;
175 AdaptiveSolverSettings settings;
176 SizeOrdinalType subblockSize;
177 mutable size_t index = 0;
178 mutable int successfulApplications = 0;
179 mutable LinearOp A_inv;
180
181 const std::string prefix = "adaptive";
182};
183
184LinearOp create_adaptive_linear_operator(
185 const LinearOp &A, const std::vector<Teuchos::RCP<InverseFactory>> &inverses,
186 const std::vector<Teuchos::RCP<InverseFactory>> &preconditioners,
187 const std::vector<SizeOrdinalType> &maximumSizes, PreconditionerState &state,
188 const AdaptiveSolverSettings &settings) {
189 ModifiableLinearOp &adaptiveOp = state.getModifiableOp("adaptive_linear_op");
190 if (adaptiveOp == Teuchos::null) {
191 adaptiveOp = Teuchos::rcp(
192 new AdaptiveLinearOp(A, inverses, preconditioners, maximumSizes, state, settings));
193 }
194
195 {
196 auto adaptiveSolver = Teuchos::rcp_dynamic_cast<AdaptiveLinearOp>(adaptiveOp);
197 adaptiveSolver->initialize_step();
198 }
199
200 return adaptiveOp;
201}
202
203} // namespace
204
206 LinearOp &lo, PreconditionerState &state) const {
207 return create_adaptive_linear_operator(lo, inverses, preconditioners, maximumSizes, state,
208 settings);
209}
210
212 settings.numAppliesCycle = 100;
213 settings.targetResidualReduction = 0.1;
214
215 if (pl.isParameter("Target Residual Reduction")) {
216 settings.targetResidualReduction = pl.get<double>("Target Residual Reduction");
217 }
218
219 if (pl.isParameter("Number of Successful Applications Before Resetting")) {
220 settings.numAppliesCycle = pl.get<int>("Number of Successful Applications Before Resetting");
221 }
222
223 const std::string inverse_type = "Inverse Type";
224 const std::string preconditioner_type = "Preconditioner Type";
225 const std::string maximum_size_subblock = "Maximum Size";
226 auto invLib = getInverseLibrary();
227
228 std::set<int> positions;
229 for (const auto &entry : pl) {
230 auto fieldName = entry.first;
231
232 // figure out what the integer is
233 bool isInverse = fieldName.find(inverse_type) != std::string::npos;
234 if (!isInverse) continue;
235
236 int position = -1;
237 std::string inverse, type;
238
239 // figure out position
240 std::stringstream ss(fieldName);
241 ss >> inverse >> type >> position;
242
243 if (position <= 0) {
244 Teko_DEBUG_MSG("Adaptive \"Inverse Type\" must be a (strictly) positive integer", 1);
245 }
246
247 positions.insert(position);
248 }
249
250 inverses.resize(positions.size());
251 preconditioners.resize(positions.size());
252 maximumSizes.resize(positions.size());
253 std::fill(maximumSizes.begin(), maximumSizes.end(), std::numeric_limits<SizeOrdinalType>::max());
254
255 // check for individual solvers
256 for (const auto &position : positions) {
257 auto inverseName = inverse_type + std::string(" ") + std::to_string(position);
258 auto preconditionerName = preconditioner_type + std::string(" ") + std::to_string(position);
259 auto maximumSizeName = maximum_size_subblock + std::string(" ") + std::to_string(position);
260
261 // inverse or preconditioner
262 const auto &invStr = pl.get<std::string>(inverseName);
263 inverses[position - 1] = invLib->getInverseFactory(invStr);
264
265 if (pl.isParameter(preconditionerName)) {
266 const auto &precStr = pl.get<std::string>(preconditionerName);
267 preconditioners[position - 1] = invLib->getInverseFactory(precStr);
268 }
269
270 if (pl.isParameter(maximumSizeName)) {
271 const auto maxSizeSubblock = pl.get<SizeOrdinalType>(maximumSizeName);
272 maximumSizes[position - 1] = maxSizeSubblock;
273 }
274 }
275}
276
277} // end namespace Teko
MultiVector deepcopy(const MultiVector &v)
Perform a deep copy of the vector.
LinearOp buildPreconditionerOperator(LinearOp &lo, PreconditionerState &state) const override
Function that is called to build the preconditioner for the linear operator that is passed in.
void initializeFromParameterList(const Teuchos::ParameterList &pl) override
This function builds the internals of the preconditioner factory from a parameter list.
A virtual class that simplifies the construction of custom operators.
Abstract class for building an inverse operator.
Teuchos::RCP< const InverseLibrary > getInverseLibrary() const
Get the inverse library used by this preconditioner factory.
An implementation of a state object preconditioners.