Teko  Version of the Day
 All Classes Files Functions Variables Pages
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 
18 namespace Teko {
19 
20 using SizeOrdinalType = AdaptivePreconditionerFactory::SizeOrdinalType;
21 
22 namespace {
23 
24 LinearOp 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 
59 class 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 
184 LinearOp 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 
211 void AdaptivePreconditionerFactory::initializeFromParameterList(const Teuchos::ParameterList &pl) {
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
void rebuildInverse(const InverseFactory &factory, const LinearOp &A, InverseLinearOp &invA)
void initializeFromParameterList(const Teuchos::ParameterList &pl) override
This function builds the internals of the preconditioner factory from a parameter list...
LinearOp buildPreconditionerOperator(LinearOp &lo, PreconditionerState &state) const override
Function that is called to build the preconditioner for the linear operator that is passed in...
InverseLinearOp buildInverse(const InverseFactory &factory, const LinearOp &A)
Build an inverse operator using a factory and a linear operator.
Teuchos::RCP< const InverseLibrary > getInverseLibrary() const
Get the inverse library used by this preconditioner factory.
An implementation of a state object preconditioners.