DPsim
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 
11 using namespace DPsim;
12 
13 namespace 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 
36 KLUAdapter::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  mSymbolic =
66  klu_analyze_partial(n, Ap, Ai, &mVaryingColumns[0], &mVaryingRows[0],
67  varying_entries, mPreordering, &mCommon);
68 
69  /* store non-zero value of current preprocessed matrix. only used until
70  * to-do in refactorize-function is resolved. Can be removed then. */
71  nnz = Eigen::internal::convert_index<Int>(systemMatrix.nonZeros());
72 }
73 
74 void KLUAdapter::factorize(SparseMatrix &systemMatrix) {
75  if (mNumeric) {
76  klu_free_numeric(&mNumeric, &mCommon);
77  }
78 
79  auto Ap = Eigen::internal::convert_index<Int *>(systemMatrix.outerIndexPtr());
80  auto Ai = Eigen::internal::convert_index<Int *>(systemMatrix.innerIndexPtr());
81  auto Ax = Eigen::internal::convert_index<Real *>(systemMatrix.valuePtr());
82 
83  mNumeric = klu_factor(Ap, Ai, Ax, mSymbolic, &mCommon);
84 
85  Int varying_entries =
86  Eigen::internal::convert_index<Int>(mChangedEntries.size());
87 
88  /* make sure that factorization path is not computed if there are no varying entries.
89  * Doing so should not be a problem, but it is safer to do it this way */
90  if (!(mVaryingColumns.empty()) && !(mVaryingRows.empty())) {
91  if (mPartialRefactorizationMethod ==
92  DPsim::PARTIAL_REFACTORIZATION_METHOD::FACTORIZATION_PATH) {
93  klu_compute_path(mSymbolic, mNumeric, &mCommon, Ap, Ai,
94  &mVaryingColumns[0], &mVaryingRows[0], varying_entries);
95  } else if (mPartialRefactorizationMethod ==
96  DPsim::PARTIAL_REFACTORIZATION_METHOD::REFACTORIZATION_RESTART) {
97  klu_determine_start(mSymbolic, mNumeric, &mCommon, Ap, Ai,
98  &mVaryingColumns[0], &mVaryingRows[0],
99  varying_entries);
100  }
101  }
102 }
103 
104 void KLUAdapter::refactorize(SparseMatrix &systemMatrix) {
105  /* TODO: remove if-else when zero<->non-zero issue during matrix stamping has been fixed. Also remove in partialRefactorize then. */
106  if (systemMatrix.nonZeros() != nnz) {
107  preprocessing(systemMatrix, mChangedEntries);
108  factorize(systemMatrix);
109  } else {
110  auto Ap =
111  Eigen::internal::convert_index<Int *>(systemMatrix.outerIndexPtr());
112  auto Ai =
113  Eigen::internal::convert_index<Int *>(systemMatrix.innerIndexPtr());
114  auto Ax = Eigen::internal::convert_index<Real *>(systemMatrix.valuePtr());
115  klu_refactor(Ap, Ai, Ax, mSymbolic, mNumeric, &mCommon);
116  }
117 }
118 
120  SparseMatrix &systemMatrix,
121  std::vector<std::pair<UInt, UInt>> &listVariableSystemMatrixEntries) {
122  if (systemMatrix.nonZeros() != nnz) {
123  preprocessing(systemMatrix, listVariableSystemMatrixEntries);
124  factorize(systemMatrix);
125  } else {
126  auto Ap =
127  Eigen::internal::convert_index<Int *>(systemMatrix.outerIndexPtr());
128  auto Ai =
129  Eigen::internal::convert_index<Int *>(systemMatrix.innerIndexPtr());
130  auto Ax = Eigen::internal::convert_index<Real *>(systemMatrix.valuePtr());
131 
132  if (mPartialRefactorizationMethod ==
133  PARTIAL_REFACTORIZATION_METHOD::FACTORIZATION_PATH) {
134  klu_partial_factorization_path(Ap, Ai, Ax, mSymbolic, mNumeric, &mCommon);
135  } else if (mPartialRefactorizationMethod ==
136  PARTIAL_REFACTORIZATION_METHOD::REFACTORIZATION_RESTART) {
137  klu_partial_refactorization_restart(Ap, Ai, Ax, mSymbolic, mNumeric,
138  &mCommon);
139  } else {
140  klu_refactor(Ap, Ai, Ax, mSymbolic, mNumeric, &mCommon);
141  }
142 
143  if (mCommon.status == KLU_PIVOT_FAULT) {
144  /* pivot became too small => fully factorize again */
145  mPivotFaults++;
146  factorize(systemMatrix);
147  }
148  }
149 }
150 
151 Matrix KLUAdapter::solve(Matrix &rightSideVector) {
152  Matrix x = rightSideVector;
153 
154  /* number of right hands sides
155  * usually one, KLU can handle multiple right hand sides */
156  Int rhsCols = Eigen::internal::convert_index<Int>(rightSideVector.cols());
157 
158  /* leading dimension, also called "n" */
159  Int rhsRows = Eigen::internal::convert_index<Int>(rightSideVector.rows());
160 
161  /* tsolve refers to transpose solve. Input matrix is stored in compressed row format,
162  * KLU operates on compressed column format. This way, the transpose of the matrix is factored.
163  * This has to be taken into account only here during right-hand solving. */
164  klu_tsolve(mSymbolic, mNumeric, rhsRows, rhsCols,
165  x.const_cast_derived().data(), &mCommon);
166 
167  return x;
168 }
169 
170 void KLUAdapter::printMatrixMarket(SparseMatrix &matrix, int counter) const {
171  std::string outputName = "A" + std::to_string(counter) + ".mtx";
172  Int n = Eigen::internal::convert_index<Int>(matrix.rows());
173 
174  auto Ap = Eigen::internal::convert_index<const Int *>(matrix.outerIndexPtr());
175  auto Ai = Eigen::internal::convert_index<const Int *>(matrix.innerIndexPtr());
176  auto Ax = Eigen::internal::convert_index<const Real *>(matrix.valuePtr());
177  Int nz = Eigen::internal::convert_index<Int>(matrix.nonZeros());
178 
179  std::ofstream ofs;
180  ofs.open(outputName);
181  /* TODO: add logger to DirectLinearSolver to use libfmt's more powerful logging tools.
182  * Then also move matrix printing (of LU matrices) here in order to avoid C-level printing. */
183  ofs.precision(14);
184  ofs << "%%MatrixMarket matrix coordinate real general" << std::endl;
185  ofs << n << " " << n << " " << nz << std::endl;
186  for (int i = 0; i < n; i++) {
187  for (int j = Ap[i]; j < Ap[i + 1]; j++) {
188  ofs << i + 1 << " " << Ai[j] + 1 << " " << Ax[j] << std::endl;
189  }
190  }
191  ofs.close();
192 }
193 
195  switch (mConfiguration.getScalingMethod()) {
196  case SCALING_METHOD::NO_SCALING:
197  mCommon.scale = 0;
198  break;
199  case SCALING_METHOD::SUM_SCALING:
200  mCommon.scale = 1;
201  break;
202  case SCALING_METHOD::MAX_SCALING:
203  mCommon.scale = 2;
204  break;
205  default:
206  mCommon.scale = 1;
207  }
208 
209  SPDLOG_LOGGER_INFO(mSLog, "Matrix is scaled using " +
210  mConfiguration.getScalingMethodString());
211 
212  // TODO: implement support for COLAMD (modifiy SuiteSparse)
213  switch (mConfiguration.getFillInReductionMethod()) {
214  case FILL_IN_REDUCTION_METHOD::AMD:
215  mPreordering = AMD_ORDERING;
216  break;
217  case FILL_IN_REDUCTION_METHOD::AMD_NV:
218  mPreordering = AMD_ORDERING_NV;
219  break;
220  case FILL_IN_REDUCTION_METHOD::AMD_RA:
221  mPreordering = AMD_ORDERING_RA;
222  break;
223  default:
224  mPreordering = AMD_ORDERING;
225  }
226 
227  SPDLOG_LOGGER_INFO(mSLog,
228  "Matrix is fill reduced with " +
229  mConfiguration.getFillInReductionMethodString());
230 
231  // NOTE: in case more partial refactorization methods are defined/developed, that are not implemented in KLU, this assigment would be invalid
232  mPartialRefactorizationMethod =
233  mConfiguration.getPartialRefactorizationMethod();
234 
235  SPDLOG_LOGGER_INFO(
236  mSLog, "Matrix is refactored " +
237  mConfiguration.getPartialRefactorizationMethodString());
238 
239  switch (mConfiguration.getBTF()) {
240  case USE_BTF::DO_BTF:
241  mCommon.btf = 1;
242  break;
243  case USE_BTF::NO_BTF:
244  mCommon.btf = 0;
245  break;
246  default:
247  mCommon.btf = 1;
248  }
249 
250  SPDLOG_LOGGER_INFO(mSLog,
251  "Matrix is permuted " + mConfiguration.getBTFString());
252 }
253 } // namespace DPsim
DirectLinearSolverConfiguration mConfiguration
Object that carries configuration options.
CPS::Logger::Log mSLog
Stores logger of solver class.
void applyConfiguration() override
Apply configuration.
Definition: KLUAdapter.cpp:194
void preprocessing(SparseMatrix &systemMatrix, std::vector< std::pair< UInt, UInt >> &listVariableSystemMatrixEntries) override
preprocessing function pre-ordering and scaling the matrix
Definition: KLUAdapter.cpp:40
void factorize(SparseMatrix &systemMatrix) override
factorization function with partial pivoting
Definition: KLUAdapter.cpp:74
Matrix solve(Matrix &rightSideVector) override
solution function for a right hand side
Definition: KLUAdapter.cpp:151
void partialRefactorize(SparseMatrix &systemMatrix, std::vector< std::pair< UInt, UInt >> &listVariableSystemMatrixEntries) override
partial refactorization withouth partial pivoting
Definition: KLUAdapter.cpp:119
void refactorize(SparseMatrix &systemMatrix) override
refactorization without partial pivoting
Definition: KLUAdapter.cpp:104
void printMatrixMarket(SparseMatrix &systemMatrix, int counter) const
Function to print matrix in MatrixMarket's coo format.
Definition: KLUAdapter.cpp:170
~KLUAdapter() override
Destructor.
Definition: KLUAdapter.cpp:14
KLUAdapter()
Constructor.
Definition: KLUAdapter.cpp:22