Teko Version of the Day
Loading...
Searching...
No Matches
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
14using Teuchos::rcp;
15using Teuchos::RCP;
16
17namespace Teko {
18
19namespace {
20
21BlockedMultiVector 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
34BlockedLinearOp 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
95std::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>
109std::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
128std::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
150unsigned index(unsigned i, unsigned j, unsigned N) { return i * N + j; }
151
152} // namespace
153
154NestedBlockGS::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
173void 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
193void 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
218void 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
244HierarchicalGaussSeidelPreconditionerFactory::HierarchicalGaussSeidelPreconditionerFactory() =
245 default;
246
247LinearOp 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
259LinearOp 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
274void 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
int blockCount(const BlockedMultiVector &bmv)
Get the column count in a block linear operator.
MultiVector getBlock(int i, const BlockedMultiVector &bmv)
Get the ith block from a BlockedMultiVector object.
MultiVector deepcopy(const MultiVector &v)
Perform a deep copy of the vector.