Teko  Version of the Day
 All Classes Files Functions Variables Pages
Teko_HierarchicalGaussSeidelPreconditionerFactory.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 "Teko_BlockImplicitLinearOp.hpp"
11 #include "Teko_HierarchicalGaussSeidelPreconditionerFactory.hpp"
12 #include "Teko_Utilities.hpp"
13 
14 using Teuchos::rcp;
15 using Teuchos::RCP;
16 
17 namespace Teko {
18 
19 namespace {
20 
21 BlockedMultiVector extractSubBlockVector(const BlockedMultiVector& blo,
22  const std::vector<int>& rows) {
23  const int nrows = rows.size();
24  TEUCHOS_ASSERT(blockCount(blo) >= nrows);
25  std::vector<MultiVector> subVectors;
26  for (int row_id = 0; row_id < nrows; ++row_id) {
27  const auto row = rows[row_id];
28  subVectors.push_back(getBlock(row, blo));
29  }
30 
31  return buildBlockedMultiVector(subVectors);
32 }
33 
34 BlockedLinearOp extractSubblockMatrix(const BlockedLinearOp& blo, const std::vector<int>& rows,
35  const std::vector<int>& cols) {
36  int nOuterBlockRows = blockRowCount(blo);
37  for (auto&& row : rows) {
38  TEUCHOS_ASSERT(row < nOuterBlockRows);
39  }
40  int nOuterBlockCols = blockColCount(blo);
41  for (auto&& col : cols) {
42  TEUCHOS_ASSERT(col < nOuterBlockCols);
43  }
44 
45  // allocate new operator
46  auto subblock = createBlockedOp();
47 
48  const int nInnerBlockRows = rows.size();
49  const int nInnerBlockCols = cols.size();
50 
51  // build new operator
52  subblock->beginBlockFill(nInnerBlockRows, nInnerBlockCols);
53  for (int innerBlockRow = 0U; innerBlockRow < nInnerBlockRows; ++innerBlockRow) {
54  for (int innerBlockCol = 0U; innerBlockCol < nInnerBlockCols; ++innerBlockCol) {
55  auto [outerBlockRow, outerBlockCol] =
56  std::make_tuple(rows[innerBlockRow], cols[innerBlockCol]);
57  auto A_row_col = blo->getBlock(outerBlockRow, outerBlockCol);
58 
59  if (A_row_col != Teuchos::null) {
60  subblock->setBlock(innerBlockRow, innerBlockCol, A_row_col);
61  } else {
62  // scan to find first non-null range/domain, construct zero operator
63  VectorSpace range;
64  VectorSpace domain;
65 
66  for (int outerBlock = 0; outerBlock < nOuterBlockCols; ++outerBlock) {
67  auto A_ij = blo->getBlock(outerBlockRow, outerBlock);
68  if (A_ij != Teuchos::null) {
69  range = A_ij->range();
70  break;
71  }
72  }
73 
74  for (int outerBlock = 0; outerBlock < nOuterBlockRows; ++outerBlock) {
75  auto A_ij = blo->getBlock(outerBlock, outerBlockCol);
76  if (A_ij != Teuchos::null) {
77  domain = A_ij->domain();
78  break;
79  }
80  }
81 
82  TEUCHOS_ASSERT(range != Teuchos::null);
83  TEUCHOS_ASSERT(domain != Teuchos::null);
84 
85  subblock->setBlock(innerBlockRow, innerBlockCol, zero(range, domain));
86  }
87  }
88  }
89 
90  subblock->endBlockFill();
91 
92  return subblock;
93 }
94 
95 std::vector<std::string> tokenize_input(std::string input, const std::string& delimiter) {
96  size_t pos = 0;
97  std::vector<std::string> tokens;
98  while ((pos = input.find(delimiter)) != std::string::npos) {
99  tokens.push_back(input.substr(0, pos));
100  input.erase(0, pos + delimiter.length());
101  }
102  tokens.push_back(input);
103  return tokens;
104 }
105 
106 // Parse hierarchical block number from:
107 // <ParameterList name="Hierarchical Block 1">
108 // </ParameterList>
109 std::optional<int> extract_hierarchical_block_number(const Teuchos::ParameterList& params,
110  const std::string& key) {
111  const std::string blockHierarchy = "Hierarchical Block ";
112  if (key.find(blockHierarchy) == std::string::npos) return {};
113  if (!params.isSublist(key)) return {};
114 
115  int blockNumber = -1;
116  try {
117  auto tokens = tokenize_input(key, " ");
118  blockNumber = std::stoi(tokens.back());
119  } catch (std::exception& err) {
120  std::ostringstream ss;
121  ss << "Error occured when trying to parse entry with key " << key << "\n";
122  ss << "It said:\n" << err.what() << "\n";
123  TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, ss.str());
124  }
125  return blockNumber - 1; // adjust to 0-based indexing
126 }
127 
128 std::vector<int> extract_block_numbers(const Teuchos::ParameterList& params) {
129  const std::string includedBlocks = "Included Subblocks";
130 
131  std::ostringstream ss;
132  ss << "Parameter 'Included Subblocks' is missing for params:\n" << params;
133  TEUCHOS_TEST_FOR_EXCEPTION(!params.isParameter(includedBlocks), std::runtime_error, ss.str());
134  std::vector<int> blocks;
135  try {
136  auto blockStrs = tokenize_input(params.get<std::string>(includedBlocks), ",");
137  for (const auto& blockStr : blockStrs) {
138  blocks.emplace_back(std::stoi(blockStr) - 1); // adjust to 0-based indexing
139  }
140  } catch (std::exception& err) {
141  std::ostringstream errSS;
142  errSS << "Error occured when trying to parse 'Included SubBlocks' for params:\n"
143  << params << "\n";
144  errSS << "It said:\n" << err.what() << "\n";
145  TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, errSS.str());
146  }
147  return blocks;
148 }
149 
150 unsigned index(unsigned i, unsigned j, unsigned N) { return i * N + j; }
151 
152 } // namespace
153 
154 NestedBlockGS::NestedBlockGS(const std::map<int, std::vector<int>>& blockToRow_,
155  const std::map<int, LinearOp>& blockToInvOp_, BlockedLinearOp& A_,
156  bool useLowerTriangle_)
157  : blockToRow(blockToRow_),
158  blockToInvOp(blockToInvOp_),
159  A(A_),
160  useLowerTriangle(useLowerTriangle_) {
161  productRange_ = A->productRange();
162  productDomain_ = A->productDomain();
163  const auto Nrows = blockToRow.size();
164  Ab.resize(Nrows * Nrows);
165  for (auto [hierchicalBlockRow, rows] : blockToRow) {
166  for (auto [hierchicalBlockCol, cols] : blockToRow) {
167  Ab[index(hierchicalBlockRow, hierchicalBlockCol, Nrows)] =
168  extractSubblockMatrix(A, rows, cols);
169  }
170  }
171 }
172 
173 void NestedBlockGS::implicitApply(const BlockedMultiVector& r, BlockedMultiVector& z,
174  const double alpha, const double beta) const {
175  const int blocks = blockToRow.size();
176 
177  auto src = deepcopy(r);
178  std::vector<BlockedMultiVector> rVec;
179  std::vector<BlockedMultiVector> zVec;
180 
181  for (int b = 0; b < blocks; b++) {
182  rVec.push_back(extractSubBlockVector(src, blockToRow.at(b)));
183  zVec.push_back(extractSubBlockVector(z, blockToRow.at(b)));
184  }
185 
186  if (useLowerTriangle) {
187  lowerTriangularImplicitApply(rVec, zVec, alpha, beta);
188  } else {
189  upperTriangularImplicitApply(rVec, zVec, alpha, beta);
190  }
191 }
192 
193 void NestedBlockGS::upperTriangularImplicitApply(std::vector<BlockedMultiVector>& r,
194  std::vector<BlockedMultiVector>& z,
195  const double /* alpha */,
196  const double /* beta */) const {
197  const int blocks = blockToRow.size();
198 
199  for (int b = blocks - 1; b >= 0; b--) {
200  int blockCount = Teko::blockCount(r[b]);
201  if (blockCount == 1) {
202  auto r_b = Teko::getBlock(0, r[b]);
203  auto z_b = Teko::getBlock(0, z[b]);
204  applyOp(blockToInvOp.at(b), r_b, z_b);
205  } else {
206  applyOp(blockToInvOp.at(b), r[b], z[b]);
207  }
208 
209  for (int i = 0; i < b; i++) {
210  auto u_ib = Ab[index(i, b, blocks)];
211  if (u_ib != Teuchos::null) {
212  applyOp(u_ib, z[b], r[i], -1.0, 1.0);
213  }
214  }
215  }
216 }
217 
218 void NestedBlockGS::lowerTriangularImplicitApply(std::vector<BlockedMultiVector>& r,
219  std::vector<BlockedMultiVector>& z,
220  const double /* alpha */,
221  const double /* beta */) const {
222  const int blocks = blockToRow.size();
223 
224  for (int b = 0; b < blocks; b++) {
225  int blockCount = Teko::blockCount(r[b]);
226  if (blockCount == 1) {
227  auto r_b = Teko::getBlock(0, r[b]);
228  auto z_b = Teko::getBlock(0, z[b]);
229  applyOp(blockToInvOp.at(b), r_b, z_b);
230  } else {
231  applyOp(blockToInvOp.at(b), r[b], z[b]);
232  }
233 
234  // loop over each row
235  for (int i = b + 1; i < blocks; i++) {
236  auto l_ib = Ab[index(i, b, blocks)];
237  if (l_ib != Teuchos::null) {
238  applyOp(l_ib, z[b], r[i], -1.0, 1.0);
239  }
240  }
241  }
242 }
243 
244 HierarchicalGaussSeidelPreconditionerFactory::HierarchicalGaussSeidelPreconditionerFactory() =
245  default;
246 
247 LinearOp HierarchicalGaussSeidelPreconditionerFactory::buildPreconditionerOperator(
248  BlockedLinearOp& blo, BlockPreconditionerState& state) const {
249  for (auto [hierarchicalBlockNum, blocks] : blockToRow) {
250  auto A_bb = extractSubblockMatrix(blo, blocks, blocks);
251  blockToInvOp[hierarchicalBlockNum] = this->buildBlockInverse(
252  *blockToInverse.at(hierarchicalBlockNum), blockToPreconditioner.at(hierarchicalBlockNum),
253  A_bb, state, hierarchicalBlockNum);
254  }
255 
256  return Teuchos::rcp(new NestedBlockGS(blockToRow, blockToInvOp, blo, useLowerTriangle));
257 }
258 
259 LinearOp HierarchicalGaussSeidelPreconditionerFactory::buildBlockInverse(
260  const InverseFactory& invFact, const Teuchos::RCP<InverseFactory>& precFact,
261  const BlockedLinearOp& matrix, BlockPreconditionerState& state,
262  int hierarchicalBlockNum) const {
263  // special case: single 1x1 block system -- use non-block based inverses
264  const int nBlock = Teko::blockRowCount(matrix);
265  if (nBlock == 1) {
266  const auto& subblockMatrix = Teko::getBlock(0, 0, matrix);
267  return buildInverseImpl<LinearOp>(invFact, precFact, subblockMatrix, state,
268  hierarchicalBlockNum);
269  }
270 
271  return buildInverseImpl<BlockedLinearOp>(invFact, precFact, matrix, state, hierarchicalBlockNum);
272 }
273 
274 void HierarchicalGaussSeidelPreconditionerFactory::initializeFromParameterList(
275  const Teuchos::ParameterList& pl) {
276  RCP<const InverseLibrary> invLib = getInverseLibrary();
277 
278  for (const auto& entry : pl) {
279  const auto key = entry.first;
280  const auto value = entry.second;
281  auto hierarchicalBlockNumber = extract_hierarchical_block_number(pl, key);
282  if (!hierarchicalBlockNumber) continue;
283  const auto& hierarchicalParams = pl.sublist(key);
284  blockToRow[*hierarchicalBlockNumber] = extract_block_numbers(hierarchicalParams);
285 
286  std::ostringstream ss;
287  ss << "Missing required parameter \"Inverse Type\" for hierarchical block "
288  << *hierarchicalBlockNumber << "\n";
289  TEUCHOS_TEST_FOR_EXCEPTION(!hierarchicalParams.isParameter("Inverse Type"), std::runtime_error,
290  ss.str());
291  auto invStr = hierarchicalParams.get<std::string>("Inverse Type");
292  blockToInverse[*hierarchicalBlockNumber] = invLib->getInverseFactory(invStr);
293 
294  blockToPreconditioner[*hierarchicalBlockNumber] = Teuchos::null;
295  if (hierarchicalParams.isParameter("Preconditioner Type")) {
296  auto precStr = hierarchicalParams.get<std::string>("Preconditioner Type");
297  blockToPreconditioner[*hierarchicalBlockNumber] = invLib->getInverseFactory(precStr);
298  }
299  }
300 
301  useLowerTriangle = false;
302  if (pl.isParameter("Use Upper Triangle")) {
303  useLowerTriangle = !pl.get<bool>("Use Upper Triangle");
304  }
305 }
306 
307 } // namespace Teko