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#ifdef WITH_KLU
22 mImplementationInUse = DirectLinearSolverImpl::KLU;
23#elif defined(WITH_SPARSE)
24 mImplementationInUse = DirectLinearSolverImpl::SparseLU;
25#else
26 mImplementationInUse = DirectLinearSolverImpl::DenseLU;
27#endif
28}
29
30template <typename VarType>
32 mSwitchedMatrices[std::bitset<SWITCH_NUM>(index)][0].setZero();
33}
34
35template <typename VarType>
37 Int freqIdx) {
38 mSwitchedMatrices[std::bitset<SWITCH_NUM>(swIdx)][freqIdx].setZero();
39}
40
41template <typename VarType>
43 std::size_t index, std::vector<std::shared_ptr<CPS::MNAInterface>> &comp) {
44 auto bit = std::bitset<SWITCH_NUM>(index);
45 auto &sys = mSwitchedMatrices[bit][0];
46 for (auto component : comp) {
47 component->mnaApplySystemMatrixStamp(sys);
48 }
49 for (UInt i = 0; i < mSwitches.size(); ++i)
50 mSwitches[i]->mnaApplySwitchSystemMatrixStamp(bit[i], sys, 0);
51
52 // Compute LU-factorization for system matrix
53 mDirectLinearSolvers[bit][0]->preprocessing(sys,
55 auto start = std::chrono::steady_clock::now();
56 mDirectLinearSolvers[bit][0]->factorize(sys);
57 auto end = std::chrono::steady_clock::now();
58 std::chrono::duration<Real> diff = end - start;
59 mFactorizeTimes.push_back(diff.count());
60}
61
62template <typename VarType>
64
67 // TODO: a direct linear solver configuration is only applied if system matrix recomputation is used
68 this->mDirectLinearSolverVariableSystemMatrix->setConfiguration(
70
71 SPDLOG_LOGGER_INFO(mSLog,
72 "Number of variable Elements: {}"
73 "\nNumber of MNA components: {}",
74 mVariableComps.size(), mMNAComponents.size());
75
76 // Build base matrix with only static elements
77 mBaseSystemMatrix.setZero();
78 for (auto statElem : mMNAComponents)
79 statElem->mnaApplySystemMatrixStamp(mBaseSystemMatrix);
80 SPDLOG_LOGGER_INFO(mSLog, "Base matrix with only static elements: {}",
81 Logger::matrixToString(mBaseSystemMatrix));
82 mSLog->flush();
83
84 // Continue from base matrix
86
87 // Now stamp initial state of variable elements and switches into matrix
88 SPDLOG_LOGGER_INFO(mSLog, "Stamping variable elements");
89 for (auto varElem : mMNAIntfVariableComps)
90 varElem->mnaApplySystemMatrixStamp(mVariableSystemMatrix);
91
92 SPDLOG_LOGGER_INFO(mSLog, "Initial system matrix with variable elements {}",
93 Logger::matrixToString(mVariableSystemMatrix));
94 /* TODO: find replacement for flush() */
95 mSLog->flush();
96
97 // Calculate factorization of current matrix
100
101 auto start = std::chrono::steady_clock::now();
103 auto end = std::chrono::steady_clock::now();
104 std::chrono::duration<Real> diff = end - start;
105 mFactorizeTimes.push_back(diff.count());
106}
107
108template <typename VarType>
110 Real time, Int timeStepCount) {
111 // Reset source vector
112 mRightSideVector.setZero();
113
114 // Add together the right side vector (computed by the components'
115 // pre-step tasks)
116 for (auto stamp : mRightVectorStamps)
117 mRightSideVector += *stamp;
118
119 // Get switch and variable comp status and update system matrix and lu factorization accordingly
123
124 // Calculate new solution vector
125 auto start = std::chrono::steady_clock::now();
128 auto end = std::chrono::steady_clock::now();
129 std::chrono::duration<Real> diff = end - start;
130 mSolveTimes.push_back(diff.count());
131
132 // TODO split into separate task? (dependent on x, updating all v attributes)
133 for (UInt nodeIdx = 0; nodeIdx < mNumNetNodes; ++nodeIdx)
134 mNodes[nodeIdx]->mnaUpdateVoltage(**mLeftSideVector);
135
136 // Components' states will be updated by the post-step tasks
137}
138
139template <typename VarType>
141 // Start from base matrix
143
144 // Now stamp variable elements and switches into matrix
145 for (auto comp : mMNAIntfVariableComps)
146 comp->mnaApplySystemMatrixStamp(mVariableSystemMatrix);
147
148 // Refactorization of matrix assuming that structure remained
149 // constant by omitting analyzePattern
150 auto start = std::chrono::steady_clock::now();
151 mDirectLinearSolverVariableSystemMatrix->partialRefactorize(
153 auto end = std::chrono::steady_clock::now();
154 std::chrono::duration<Real> diff = end - start;
155 mRecomputationTimes.push_back(diff.count());
157}
158
159template <typename VarType>
174
176 if (mSwitches.size() > SWITCH_NUM)
177 throw SystemError("Too many Switches.");
178
184 } else {
185 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
186 auto bit = std::bitset<SWITCH_NUM>(i);
187 mSwitchedMatrices[bit].push_back(
189 mDirectLinearSolvers[bit].push_back(
191 }
192 }
193}
194
196 if (mSwitches.size() > SWITCH_NUM)
197 throw SystemError("Too many Switches.");
198
199 if (mFrequencyParallel) {
200 for (UInt i = 0; i < std::pow(2, mSwitches.size()); ++i) {
201 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
202 auto bit = std::bitset<SWITCH_NUM>(i);
203 mSwitchedMatrices[bit].push_back(SparseMatrix(
205 mDirectLinearSolvers[bit].push_back(
207 }
208 }
209 } else if (mSystemMatrixRecomputation) {
211 SparseMatrix(2 * (mNumMatrixNodeIndices), 2 * (mNumMatrixNodeIndices));
213 SparseMatrix(2 * (mNumMatrixNodeIndices), 2 * (mNumMatrixNodeIndices));
214 } else {
215 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
216 auto bit = std::bitset<SWITCH_NUM>(i);
217 mSwitchedMatrices[bit].push_back(SparseMatrix(
219 mDirectLinearSolvers[bit].push_back(
221 }
222 }
223}
224
225template <typename VarType>
226std::shared_ptr<CPS::Task> MnaSolverDirect<VarType>::createSolveTask() {
227 return std::make_shared<MnaSolverDirect<VarType>::SolveTask>(*this);
228}
229
230template <typename VarType>
232 return std::make_shared<MnaSolverDirect<VarType>::SolveTaskRecomp>(*this);
233}
234
235template <typename VarType>
236std::shared_ptr<CPS::Task>
238 return std::make_shared<MnaSolverDirect<VarType>::SolveTaskHarm>(*this,
239 freqIdx);
240}
241
242template <typename VarType>
243std::shared_ptr<CPS::Task>
248
249template <typename VarType>
250std::shared_ptr<CPS::Task> MnaSolverDirect<VarType>::createLogTask() {
251 return std::make_shared<MnaSolverDirect<VarType>::LogTask>(*this);
252}
253
254template <typename VarType>
255void MnaSolverDirect<VarType>::solve(Real time, Int timeStepCount) {
256 const auto previousSwitchStatus = mCurrentSwitchStatus;
257 // Reset source vector
258 mRightSideVector.setZero();
259
260 // Add together the right side vector (computed by the components' pre-step tasks)
261 for (auto stamp : mRightVectorStamps)
262 mRightSideVector += *stamp;
263
266
267 if (mSwitchedMatrices.size() > 0) {
268 std::chrono::steady_clock::time_point start;
270 start = std::chrono::steady_clock::now();
271
274
276 auto end = std::chrono::steady_clock::now();
277 std::chrono::duration<Real> diff = end - start;
278 mSolveTimes.push_back(diff.count());
279 }
280 }
281
282 // CHECK: Is this really required? Or can operations actually become part of
283 // correctorStep and mnaPostStep?
284 for (auto syncGen : mSyncGen)
285 syncGen->updateVoltage(**mLeftSideVector);
286
287 // Reset number of iterations
288 mIter = 0;
289
290 // Additional solve steps for iterative models
291 if (mSyncGen.size() > 0) {
292 UInt numCompsRequireIter;
293 do {
294 // count synchronous generators that require iteration
295 numCompsRequireIter = 0;
296 for (auto syncGen : mSyncGen)
297 if (syncGen->requiresIteration())
298 numCompsRequireIter++;
299
300 // recompute solve step if at least one component demands iteration
301 if (numCompsRequireIter > 0) {
302 mIter++;
303
304 // Reset source vector
305 mRightSideVector.setZero();
306
309
310 for (auto syncGen : mSyncGen)
311 syncGen->correctorStep();
312
313 // Add together the right side vector (computed by the components' pre-step tasks)
314 for (auto stamp : mRightVectorStamps)
315 mRightSideVector += *stamp;
316
317 if (mSwitchedMatrices.size() > 0) {
318 auto start = std::chrono::steady_clock::now();
322 auto end = std::chrono::steady_clock::now();
323 std::chrono::duration<Real> diff = end - start;
324 mSolveTimes.push_back(diff.count());
325 }
326
327 // CHECK: Is this really required? Or can operations actually become part of
328 // correctorStep and mnaPostStep?
329 for (auto syncGen : mSyncGen)
330 syncGen->updateVoltage(**mLeftSideVector);
331 }
332 } while (numCompsRequireIter > 0);
333 }
334
335 mSystemMatrixChanged = (mCurrentSwitchStatus != previousSwitchStatus);
336
337 // TODO split into separate task? (dependent on x, updating all v attributes)
338 for (UInt nodeIdx = 0; nodeIdx < mNumNetNodes; ++nodeIdx)
339 mNodes[nodeIdx]->mnaUpdateVoltage(**mLeftSideVector);
340
341 // Components' states will be updated by the post-step tasks
342}
343
344template <typename VarType>
345void MnaSolverDirect<VarType>::solveWithHarmonics(Real time, Int timeStepCount,
346 Int freqIdx) {
347 mRightSideVectorHarm[freqIdx].setZero();
348
349 // Sum of right side vectors (computed by the components' pre-step tasks)
350 for (auto stamp : mRightVectorStamps)
351 mRightSideVectorHarm[freqIdx] += stamp->col(freqIdx);
352
353 **mLeftSideVectorHarm[freqIdx] =
355 mRightSideVectorHarm[freqIdx]);
356}
357
358template <typename VarType> void MnaSolverDirect<VarType>::logSystemMatrices() {
359 if (mFrequencyParallel) {
360 for (UInt i = 0; i < mSwitchedMatrices[std::bitset<SWITCH_NUM>(0)].size();
361 ++i) {
362 SPDLOG_LOGGER_INFO(mSLog, "System matrix for frequency: {:d} \n{:s}", i,
363 Logger::matrixToString(
364 mSwitchedMatrices[std::bitset<SWITCH_NUM>(0)][i]));
365 }
366
367 for (UInt i = 0; i < mRightSideVectorHarm.size(); ++i) {
368 SPDLOG_LOGGER_INFO(mSLog, "Right side vector for frequency: {:d} \n{:s}",
369 i, Logger::matrixToString(mRightSideVectorHarm[i]));
370 }
371
372 } else if (mSystemMatrixRecomputation) {
373 SPDLOG_LOGGER_INFO(mSLog, "Summarizing matrices: ");
374 SPDLOG_LOGGER_INFO(mSLog, "Base matrix with only static elements: {}",
375 Logger::matrixToString(mBaseSystemMatrix));
376 SPDLOG_LOGGER_INFO(mSLog, "Initial system matrix with variable elements {}",
377 Logger::matrixToString(mVariableSystemMatrix));
378 SPDLOG_LOGGER_INFO(mSLog, "Right side vector: {}",
379 Logger::matrixToString(mRightSideVector));
380 } else {
381 if (mSwitches.size() < 1) {
382 SPDLOG_LOGGER_INFO(mSLog, "System matrix: \n{}",
383 mSwitchedMatrices[std::bitset<SWITCH_NUM>(0)][0]);
384 } else {
385 SPDLOG_LOGGER_INFO(mSLog, "Initial switch status: {:s}",
386 mCurrentSwitchStatus.to_string());
387
388 for (auto sys : mSwitchedMatrices) {
389 SPDLOG_LOGGER_INFO(mSLog, "Switching System matrix {:s} \n{:s}",
390 sys.first.to_string(),
391 Logger::matrixToString(sys.second[0]));
392 }
393 }
394 SPDLOG_LOGGER_INFO(mSLog, "Right side vector: \n{}", mRightSideVector);
395 }
396}
397
398template <typename VarType> void MnaSolverDirect<VarType>::logLUTimes() {
401 logSolveTime();
402}
403
404template <typename VarType> void MnaSolverDirect<VarType>::logSolveTime() {
405 Real solveSum = 0.0;
406 Real solveMax = 0.0;
407 for (auto meas : mSolveTimes) {
408 solveSum += meas;
409 if (meas > solveMax)
410 solveMax = meas;
411 }
412 SPDLOG_LOGGER_INFO(mSLog, "Cumulative solve times: {:.12f}", solveSum);
413 SPDLOG_LOGGER_INFO(mSLog, "Average solve time: {:.12f}",
414 solveSum / static_cast<double>(mSolveTimes.size()));
415 SPDLOG_LOGGER_INFO(mSLog, "Maximum solve time: {:.12f}", solveMax);
416 SPDLOG_LOGGER_INFO(mSLog, "Number of solves: {:d}", mSolveTimes.size());
417}
418
419template <typename VarType>
421 for (auto meas : mFactorizeTimes) {
422 SPDLOG_LOGGER_INFO(mSLog, "LU factorization time: {:.12f}", meas);
423 }
424}
425
426template <typename VarType>
428 Real recompSum = 0.0;
429 Real recompMax = 0.0;
430 for (auto meas : mRecomputationTimes) {
431 recompSum += meas;
432 if (meas > recompMax)
433 recompMax = meas;
434 }
435 // Sometimes, refactorization is not used
436 if (mRecomputationTimes.size() != 0) {
437 SPDLOG_LOGGER_INFO(mSLog, "Cumulative refactorization times: {:.12f}",
438 recompSum);
439 SPDLOG_LOGGER_INFO(mSLog, "Average refactorization time: {:.12f}",
440 recompSum / ((double)mRecomputationTimes.size()));
441 SPDLOG_LOGGER_INFO(mSLog, "Maximum refactorization time: {:.12f}",
442 recompMax);
443 SPDLOG_LOGGER_INFO(mSLog, "Number of refactorizations: {:d}",
444 mRecomputationTimes.size());
445 }
446}
447
448template <typename VarType>
449std::shared_ptr<DirectLinearSolver>
451 CPS::Logger::Log mSLog) {
452 switch (this->mImplementationInUse) {
453 case DirectLinearSolverImpl::DenseLU:
454 return std::make_shared<DenseLUAdapter>(mSLog);
455#ifdef WITH_SPARSE
456 case DirectLinearSolverImpl::SparseLU:
457 return std::make_shared<SparseLUAdapter>(mSLog);
458#endif // WITH_SPARSE
459#ifdef WITH_KLU
460 case DirectLinearSolverImpl::KLU:
461 return std::make_shared<KLUAdapter>(mSLog);
462#endif
463#ifdef WITH_CUDA
464 case DirectLinearSolverImpl::CUDADense:
465 return std::make_shared<GpuDenseAdapter>(mSLog);
466#ifdef WITH_CUDA_SPARSE
467 case DirectLinearSolverImpl::CUDASparse:
468 return std::make_shared<GpuSparseAdapter>(mSLog);
469#endif
470#ifdef WITH_MAGMA
471 case DirectLinearSolverImpl::CUDAMagma:
472 return std::make_shared<GpuMagmaAdapter>(mSLog);
473#endif
474#endif
475 default:
476 throw CPS::SystemError("unsupported linear solver implementation.");
477 }
478}
479
480template <typename VarType>
482 DirectLinearSolverImpl implementation) {
483 this->mImplementationInUse = implementation;
484}
485
486template <typename VarType>
491
492} // namespace DPsim
493
494template 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 extractStateSpace(Real time)
Runs state-space extraction using the active linear solver.
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.
Bool mSystemMatrixChanged
Tracks active MNA system-matrix changes from the last solve step.
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.
Bool mVariableComponentChanged
Tracks variable-component changes from the last solve step.
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.
std::shared_ptr< CPS::Task > createStateSpaceExtractionTask() override
Create state-space extraction task for this solver implementation.
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:79
std::vector< Matrix > mRightSideVectorHarm
Source vector of known quantities.
Definition MNASolver.h:90
Matrix mRightSideVector
Source vector of known quantities.
Definition MNASolver.h:84
CPS::SystemTopology mSystem
System topology.
Definition MNASolver.h:64
std::vector< Real > mRecomputationTimes
LU refactorization measurements.
Definition MNASolver.h:122
std::vector< Real > mFactorizeTimes
LU factorization measurements.
Definition MNASolver.h:118
std::vector< CPS::Attribute< Matrix >::Ptr > mLeftSideVectorHarm
Solution vector of unknown quantities (parallel frequencies)
Definition MNASolver.h:211
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:99
UInt mNumNetNodes
Number of network nodes, single line equivalent.
Definition MNASolver.h:47
MNAStateSpaceExtractor::Ptr mStateSpaceExtractor
Extractor for the MNA-coupled state-space model.
Definition MNASolver.h:129
UInt mNumTotalMatrixNodeIndices
Total number of network and virtual nodes, considering individual phases and additional frequencies.
Definition MNASolver.h:59
CPS::MNASyncGenInterface::List mSyncGen
List of synchronous generators that need iterate to solve the differential equations.
Definition MNASolver.h:81
std::vector< const Matrix * > mRightVectorStamps
List of all right side vector contributions.
Definition MNASolver.h:86
void updateSwitchStatus()
Collects the status of switches to select correct system matrix.
std::vector< Real > mSolveTimes
Right-hand side solution measurements.
Definition MNASolver.h:120
std::vector< std::pair< UInt, UInt > > mListVariableSystemMatrixEntries
List of index pairs of varying matrix entries.
Definition MNASolver.h:61
CPS::MNAVariableCompInterface::List mVariableComps
Definition MNASolver.h:97
Int mNumRecomputations
Number of system matrix recomputations.
Definition MNASolver.h:94
CPS::MNAInterface::List mMNAComponents
List of MNA components with static stamp into system matrix.
Definition MNASolver.h:70
UInt mNumMatrixNodeIndices
Number of network and virtual nodes, considering individual phases.
Definition MNASolver.h:51
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:23
CPS::Attribute< Matrix >::Ptr mLeftSideVector
Solution vector of unknown quantities.
Definition MNASolver.h:208
CPS::MNASwitchInterface::List mSwitches
Definition MNASolver.h:73
CPS::SimNode< VarType >::List mNodes
List of simulation nodes.
Definition MNASolver.h:66
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