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  // TODO: this calls malloc, which is not allowed in the simulation loop
153  // We should preallocate this buffer.
154  Matrix x = rightSideVector;
155 
156  /* number of right hands sides
157  * usually one, KLU can handle multiple right hand sides */
158  Int rhsCols = Eigen::internal::convert_index<Int>(rightSideVector.cols());
159 
160  /* leading dimension, also called "n" */
161  Int rhsRows = Eigen::internal::convert_index<Int>(rightSideVector.rows());
162 
163  /* tsolve refers to transpose solve. Input matrix is stored in compressed row format,
164  * KLU operates on compressed column format. This way, the transpose of the matrix is factored.
165  * This has to be taken into account only here during right-hand solving. */
166  klu_tsolve(mSymbolic, mNumeric, rhsRows, rhsCols,
167  x.const_cast_derived().data(), &mCommon);
168 
169  return x;
170 }
171 
172 void KLUAdapter::printMatrixMarket(SparseMatrix &matrix, int counter) const {
173  std::string outputName = "A" + std::to_string(counter) + ".mtx";
174  Int n = Eigen::internal::convert_index<Int>(matrix.rows());
175 
176  auto Ap = Eigen::internal::convert_index<const Int *>(matrix.outerIndexPtr());
177  auto Ai = Eigen::internal::convert_index<const Int *>(matrix.innerIndexPtr());
178  auto Ax = Eigen::internal::convert_index<const Real *>(matrix.valuePtr());
179  Int nz = Eigen::internal::convert_index<Int>(matrix.nonZeros());
180 
181  std::ofstream ofs;
182  ofs.open(outputName);
183  /* TODO: add logger to DirectLinearSolver to use libfmt's more powerful logging tools.
184  * Then also move matrix printing (of LU matrices) here in order to avoid C-level printing. */
185  ofs.precision(14);
186  ofs << "%%MatrixMarket matrix coordinate real general" << std::endl;
187  ofs << n << " " << n << " " << nz << std::endl;
188  for (int i = 0; i < n; i++) {
189  for (int j = Ap[i]; j < Ap[i + 1]; j++) {
190  ofs << i + 1 << " " << Ai[j] + 1 << " " << Ax[j] << std::endl;
191  }
192  }
193  ofs.close();
194 }
195 
197  switch (mConfiguration.getScalingMethod()) {
198  case SCALING_METHOD::NO_SCALING:
199  mCommon.scale = 0;
200  break;
201  case SCALING_METHOD::SUM_SCALING:
202  mCommon.scale = 1;
203  break;
204  case SCALING_METHOD::MAX_SCALING:
205  mCommon.scale = 2;
206  break;
207  default:
208  mCommon.scale = 1;
209  }
210 
211  SPDLOG_LOGGER_INFO(mSLog, "Matrix is scaled using " +
212  mConfiguration.getScalingMethodString());
213 
214  // TODO: implement support for COLAMD (modifiy SuiteSparse)
215  switch (mConfiguration.getFillInReductionMethod()) {
216  case FILL_IN_REDUCTION_METHOD::AMD:
217  mPreordering = AMD_ORDERING;
218  break;
219  case FILL_IN_REDUCTION_METHOD::AMD_NV:
220  mPreordering = AMD_ORDERING_NV;
221  break;
222  case FILL_IN_REDUCTION_METHOD::AMD_RA:
223  mPreordering = AMD_ORDERING_RA;
224  break;
225  default:
226  mPreordering = AMD_ORDERING;
227  }
228 
229  SPDLOG_LOGGER_INFO(mSLog,
230  "Matrix is fill reduced with " +
231  mConfiguration.getFillInReductionMethodString());
232 
233  // NOTE: in case more partial refactorization methods are defined/developed, that are not implemented in KLU, this assigment would be invalid
234  mPartialRefactorizationMethod =
235  mConfiguration.getPartialRefactorizationMethod();
236 
237  SPDLOG_LOGGER_INFO(
238  mSLog, "Matrix is refactored " +
239  mConfiguration.getPartialRefactorizationMethodString());
240 
241  switch (mConfiguration.getBTF()) {
242  case USE_BTF::DO_BTF:
243  mCommon.btf = 1;
244  break;
245  case USE_BTF::NO_BTF:
246  mCommon.btf = 0;
247  break;
248  default:
249  mCommon.btf = 1;
250  }
251 
252  SPDLOG_LOGGER_INFO(mSLog,
253  "Matrix is permuted " + mConfiguration.getBTFString());
254 }
255 } // 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:196
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:172
~KLUAdapter() override
Destructor.
Definition: KLUAdapter.cpp:14
KLUAdapter()
Constructor.
Definition: KLUAdapter.cpp:22