DPsim
Loading...
Searching...
No Matches
KLUAdapter.cpp
1/* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2 * EONERC, RWTH Aachen University
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 *********************************************************************************/
8
9#include <dpsim/KLUAdapter.h>
10
11using namespace DPsim;
12
13namespace DPsim {
15 if (mSymbolic)
16 klu_free_symbolic(&mSymbolic, &mCommon);
17 if (mNumeric)
18 klu_free_numeric(&mNumeric, &mCommon);
19 SPDLOG_LOGGER_INFO(mSLog, "Number of Pivot Faults: {}", mPivotFaults);
20}
21
23 klu_defaults(&mCommon);
24
25 // NOTE: klu_defaults should already set the preordering methods correctly.
26 // It is repeated here in case this is altered in SuiteSparse at some point
27
28 mCommon.scale = 2;
29 mPreordering = AMD_ORDERING;
30 mCommon.btf = 1;
31
32 mVaryingColumns.clear();
33 mVaryingRows.clear();
34}
35
36KLUAdapter::KLUAdapter(CPS::Logger::Log log) : KLUAdapter() {
37 this->mSLog = log;
38}
39
41 SparseMatrix &systemMatrix,
42 std::vector<std::pair<UInt, UInt>> &listVariableSystemMatrixEntries) {
43 if (mSymbolic) {
44 klu_free_symbolic(&mSymbolic, &mCommon);
45 }
46
47 const Int n = Eigen::internal::convert_index<Int>(systemMatrix.rows());
48
49 auto Ap = Eigen::internal::convert_index<Int *>(systemMatrix.outerIndexPtr());
50 auto Ai = Eigen::internal::convert_index<Int *>(systemMatrix.innerIndexPtr());
51
52 mVaryingColumns.clear();
53 mVaryingRows.clear();
54
55 mChangedEntries = listVariableSystemMatrixEntries;
56 Int varying_entries =
57 Eigen::internal::convert_index<Int>(mChangedEntries.size());
58
59 for (auto &changedEntry : mChangedEntries) {
60 mVaryingRows.push_back(changedEntry.first);
61 mVaryingColumns.push_back(changedEntry.second);
62 }
63
64 // This call also works if mVaryingColumns, mVaryingRows are empty
65 int *colPtr = mVaryingColumns.empty() ? nullptr : &mVaryingColumns[0];
66 int *rowPtr = mVaryingRows.empty() ? nullptr : &mVaryingRows[0];
67
68 mSymbolic = klu_analyze_partial(n, Ap, Ai, colPtr, rowPtr, varying_entries,
69 mPreordering, &mCommon);
70
71 if (mVaryingColumns.empty()) {
72 SPDLOG_LOGGER_INFO(
73 mSLog,
74 "KLUAdapter: No variable COLUMN entries passed to klu_analyze_partial");
75 }
76 if (mVaryingRows.empty()) {
77 SPDLOG_LOGGER_INFO(
78 mSLog,
79 "KLUAdapter: No variable ROW entries passed to klu_analyze_partial");
80 }
81
82 /* Store non-zero value of current preprocessed matrix. only used until
83 * to-do in refactorize-function is resolved. Can be removed then.
84 */
85 nnz = Eigen::internal::convert_index<Int>(systemMatrix.nonZeros());
86}
87
88void KLUAdapter::factorize(SparseMatrix &systemMatrix) {
89 if (mNumeric) {
90 klu_free_numeric(&mNumeric, &mCommon);
91 }
92
93 auto Ap = Eigen::internal::convert_index<Int *>(systemMatrix.outerIndexPtr());
94 auto Ai = Eigen::internal::convert_index<Int *>(systemMatrix.innerIndexPtr());
95 auto Ax = Eigen::internal::convert_index<Real *>(systemMatrix.valuePtr());
96
97 mNumeric = klu_factor(Ap, Ai, Ax, mSymbolic, &mCommon);
98
99 Int varying_entries =
100 Eigen::internal::convert_index<Int>(mChangedEntries.size());
101
102 /* Make sure that factorization path is not computed if there are no varying entries.
103 * Doing so should not be a problem, but it is safer to do it this way.
104 */
105 if (!(mVaryingColumns.empty()) && !(mVaryingRows.empty())) {
106 if (mPartialRefactorizationMethod ==
107 DPsim::PARTIAL_REFACTORIZATION_METHOD::FACTORIZATION_PATH) {
108 klu_compute_path(mSymbolic, mNumeric, &mCommon, Ap, Ai,
109 &mVaryingColumns[0], &mVaryingRows[0], varying_entries);
110 } else if (mPartialRefactorizationMethod ==
111 DPsim::PARTIAL_REFACTORIZATION_METHOD::REFACTORIZATION_RESTART) {
112 klu_determine_start(mSymbolic, mNumeric, &mCommon, Ap, Ai,
113 &mVaryingColumns[0], &mVaryingRows[0],
114 varying_entries);
115 }
116 }
117}
118
119void KLUAdapter::refactorize(SparseMatrix &systemMatrix) {
120 // TODO: Remove if-else when zero<->non-zero issue during matrix stamping has been fixed. Also remove in partialRefactorize then.
121 if (systemMatrix.nonZeros() != nnz) {
122 preprocessing(systemMatrix, mChangedEntries);
123 factorize(systemMatrix);
124 } else {
125 auto Ap =
126 Eigen::internal::convert_index<Int *>(systemMatrix.outerIndexPtr());
127 auto Ai =
128 Eigen::internal::convert_index<Int *>(systemMatrix.innerIndexPtr());
129 auto Ax = Eigen::internal::convert_index<Real *>(systemMatrix.valuePtr());
130 klu_refactor(Ap, Ai, Ax, mSymbolic, mNumeric, &mCommon);
131 }
132}
133
135 SparseMatrix &systemMatrix,
136 std::vector<std::pair<UInt, UInt>> &listVariableSystemMatrixEntries) {
137 if (systemMatrix.nonZeros() != nnz) {
138 preprocessing(systemMatrix, listVariableSystemMatrixEntries);
139 factorize(systemMatrix);
140 } else {
141 auto Ap =
142 Eigen::internal::convert_index<Int *>(systemMatrix.outerIndexPtr());
143 auto Ai =
144 Eigen::internal::convert_index<Int *>(systemMatrix.innerIndexPtr());
145 auto Ax = Eigen::internal::convert_index<Real *>(systemMatrix.valuePtr());
146
147 if (mPartialRefactorizationMethod ==
148 PARTIAL_REFACTORIZATION_METHOD::FACTORIZATION_PATH) {
149 klu_partial_factorization_path(Ap, Ai, Ax, mSymbolic, mNumeric, &mCommon);
150 } else if (mPartialRefactorizationMethod ==
151 PARTIAL_REFACTORIZATION_METHOD::REFACTORIZATION_RESTART) {
152 klu_partial_refactorization_restart(Ap, Ai, Ax, mSymbolic, mNumeric,
153 &mCommon);
154 } else {
155 klu_refactor(Ap, Ai, Ax, mSymbolic, mNumeric, &mCommon);
156 }
157
158 if (mCommon.status == KLU_PIVOT_FAULT) {
159 /* pivot became too small => fully factorize again */
160 mPivotFaults++;
161 factorize(systemMatrix);
162 }
163 }
164}
165
166Matrix KLUAdapter::solve(Matrix &rightSideVector) {
167 // TODO: this calls malloc, which is not allowed in the simulation loop
168 // We should preallocate this buffer.
169 Matrix x = rightSideVector;
170
171 /* Number of right hands sides
172 * usually one, KLU can handle multiple right hand sides.
173 */
174 Int rhsCols = Eigen::internal::convert_index<Int>(rightSideVector.cols());
175
176 // Leading dimension, also called "n".
177 Int rhsRows = Eigen::internal::convert_index<Int>(rightSideVector.rows());
178
179 /* tsolve refers to transpose solve. Input matrix is stored in compressed row format,
180 * KLU operates on compressed column format. This way, the transpose of the matrix is factored.
181 * This has to be taken into account only here during right-hand solving.
182 */
183 klu_tsolve(mSymbolic, mNumeric, rhsRows, rhsCols,
184 x.const_cast_derived().data(), &mCommon);
185
186 return x;
187}
188
189void KLUAdapter::printMatrixMarket(SparseMatrix &matrix, int counter) const {
190 std::string outputName = "A" + std::to_string(counter) + ".mtx";
191 Int n = Eigen::internal::convert_index<Int>(matrix.rows());
192
193 auto Ap = Eigen::internal::convert_index<const Int *>(matrix.outerIndexPtr());
194 auto Ai = Eigen::internal::convert_index<const Int *>(matrix.innerIndexPtr());
195 auto Ax = Eigen::internal::convert_index<const Real *>(matrix.valuePtr());
196 Int nz = Eigen::internal::convert_index<Int>(matrix.nonZeros());
197
198 std::ofstream ofs;
199 ofs.open(outputName);
200 /* TODO: Add logger to DirectLinearSolver to use libfmt's more powerful logging tools.
201 * Then also move matrix printing (of LU matrices) here in order to avoid C-level printing.
202 */
203 ofs.precision(14);
204 ofs << "%%MatrixMarket matrix coordinate real general" << std::endl;
205 ofs << n << " " << n << " " << nz << std::endl;
206 for (int i = 0; i < n; i++) {
207 for (int j = Ap[i]; j < Ap[i + 1]; j++) {
208 ofs << i + 1 << " " << Ai[j] + 1 << " " << Ax[j] << std::endl;
209 }
210 }
211 ofs.close();
212}
213
215 switch (mConfiguration.getScalingMethod()) {
216 case SCALING_METHOD::NO_SCALING:
217 mCommon.scale = 0;
218 break;
219 case SCALING_METHOD::SUM_SCALING:
220 mCommon.scale = 1;
221 break;
222 case SCALING_METHOD::MAX_SCALING:
223 mCommon.scale = 2;
224 break;
225 default:
226 mCommon.scale = 1;
227 }
228
229 SPDLOG_LOGGER_INFO(mSLog, "Matrix is scaled using " +
230 mConfiguration.getScalingMethodString());
231
232 // TODO: Implement support for COLAMD (modifiy SuiteSparse)
233 switch (mConfiguration.getFillInReductionMethod()) {
234 case FILL_IN_REDUCTION_METHOD::AMD:
235 mPreordering = AMD_ORDERING;
236 break;
237 case FILL_IN_REDUCTION_METHOD::AMD_NV:
238 mPreordering = AMD_ORDERING_NV;
239 break;
240 case FILL_IN_REDUCTION_METHOD::AMD_RA:
241 mPreordering = AMD_ORDERING_RA;
242 break;
243 default:
244 mPreordering = AMD_ORDERING;
245 }
246
247 SPDLOG_LOGGER_INFO(mSLog,
248 "Matrix is fill reduced with " +
249 mConfiguration.getFillInReductionMethodString());
250
251 // NOTE: in case more partial refactorization methods are defined/developed, that are not implemented in KLU, this assigment would be invalid
252 mPartialRefactorizationMethod =
253 mConfiguration.getPartialRefactorizationMethod();
254
255 SPDLOG_LOGGER_INFO(
256 mSLog, "Matrix is refactored " +
257 mConfiguration.getPartialRefactorizationMethodString());
258
259 switch (mConfiguration.getBTF()) {
260 case USE_BTF::DO_BTF:
261 mCommon.btf = 1;
262 break;
263 case USE_BTF::NO_BTF:
264 mCommon.btf = 0;
265 break;
266 default:
267 mCommon.btf = 1;
268 }
269
270 SPDLOG_LOGGER_INFO(mSLog,
271 "Matrix is permuted " + mConfiguration.getBTFString());
272}
273} // namespace DPsim
DirectLinearSolverConfiguration mConfiguration
Object that carries configuration options.
CPS::Logger::Log mSLog
Stores logger of solver class.
void applyConfiguration() override
Apply configuration.
void factorize(SparseMatrix &systemMatrix) override
factorization function with partial pivoting
Matrix solve(Matrix &rightSideVector) override
solution function for a right hand side
void refactorize(SparseMatrix &systemMatrix) override
refactorization without partial pivoting
void printMatrixMarket(SparseMatrix &systemMatrix, int counter) const
Function to print matrix in MatrixMarket's coo format.
void partialRefactorize(SparseMatrix &systemMatrix, std::vector< std::pair< UInt, UInt > > &listVariableSystemMatrixEntries) override
partial refactorization withouth partial pivoting
~KLUAdapter() override
Destructor.
KLUAdapter()
Constructor.
void preprocessing(SparseMatrix &systemMatrix, std::vector< std::pair< UInt, UInt > > &listVariableSystemMatrixEntries) override
preprocessing function pre-ordering and scaling the matrix