DPsim
Loading...
Searching...
No Matches
MNASolverDirect.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/MNASolverDirect.h>
10#include <dpsim/SequentialScheduler.h>
11
12using namespace DPsim;
13using namespace CPS;
14
15namespace DPsim {
16
17template <typename VarType>
18MnaSolverDirect<VarType>::MnaSolverDirect(String name, CPS::Domain domain,
19 CPS::Logger::Level logLevel)
20 : MnaSolver<VarType>(name, domain, logLevel) {
21 mImplementationInUse = DirectLinearSolverImpl::KLU;
22}
23
24template <typename VarType>
26 mSwitchedMatrices[std::bitset<SWITCH_NUM>(index)][0].setZero();
27}
28
29template <typename VarType>
31 Int freqIdx) {
32 mSwitchedMatrices[std::bitset<SWITCH_NUM>(swIdx)][freqIdx].setZero();
33}
34
35template <typename VarType>
37 std::size_t index, std::vector<std::shared_ptr<CPS::MNAInterface>> &comp) {
38 auto bit = std::bitset<SWITCH_NUM>(index);
39 auto &sys = mSwitchedMatrices[bit][0];
40 for (auto component : comp) {
41 component->mnaApplySystemMatrixStamp(sys);
42 }
43 for (UInt i = 0; i < mSwitches.size(); ++i)
44 mSwitches[i]->mnaApplySwitchSystemMatrixStamp(bit[i], sys, 0);
45
46 // Compute LU-factorization for system matrix
47 mDirectLinearSolvers[bit][0]->preprocessing(sys,
49 auto start = std::chrono::steady_clock::now();
50 mDirectLinearSolvers[bit][0]->factorize(sys);
51 auto end = std::chrono::steady_clock::now();
52 std::chrono::duration<Real> diff = end - start;
53 mFactorizeTimes.push_back(diff.count());
54}
55
56template <typename VarType>
58
61 // TODO: a direct linear solver configuration is only applied if system matrix recomputation is used
62 this->mDirectLinearSolverVariableSystemMatrix->setConfiguration(
64
65 SPDLOG_LOGGER_INFO(mSLog,
66 "Number of variable Elements: {}"
67 "\nNumber of MNA components: {}",
68 mVariableComps.size(), mMNAComponents.size());
69
70 // Build base matrix with only static elements
71 mBaseSystemMatrix.setZero();
72 for (auto statElem : mMNAComponents)
73 statElem->mnaApplySystemMatrixStamp(mBaseSystemMatrix);
74 SPDLOG_LOGGER_INFO(mSLog, "Base matrix with only static elements: {}",
75 Logger::matrixToString(mBaseSystemMatrix));
76 mSLog->flush();
77
78 // Continue from base matrix
80
81 // Now stamp switches into matrix
82 SPDLOG_LOGGER_INFO(mSLog, "Stamping switches");
83 for (auto sw : mMNAIntfSwitches)
84 sw->mnaApplySystemMatrixStamp(mVariableSystemMatrix);
85
86 // Now stamp initial state of variable elements into matrix
87 SPDLOG_LOGGER_INFO(mSLog, "Stamping variable elements");
88 for (auto varElem : mMNAIntfVariableComps)
89 varElem->mnaApplySystemMatrixStamp(mVariableSystemMatrix);
90
91 SPDLOG_LOGGER_INFO(mSLog, "Initial system matrix with variable elements {}",
92 Logger::matrixToString(mVariableSystemMatrix));
93 /* TODO: find replacement for flush() */
94 mSLog->flush();
95
96 // Calculate factorization of current matrix
99
100 auto start = std::chrono::steady_clock::now();
102 auto end = std::chrono::steady_clock::now();
103 std::chrono::duration<Real> diff = end - start;
104 mFactorizeTimes.push_back(diff.count());
105}
106
107template <typename VarType>
109 Real time, Int timeStepCount) {
110 // Reset source vector
111 mRightSideVector.setZero();
112
113 // Add together the right side vector (computed by the components'
114 // pre-step tasks)
115 for (auto stamp : mRightVectorStamps)
116 mRightSideVector += *stamp;
117
118 // Get switch and variable comp status and update system matrix and lu factorization accordingly
121
122 // Calculate new solution vector
123 auto start = std::chrono::steady_clock::now();
126 auto end = std::chrono::steady_clock::now();
127 std::chrono::duration<Real> diff = end - start;
128 mSolveTimes.push_back(diff.count());
129
130 // TODO split into separate task? (dependent on x, updating all v attributes)
131 for (UInt nodeIdx = 0; nodeIdx < mNumNetNodes; ++nodeIdx)
132 mNodes[nodeIdx]->mnaUpdateVoltage(**mLeftSideVector);
133
134 // Components' states will be updated by the post-step tasks
135}
136
137template <typename VarType>
139 // Start from base matrix
141
142 // Now stamp switches into matrix
143 for (auto sw : mMNAIntfSwitches)
144 sw->mnaApplySystemMatrixStamp(mVariableSystemMatrix);
145
146 // Now stamp variable elements into matrix
147 for (auto comp : mMNAIntfVariableComps)
148 comp->mnaApplySystemMatrixStamp(mVariableSystemMatrix);
149
150 // Refactorization of matrix assuming that structure remained
151 // constant by omitting analyzePattern
152 auto start = std::chrono::steady_clock::now();
153 mDirectLinearSolverVariableSystemMatrix->partialRefactorize(
155 auto end = std::chrono::steady_clock::now();
156 std::chrono::duration<Real> diff = end - start;
157 mRecomputationTimes.push_back(diff.count());
159}
160
162 if (mSwitches.size() > SWITCH_NUM)
163 throw SystemError("Too many Switches.");
164
170 } else {
171 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
172 auto bit = std::bitset<SWITCH_NUM>(i);
173 mSwitchedMatrices[bit].push_back(
175 mDirectLinearSolvers[bit].push_back(
177 }
178 }
179}
180
182 if (mSwitches.size() > SWITCH_NUM)
183 throw SystemError("Too many Switches.");
184
185 if (mFrequencyParallel) {
186 for (UInt i = 0; i < std::pow(2, mSwitches.size()); ++i) {
187 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
188 auto bit = std::bitset<SWITCH_NUM>(i);
189 mSwitchedMatrices[bit].push_back(SparseMatrix(
191 mDirectLinearSolvers[bit].push_back(
193 }
194 }
195 } else if (mSystemMatrixRecomputation) {
197 SparseMatrix(2 * (mNumMatrixNodeIndices), 2 * (mNumMatrixNodeIndices));
199 SparseMatrix(2 * (mNumMatrixNodeIndices), 2 * (mNumMatrixNodeIndices));
200 } else {
201 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
202 auto bit = std::bitset<SWITCH_NUM>(i);
203 mSwitchedMatrices[bit].push_back(SparseMatrix(
205 mDirectLinearSolvers[bit].push_back(
207 }
208 }
209}
210
211template <typename VarType>
212std::shared_ptr<CPS::Task> MnaSolverDirect<VarType>::createSolveTask() {
213 return std::make_shared<MnaSolverDirect<VarType>::SolveTask>(*this);
214}
215
216template <typename VarType>
218 return std::make_shared<MnaSolverDirect<VarType>::SolveTaskRecomp>(*this);
219}
220
221template <typename VarType>
222std::shared_ptr<CPS::Task>
224 return std::make_shared<MnaSolverDirect<VarType>::SolveTaskHarm>(*this,
225 freqIdx);
226}
227
228template <typename VarType>
229std::shared_ptr<CPS::Task> MnaSolverDirect<VarType>::createLogTask() {
230 return std::make_shared<MnaSolverDirect<VarType>::LogTask>(*this);
231}
232
233template <typename VarType>
234void MnaSolverDirect<VarType>::solve(Real time, Int timeStepCount) {
235 // Reset source vector
236 mRightSideVector.setZero();
237
238 // Add together the right side vector (computed by the components' pre-step tasks)
239 for (auto stamp : mRightVectorStamps)
240 mRightSideVector += *stamp;
241
244
245 if (mSwitchedMatrices.size() > 0) {
246 std::chrono::steady_clock::time_point start;
248 start = std::chrono::steady_clock::now();
249
252
254 auto end = std::chrono::steady_clock::now();
255 std::chrono::duration<Real> diff = end - start;
256 mSolveTimes.push_back(diff.count());
257 }
258 }
259
260 // CHECK: Is this really required? Or can operations actually become part of
261 // correctorStep and mnaPostStep?
262 for (auto syncGen : mSyncGen)
263 syncGen->updateVoltage(**mLeftSideVector);
264
265 // Reset number of iterations
266 mIter = 0;
267
268 // Additional solve steps for iterative models
269 if (mSyncGen.size() > 0) {
270 UInt numCompsRequireIter;
271 do {
272 // count synchronous generators that require iteration
273 numCompsRequireIter = 0;
274 for (auto syncGen : mSyncGen)
275 if (syncGen->requiresIteration())
276 numCompsRequireIter++;
277
278 // recompute solve step if at least one component demands iteration
279 if (numCompsRequireIter > 0) {
280 mIter++;
281
282 // Reset source vector
283 mRightSideVector.setZero();
284
287
288 for (auto syncGen : mSyncGen)
289 syncGen->correctorStep();
290
291 // Add together the right side vector (computed by the components' pre-step tasks)
292 for (auto stamp : mRightVectorStamps)
293 mRightSideVector += *stamp;
294
295 if (mSwitchedMatrices.size() > 0) {
296 auto start = std::chrono::steady_clock::now();
300 auto end = std::chrono::steady_clock::now();
301 std::chrono::duration<Real> diff = end - start;
302 mSolveTimes.push_back(diff.count());
303 }
304
305 // CHECK: Is this really required? Or can operations actually become part of
306 // correctorStep and mnaPostStep?
307 for (auto syncGen : mSyncGen)
308 syncGen->updateVoltage(**mLeftSideVector);
309 }
310 } while (numCompsRequireIter > 0);
311 }
312
313 // TODO split into separate task? (dependent on x, updating all v attributes)
314 for (UInt nodeIdx = 0; nodeIdx < mNumNetNodes; ++nodeIdx)
315 mNodes[nodeIdx]->mnaUpdateVoltage(**mLeftSideVector);
316
317 // Components' states will be updated by the post-step tasks
318}
319
320template <typename VarType>
321void MnaSolverDirect<VarType>::solveWithHarmonics(Real time, Int timeStepCount,
322 Int freqIdx) {
323 mRightSideVectorHarm[freqIdx].setZero();
324
325 // Sum of right side vectors (computed by the components' pre-step tasks)
326 for (auto stamp : mRightVectorStamps)
327 mRightSideVectorHarm[freqIdx] += stamp->col(freqIdx);
328
329 **mLeftSideVectorHarm[freqIdx] =
331 mRightSideVectorHarm[freqIdx]);
332}
333
334template <typename VarType> void MnaSolverDirect<VarType>::logSystemMatrices() {
335 if (mFrequencyParallel) {
336 for (UInt i = 0; i < mSwitchedMatrices[std::bitset<SWITCH_NUM>(0)].size();
337 ++i) {
338 SPDLOG_LOGGER_INFO(mSLog, "System matrix for frequency: {:d} \n{:s}", i,
339 Logger::matrixToString(
340 mSwitchedMatrices[std::bitset<SWITCH_NUM>(0)][i]));
341 }
342
343 for (UInt i = 0; i < mRightSideVectorHarm.size(); ++i) {
344 SPDLOG_LOGGER_INFO(mSLog, "Right side vector for frequency: {:d} \n{:s}",
345 i, Logger::matrixToString(mRightSideVectorHarm[i]));
346 }
347
348 } else if (mSystemMatrixRecomputation) {
349 SPDLOG_LOGGER_INFO(mSLog, "Summarizing matrices: ");
350 SPDLOG_LOGGER_INFO(mSLog, "Base matrix with only static elements: {}",
351 Logger::matrixToString(mBaseSystemMatrix));
352 SPDLOG_LOGGER_INFO(mSLog, "Initial system matrix with variable elements {}",
353 Logger::matrixToString(mVariableSystemMatrix));
354 SPDLOG_LOGGER_INFO(mSLog, "Right side vector: {}",
355 Logger::matrixToString(mRightSideVector));
356 } else {
357 if (mSwitches.size() < 1) {
358 SPDLOG_LOGGER_INFO(mSLog, "System matrix: \n{}",
359 mSwitchedMatrices[std::bitset<SWITCH_NUM>(0)][0]);
360 } else {
361 SPDLOG_LOGGER_INFO(mSLog, "Initial switch status: {:s}",
362 mCurrentSwitchStatus.to_string());
363
364 for (auto sys : mSwitchedMatrices) {
365 SPDLOG_LOGGER_INFO(mSLog, "Switching System matrix {:s} \n{:s}",
366 sys.first.to_string(),
367 Logger::matrixToString(sys.second[0]));
368 }
369 }
370 SPDLOG_LOGGER_INFO(mSLog, "Right side vector: \n{}", mRightSideVector);
371 }
372}
373
374template <typename VarType> void MnaSolverDirect<VarType>::logLUTimes() {
377 logSolveTime();
378}
379
380template <typename VarType> void MnaSolverDirect<VarType>::logSolveTime() {
381 Real solveSum = 0.0;
382 Real solveMax = 0.0;
383 for (auto meas : mSolveTimes) {
384 solveSum += meas;
385 if (meas > solveMax)
386 solveMax = meas;
387 }
388 SPDLOG_LOGGER_INFO(mSLog, "Cumulative solve times: {:.12f}", solveSum);
389 SPDLOG_LOGGER_INFO(mSLog, "Average solve time: {:.12f}",
390 solveSum / static_cast<double>(mSolveTimes.size()));
391 SPDLOG_LOGGER_INFO(mSLog, "Maximum solve time: {:.12f}", solveMax);
392 SPDLOG_LOGGER_INFO(mSLog, "Number of solves: {:d}", mSolveTimes.size());
393}
394
395template <typename VarType>
397 for (auto meas : mFactorizeTimes) {
398 SPDLOG_LOGGER_INFO(mSLog, "LU factorization time: {:.12f}", meas);
399 }
400}
401
402template <typename VarType>
404 Real recompSum = 0.0;
405 Real recompMax = 0.0;
406 for (auto meas : mRecomputationTimes) {
407 recompSum += meas;
408 if (meas > recompMax)
409 recompMax = meas;
410 }
411 // Sometimes, refactorization is not used
412 if (mRecomputationTimes.size() != 0) {
413 SPDLOG_LOGGER_INFO(mSLog, "Cumulative refactorization times: {:.12f}",
414 recompSum);
415 SPDLOG_LOGGER_INFO(mSLog, "Average refactorization time: {:.12f}",
416 recompSum / ((double)mRecomputationTimes.size()));
417 SPDLOG_LOGGER_INFO(mSLog, "Maximum refactorization time: {:.12f}",
418 recompMax);
419 SPDLOG_LOGGER_INFO(mSLog, "Number of refactorizations: {:d}",
420 mRecomputationTimes.size());
421 }
422}
423
424template <typename VarType>
425std::shared_ptr<DirectLinearSolver>
427 CPS::Logger::Log mSLog) {
428 switch (this->mImplementationInUse) {
429 case DirectLinearSolverImpl::DenseLU:
430 return std::make_shared<DenseLUAdapter>(mSLog);
431 case DirectLinearSolverImpl::SparseLU:
432 return std::make_shared<SparseLUAdapter>(mSLog);
433#ifdef WITH_KLU
434 case DirectLinearSolverImpl::KLU:
435 return std::make_shared<KLUAdapter>(mSLog);
436#endif
437#ifdef WITH_CUDA
438 case DirectLinearSolverImpl::CUDADense:
439 return std::make_shared<GpuDenseAdapter>(mSLog);
440#ifdef WITH_CUDA_SPARSE
441 case DirectLinearSolverImpl::CUDASparse:
442 return std::make_shared<GpuSparseAdapter>(mSLog);
443#endif
444#ifdef WITH_MAGMA
445 case DirectLinearSolverImpl::CUDAMagma:
446 return std::make_shared<GpuMagmaAdapter>(mSLog);
447#endif
448#endif
449 default:
450 throw CPS::SystemError("unsupported linear solver implementation.");
451 }
452}
453
454template <typename VarType>
456 DirectLinearSolverImpl implementation) {
457 this->mImplementationInUse = implementation;
458}
459
460template <typename VarType>
465
466} // namespace DPsim
467
468template class DPsim::MnaSolverDirect<Real>;
Solver class using Modified Nodal Analysis (MNA).
std::shared_ptr< CPS::Task > createSolveTask() override
Create a solve task for this solver implementation.
void stampVariableSystemMatrix() override
Stamps components into the variable system matrix.
void logSolveTime()
Logging of the right-hand-side solution time.
void logFactorizationTime()
Logging of the LU factorization time.
void solve(Real time, Int timeStepCount) override
Solves system for single frequency.
void logRecomputationTime()
Logging of the LU refactorization time.
virtual void recomputeSystemMatrix(Real time)
Recomputes systems matrix.
SparseMatrix mVariableSystemMatrix
System matrix including stamp of static and variable elements.
SparseMatrix mBaseSystemMatrix
System matrix including all static elements.
std::unordered_map< std::bitset< SWITCH_NUM >, std::vector< SparseMatrix > > mSwitchedMatrices
Map of system matrices where the key is the bitset describing the switch states.
std::shared_ptr< DirectLinearSolver > createDirectSolverImplementation(CPS::Logger::Log mSLog)
Returns a pointer to an object of type DirectLinearSolver.
void setDirectLinearSolverConfiguration(DirectLinearSolverConfiguration &configuration) override
Sets the linear solver configuration.
DirectLinearSolverImpl mImplementationInUse
LU factorization indicator.
std::shared_ptr< DirectLinearSolver > mDirectLinearSolverVariableSystemMatrix
LU factorization of variable system matrix.
void setDirectLinearSolverImplementation(DirectLinearSolverImpl implementation)
Sets the linear solver to "implementation" and creates an object.
std::shared_ptr< CPS::Task > createSolveTaskHarm(UInt freqIdx) override
Create a solve task for this solver implementation.
std::shared_ptr< CPS::Task > createSolveTaskRecomp() override
Create a solve task for recomputation solver.
void logSystemMatrices() override
Logging of system matrices and source vector.
void createEmptySystemMatrix() override
Create system matrix.
std::shared_ptr< CPS::Task > createLogTask() override
Create a solve task for this solver implementation.
void logLUTimes() override
log LU decomposition times
void switchedMatrixEmpty(std::size_t index) override
Sets all entries in the matrix with the given switch index to zero.
void solveWithSystemMatrixRecomputation(Real time, Int timeStepCount) override
Solves the system with variable system matrix.
void switchedMatrixStamp(std::size_t index, std::vector< std::shared_ptr< CPS::MNAInterface > > &comp) override
Applies a component stamp to the matrix with the given switch index.
std::unordered_map< std::bitset< SWITCH_NUM >, std::vector< std::shared_ptr< DirectLinearSolver > > > mDirectLinearSolvers
Map of direct linear solvers related to the system matrices.
MnaSolverDirect(String name, CPS::Domain domain=CPS::Domain::DP, CPS::Logger::Level logLevel=CPS::Logger::Level::info)
DirectLinearSolverConfiguration mConfigurationInUse
LU factorization configuration.
void solveWithHarmonics(Real time, Int timeStepCount, Int freqIdx) override
Solves system for multiple frequencies.
std::bitset< SWITCH_NUM > mCurrentSwitchStatus
Current status of all switches encoded as bitset.
Definition MNASolver.h:78
std::vector< Matrix > mRightSideVectorHarm
Source vector of known quantities.
Definition MNASolver.h:89
Matrix mRightSideVector
Source vector of known quantities.
Definition MNASolver.h:83
CPS::SystemTopology mSystem
System topology.
Definition MNASolver.h:63
std::vector< Real > mRecomputationTimes
LU refactorization measurements.
Definition MNASolver.h:121
std::vector< Real > mFactorizeTimes
LU factorization measurements.
Definition MNASolver.h:117
std::vector< CPS::Attribute< Matrix >::Ptr > mLeftSideVectorHarm
Solution vector of unknown quantities (parallel frequencies)
Definition MNASolver.h:199
Bool hasVariableComponentChanged()
Checks whether the status of variable MNA elements have changed.
CPS::MNAInterface::List mMNAIntfVariableComps
List of variable components if they must be accessed as MNAInterface objects.
Definition MNASolver.h:98
UInt mNumNetNodes
Number of network nodes, single line equivalent.
Definition MNASolver.h:46
UInt mNumTotalMatrixNodeIndices
Total number of network and virtual nodes, considering individual phases and additional frequencies.
Definition MNASolver.h:58
CPS::MNASyncGenInterface::List mSyncGen
List of synchronous generators that need iterate to solve the differential equations.
Definition MNASolver.h:80
CPS::MNAInterface::List mMNAIntfSwitches
List of switches if they must be accessed as MNAInterface objects.
Definition MNASolver.h:74
std::vector< const Matrix * > mRightVectorStamps
List of all right side vector contributions.
Definition MNASolver.h:85
void updateSwitchStatus()
Collects the status of switches to select correct system matrix.
std::vector< Real > mSolveTimes
Right-hand side solution measurements.
Definition MNASolver.h:119
std::vector< std::pair< UInt, UInt > > mListVariableSystemMatrixEntries
List of index pairs of varying matrix entries.
Definition MNASolver.h:60
CPS::MNAVariableCompInterface::List mVariableComps
Definition MNASolver.h:96
Int mNumRecomputations
Number of system matrix recomputations.
Definition MNASolver.h:93
CPS::MNAInterface::List mMNAComponents
List of MNA components with static stamp into system matrix.
Definition MNASolver.h:69
UInt mNumMatrixNodeIndices
Number of network and virtual nodes, considering individual phases.
Definition MNASolver.h:50
MnaSolver(String name, CPS::Domain domain=CPS::Domain::DP, CPS::Logger::Level logLevel=CPS::Logger::Level::info)
Constructor should not be called by users but by Simulation.
Definition MNASolver.cpp:19
CPS::Attribute< Matrix >::Ptr mLeftSideVector
Solution vector of unknown quantities.
Definition MNASolver.h:196
CPS::MNASwitchInterface::List mSwitches
Definition MNASolver.h:72
CPS::SimNode< VarType >::List mNodes
List of simulation nodes.
Definition MNASolver.h:65
Bool mSystemMatrixRecomputation
Enable recomputation of system matrix during simulation.
Definition Solver.h:64
CPS::Logger::Log mSLog
Logger.
Definition Solver.h:45
Bool mIsInInitialization
Determines if solver is in initialization phase, which requires different behavior.
Definition Solver.h:59
Bool mLogSolveTimes
Collect step time for logging.
Definition Solver.h:43
Bool mFrequencyParallel
Activates parallelized computation of frequencies.
Definition Solver.h:49