DPsim
Loading...
Searching...
No Matches
DiakopticsSolver.cpp
Go to the documentation of this file.
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
10
11#include <iomanip>
12
15#include <dpsim/Definitions.h>
16#include <dpsim/KLUAdapter.h>
17
18using namespace CPS;
19using namespace DPsim;
20
21namespace DPsim {
22
23template <typename VarType>
25 String name, SystemTopology system, IdentifiedObject::List tearComponents,
26 Real timeStep, Logger::Level logLevel)
27 : Solver(name, logLevel),
30 mTimeStep = timeStep;
31
32 // Raw source and solution vector logging
33 mLeftVectorLog = std::make_shared<DataLogger>(
34 name + "_LeftVector", logLevel != CPS::Logger::Level::off);
35 mRightVectorLog = std::make_shared<DataLogger>(
36 name + "_RightVector", logLevel != CPS::Logger::Level::off);
37
38 for (auto comp : tearComponents) {
39 auto pcomp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
40 if (pcomp)
41 mTearComponents.push_back(pcomp);
42 }
43 init(system);
44}
45
46template <typename VarType>
47void DiakopticsSolver<VarType>::init(SystemTopology &system) {
48 std::vector<SystemTopology> subnets;
49 mSystem = system;
50 if (mSystem.mNodes.size() > 0)
51 mPhaseType = mSystem.mNodes.at(0)->phaseType();
52 mSystemFrequency = system.mSystemFrequency;
53
54 system.splitSubnets<VarType>(subnets);
55 initSubnets(subnets);
56 setLogColumns();
57 createMatrices();
58 initComponents();
59 initMatrices();
60}
61
62template <typename VarType>
63void DiakopticsSolver<VarType>::initSubnets(
64 const std::vector<SystemTopology> &subnets) {
65 mSubnets.resize(subnets.size());
66 for (UInt i = 0; i < subnets.size(); ++i) {
67 // Add nodes to the list and ignore ground nodes.
68 for (auto baseNode : subnets[i].mNodes) {
69 if (!baseNode->isGround()) {
70 auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
71 mSubnets[i].nodes.push_back(node);
72 }
73 }
74
75 for (auto comp : subnets[i].mComponents) {
76 // TODO switches
77 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
78 if (mnaComp)
79 mSubnets[i].components.push_back(mnaComp);
80
81 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
82 if (sigComp)
83 mSimSignalComps.push_back(sigComp);
84
85 auto mnaVarComp =
86 std::dynamic_pointer_cast<CPS::MNAVariableCompInterface>(comp);
87 if (mnaVarComp)
88 mSubnets[i].mVariableComps.push_back(mnaVarComp);
89 }
90 }
91
92 // Create map that relates nodes to subnetworks
93 for (auto &net : mSubnets) {
94 for (auto &node : net.nodes) {
95 mNodeSubnetMap[node] = &net;
96 }
97 }
98
99 for (UInt idx = 0; idx < mTearComponents.size(); ++idx) {
100 auto comp = mTearComponents[idx];
101 auto tComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
102 if (!tComp)
103 throw SystemError("Unsupported component type for diakoptics");
104
105 if (comp->hasVirtualNodes()) {
106 for (UInt node = 0; node < comp->virtualNodesNumber(); ++node) {
107 // sim node number doesn't matter here because it shouldn't be used anyway
108 // TODO adapt this to new concept
109 comp->setVirtualNodeAt(std::make_shared<CPS::SimNode<VarType>>(node),
110 node);
111 }
112 }
113 tComp->mnaTearSetIdx(idx);
114 comp->initializeFromNodesAndTerminals(mSystemFrequency);
115 tComp->mnaTearInitialize(2 * PI * mSystemFrequency, mTimeStep);
116
117 for (auto gndComp : tComp->mnaTearGroundComponents()) {
118 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(gndComp);
119 Subnet *net = nullptr;
120 if (pComp->node(0)->isGround()) {
121 net = mNodeSubnetMap[pComp->node(1)];
122 } else if (pComp->node(1)->isGround()) {
123 net = mNodeSubnetMap[pComp->node(0)];
124 } else {
125 throw SystemError(
126 "Invalid ground component passed from torn component");
127 }
128 net->components.push_back(gndComp);
129 }
130 }
131
132 for (UInt i = 0; i < subnets.size(); ++i) {
133 collectVirtualNodes(i);
134 assignMatrixNodeIndices(i);
135 }
136}
137
138template <typename VarType>
139void DiakopticsSolver<VarType>::collectVirtualNodes(int net) {
140 mSubnets[net].mVirtualNodeNum = 0;
141 mSubnets[net].mRealNetNodeNum = static_cast<UInt>(mSubnets[net].nodes.size());
142
143 for (auto comp : mSubnets[net].components) {
144 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
145 if (!pComp)
146 continue;
147
148 if (pComp->hasVirtualNodes()) {
149 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
150 ++(mSubnets[net].mVirtualNodeNum);
151 mSubnets[net].nodes.push_back(pComp->virtualNode(node));
152 }
153 }
154
155 if (pComp->hasSubComponents()) {
156 for (auto pSubComp : pComp->subComponents()) {
157 for (UInt node = 0; node < pSubComp->virtualNodesNumber(); ++node) {
158 auto vnode = pSubComp->virtualNode(node);
159 bool alreadyRegistered = false;
160 for (auto registeredNode : mSubnets[net].nodes) {
161 if (registeredNode == vnode) {
162 alreadyRegistered = true;
163 break;
164 }
165 }
166 if (alreadyRegistered)
167 continue;
168 ++(mSubnets[net].mVirtualNodeNum);
169 mSubnets[net].nodes.push_back(vnode);
170 }
171 }
172 }
173 }
174 SPDLOG_LOGGER_INFO(mSLog, "Subnet {} has {} real network nodes.", net,
175 mSubnets[net].mRealNetNodeNum);
176 SPDLOG_LOGGER_INFO(mSLog, "Subnet {} has {} virtual nodes.", net,
177 mSubnets[net].mVirtualNodeNum);
178}
179
180template <typename VarType>
181void DiakopticsSolver<VarType>::assignMatrixNodeIndices(int net) {
182 UInt matrixNodeIndexIdx = 0;
183 for (UInt idx = 0; idx < mSubnets[net].nodes.size(); ++idx) {
184 auto &node = mSubnets[net].nodes[idx];
185
186 node->setMatrixNodeIndex(0, matrixNodeIndexIdx);
187 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to node {}",
188 matrixNodeIndexIdx, node->name());
189 ++matrixNodeIndexIdx;
190
191 if (node->phaseType() == CPS::PhaseType::ABC) {
192 node->setMatrixNodeIndex(1, matrixNodeIndexIdx);
193 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to node {} phase B",
194 matrixNodeIndexIdx, node->name());
195 ++matrixNodeIndexIdx;
196 node->setMatrixNodeIndex(2, matrixNodeIndexIdx);
197 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to node {} phase C",
198 matrixNodeIndexIdx, node->name());
199 ++matrixNodeIndexIdx;
200 }
201 }
202 setSubnetSize(net, matrixNodeIndexIdx);
203
204 if (net == 0)
205 mSubnets[net].sysOff = 0;
206 else
207 mSubnets[net].sysOff = mSubnets[net - 1].sysOff + mSubnets[net - 1].sysSize;
208}
209
210template <> void DiakopticsSolver<Real>::setSubnetSize(int net, UInt nodes) {
211 mSubnets[net].sysSize = nodes;
212 mSubnets[net].mCmplOff = 0;
213}
214
215template <> void DiakopticsSolver<Complex>::setSubnetSize(int net, UInt nodes) {
216 mSubnets[net].sysSize = 2 * nodes;
217 mSubnets[net].mCmplOff = nodes;
218}
219
220template <> void DiakopticsSolver<Real>::setLogColumns() {
221 // nothing to do, column names generated by DataLogger are correct
222}
223
224template <> void DiakopticsSolver<Complex>::setLogColumns() {
225 std::vector<String> names;
226 for (auto &subnet : mSubnets) {
227 for (UInt i = subnet.sysOff; i < subnet.sysOff + subnet.sysSize; ++i) {
228 std::stringstream name;
229 if (i < subnet.sysOff + subnet.mCmplOff)
230 name << "node" << std::setfill('0') << std::setw(5)
231 << i - subnet.sysOff / 2 << ".real";
232 else
233 name << "node" << std::setfill('0') << std::setw(5)
234 << i - (subnet.sysOff + subnet.sysSize) / 2 << ".imag";
235 names.push_back(name.str());
236 }
237 }
238 mLeftVectorLog->setColumnNames(names);
239 mRightVectorLog->setColumnNames(names);
240}
241
242template <typename VarType> void DiakopticsSolver<VarType>::createMatrices() {
243 UInt totalSize = mSubnets.back().sysOff + mSubnets.back().sysSize;
244 mSystemMatrix = Matrix::Zero(totalSize, totalSize);
245
246 mRightSideVector = Matrix::Zero(totalSize, 1);
247 mLeftSideVector = Matrix::Zero(totalSize, 1);
248 **mOrigLeftSideVector = Matrix::Zero(totalSize, 1);
249 **mMappedTearCurrents = Matrix::Zero(totalSize, 1);
250
251 for (auto &net : mSubnets) {
252 // The subnets' components expect to be passed a left-side vector matching
253 // the size of the subnet, so we have to create separate vectors here and
254 // copy the solution there
255 net.leftVector = AttributeStatic<Matrix>::make();
256 net.leftVector->set(Matrix::Zero(net.sysSize, 1));
257 }
258
259 createTearMatrices(totalSize);
260}
261
262template <> void DiakopticsSolver<Real>::createTearMatrices(UInt totalSize) {
263 int phaseMultiplier = 1;
264 if (mPhaseType == PhaseType::ABC) {
265 phaseMultiplier = 3;
266 }
267 mTearTopology =
268 Matrix::Zero(totalSize, mTearComponents.size() * phaseMultiplier);
269 mTearImpedance =
270 CPS::SparseMatrixRow(mTearComponents.size() * phaseMultiplier,
271 mTearComponents.size() * phaseMultiplier);
272 mTearCurrents = Matrix::Zero(mTearComponents.size() * phaseMultiplier, 1);
273 mTearVoltages = Matrix::Zero(mTearComponents.size() * phaseMultiplier, 1);
274}
275
276template <> void DiakopticsSolver<Complex>::createTearMatrices(UInt totalSize) {
277 int phaseMultiplier = 1;
278 if (mPhaseType == PhaseType::ABC) {
279 phaseMultiplier = 3;
280 }
281 mTearTopology =
282 Matrix::Zero(totalSize, 2 * mTearComponents.size() * phaseMultiplier);
283 mTearImpedance =
284 CPS::SparseMatrixRow(2 * mTearComponents.size() * phaseMultiplier,
285 2 * mTearComponents.size() * phaseMultiplier);
286 mTearCurrents = Matrix::Zero(2 * mTearComponents.size() * phaseMultiplier, 1);
287 mTearVoltages = Matrix::Zero(2 * mTearComponents.size() * phaseMultiplier, 1);
288}
289
290template <typename VarType> void DiakopticsSolver<VarType>::initComponents() {
291 for (UInt net = 0; net < mSubnets.size(); ++net) {
292 for (auto comp : mSubnets[net].components) {
293 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
294 if (!pComp)
295 continue;
296 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
297 }
298
299 // Initialize MNA specific parts of components.
300 for (auto comp : mSubnets[net].components) {
301 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep,
302 mSubnets[net].leftVector);
303 const Matrix &stamp = comp->getRightVector()->get();
304 if (stamp.size() != 0) {
305 mSubnets[net].rightVectorStamps.push_back(&stamp);
306 }
307 }
308 }
309 // Initialize signal components.
310 for (auto comp : mSimSignalComps)
311 comp->initialize(mSystem.mSystemOmega, mTimeStep);
312
313 // Initialize nodes
314 for (UInt net = 0; net < mSubnets.size(); ++net)
315 for (auto node : mSubnets[net].nodes)
316 node->initialize();
317}
318
319template <typename VarType> void DiakopticsSolver<VarType>::initMatrices() {
320 for (auto &net : mSubnets) {
321 // mnaApplySystemMatrixStamp needs a concrete Matrix, not a block reference
322 // (it is virtual, so it cannot be templated to accept a block expression).
323 net.systemMatrix = SparseMatrix(net.sysSize, net.sysSize);
324 for (auto comp : net.components) {
325 comp->mnaApplySystemMatrixStamp(net.systemMatrix);
326 }
327 auto block =
328 mSystemMatrix.block(net.sysOff, net.sysOff, net.sysSize, net.sysSize);
329 block = net.systemMatrix;
330 SPDLOG_LOGGER_INFO(mSLog, "Block: \n{}", block);
331 net.listVariableEntries.clear();
332 for (auto varElem : net.mVariableComps)
333 for (auto varEntry : varElem->mVariableSystemMatrixEntries)
334 net.listVariableEntries.push_back(varEntry);
335 net.directLinearSolver = std::make_shared<KLUAdapter>(mSLog);
336 net.directLinearSolver->preprocessing(net.systemMatrix,
337 net.listVariableEntries);
338 net.directLinearSolver->factorize(net.systemMatrix);
339 }
340 SPDLOG_LOGGER_INFO(mSLog, "Complete system matrix: \n{}", mSystemMatrix);
341
342 // initialize tear topology matrix and impedance matrix of removed network
343 for (UInt compIdx = 0; compIdx < mTearComponents.size(); ++compIdx) {
344 applyTearComponentStamp(compIdx);
345 }
346 SPDLOG_LOGGER_INFO(mSLog, "Topology matrix: \n{}", mTearTopology);
347 SPDLOG_LOGGER_INFO(mSLog, "Removed impedance matrix: \n{}", mTearImpedance);
348 // TODO this can be sped up as well by using the block diagonal form of Yinv
349 mSystemInverseTearTopology =
350 Matrix::Zero(mTearTopology.rows(), mTearTopology.cols());
351 for (auto &net : mSubnets) {
352 Matrix tearTopoBlock =
353 mTearTopology.block(net.sysOff, 0, net.sysSize, mTearTopology.cols());
354 mSystemInverseTearTopology.block(net.sysOff, 0, net.sysSize,
355 mTearTopology.cols()) =
356 net.directLinearSolver->solve(tearTopoBlock);
357
358 // Cache tear columns coupled to this subnet (used by recomputeSubnetMatrix)
359 net.tearColumns.clear();
360 for (UInt col = 0; col < static_cast<UInt>(mTearTopology.cols()); ++col) {
361 if (!tearTopoBlock.col(col).isZero())
362 net.tearColumns.push_back(col);
363 }
364 }
365 mTearSchur =
366 mTearImpedance + mTearTopology.transpose() * mSystemInverseTearTopology;
367 mTotalTearImpedance = Eigen::PartialPivLU<Matrix>(mTearSchur);
368 SPDLOG_LOGGER_INFO(mSLog,
369 "Total removed impedance matrix LU decomposition: \n{}",
370 mTotalTearImpedance.matrixLU());
371
372 // Compute subnet right side (source) vectors for debugging
373 for (auto &net : mSubnets) {
374 Matrix rInit = Matrix::Zero(net.sysSize, 1);
375
376 for (auto comp : net.components) {
377 comp->mnaApplyRightSideVectorStamp(rInit);
378 }
379 SPDLOG_LOGGER_INFO(mSLog, "Source block: \n{}", rInit);
380 }
381}
382
383template <> void DiakopticsSolver<Real>::applyTearComponentStamp(UInt compIdx) {
384 auto comp = mTearComponents[compIdx];
385
386 // Use triplets to populate the sparse matrix
387 std::vector<Eigen::Triplet<double>> triplets;
388
389 // Node 0 contributions
390 if (comp->node(0)->phaseType() == CPS::PhaseType::ABC) {
391 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
392 comp->node(0)->matrixNodeIndex(PhaseType::A),
393 compIdx * 3, 1);
394 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
395 comp->node(0)->matrixNodeIndex(PhaseType::B),
396 compIdx * 3 + 1, 1);
397 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
398 comp->node(0)->matrixNodeIndex(PhaseType::C),
399 compIdx * 3 + 2, 1);
400 } else {
401 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
402 comp->node(0)->matrixNodeIndex(),
403 compIdx, 1);
404 }
405
406 // Node 1 contributions
407 if (comp->node(0)->phaseType() == CPS::PhaseType::ABC) {
408 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
409 comp->node(1)->matrixNodeIndex(PhaseType::A),
410 compIdx * 3, -1);
411 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
412 comp->node(1)->matrixNodeIndex(PhaseType::B),
413 compIdx * 3 + 1, -1);
414 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
415 comp->node(1)->matrixNodeIndex(PhaseType::C),
416 compIdx * 3 + 2, -1);
417 } else {
418 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
419 comp->node(1)->matrixNodeIndex(),
420 compIdx, -1);
421 }
422
423 // Apply triplets to the sparse matrix
424 for (const auto &triplet : triplets) {
425 mTearTopology.coeffRef(triplet.row(), triplet.col()) += triplet.value();
426 }
427
428 // Call tear component stamp function
429 auto tearComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
430 tearComp->mnaTearApplyMatrixStamp(mTearImpedance);
431}
432
433template <>
434void DiakopticsSolver<Complex>::applyTearComponentStamp(UInt compIdx) {
435 auto comp = mTearComponents[compIdx];
436
437 // Use triplets to populate the sparse matrix
438 std::vector<Eigen::Triplet<double>> triplets;
439
440 // Node 0 contributions
441 if (comp->node(0)->phaseType() == CPS::PhaseType::ABC) {
442 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
443 comp->node(0)->matrixNodeIndex(PhaseType::A),
444 compIdx * 3, 1);
445 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
446 mNodeSubnetMap[comp->node(0)]->mCmplOff +
447 comp->node(0)->matrixNodeIndex(PhaseType::A),
448 mTearComponents.size() * 3 + compIdx * 3,
449 double(1.0));
450 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
451 comp->node(0)->matrixNodeIndex(PhaseType::B),
452 compIdx * 3 + 1, 1);
453 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
454 mNodeSubnetMap[comp->node(0)]->mCmplOff +
455 comp->node(0)->matrixNodeIndex(PhaseType::B),
456 mTearComponents.size() * 3 + compIdx * 3 + 1,
457 double(1.0));
458 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
459 comp->node(0)->matrixNodeIndex(PhaseType::C),
460 compIdx * 3 + 2, 1);
461 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
462 mNodeSubnetMap[comp->node(0)]->mCmplOff +
463 comp->node(0)->matrixNodeIndex(PhaseType::C),
464 mTearComponents.size() * 3 + compIdx * 3 + 2,
465 double(1.0));
466 } else {
467 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
468 comp->node(0)->matrixNodeIndex(),
469 compIdx, 1);
470 triplets.emplace_back(mNodeSubnetMap[comp->node(0)]->sysOff +
471 mNodeSubnetMap[comp->node(0)]->mCmplOff +
472 comp->node(0)->matrixNodeIndex(),
473 mTearComponents.size() + compIdx, double(1.0));
474 }
475
476 if (comp->node(0)->phaseType() == CPS::PhaseType::ABC) {
477 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
478 comp->node(1)->matrixNodeIndex(PhaseType::A),
479 compIdx * 3, -1);
480 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
481 mNodeSubnetMap[comp->node(1)]->mCmplOff +
482 comp->node(1)->matrixNodeIndex(PhaseType::A),
483 mTearComponents.size() * 3 + compIdx * 3,
484 double(-1.0));
485 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
486 comp->node(1)->matrixNodeIndex(PhaseType::B),
487 compIdx * 3 + 1, -1);
488 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
489 mNodeSubnetMap[comp->node(1)]->mCmplOff +
490 comp->node(1)->matrixNodeIndex(PhaseType::B),
491 mTearComponents.size() * 3 + compIdx * 3 + 1,
492 double(-1.0));
493 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
494 comp->node(1)->matrixNodeIndex(PhaseType::C),
495 compIdx * 3 + 2, -1);
496 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
497 mNodeSubnetMap[comp->node(1)]->mCmplOff +
498 comp->node(1)->matrixNodeIndex(PhaseType::C),
499 mTearComponents.size() * 3 + compIdx * 3 + 2,
500 double(-1.0));
501 } else {
502 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
503 comp->node(1)->matrixNodeIndex(),
504 compIdx, -1);
505 triplets.emplace_back(mNodeSubnetMap[comp->node(1)]->sysOff +
506 mNodeSubnetMap[comp->node(1)]->mCmplOff +
507 comp->node(1)->matrixNodeIndex(),
508 mTearComponents.size() + compIdx, double(-1.0));
509 }
510 // Apply triplets to the sparse matrix
511 for (const auto &triplet : triplets) {
512 mTearTopology.coeffRef(triplet.row(), triplet.col()) += triplet.value();
513 }
514
515 // Call tear component stamp function
516 auto tearComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
517 tearComp->mnaTearApplyMatrixStamp(mTearImpedance);
518}
519
520template <typename VarType> Task::List DiakopticsSolver<VarType>::getTasks() {
521 Task::List l;
522
523 for (UInt net = 0; net < mSubnets.size(); ++net) {
524 for (auto node : mSubnets[net].nodes) {
525 for (auto task : node->mnaTasks())
526 l.push_back(task);
527 }
528
529 for (auto comp : mSubnets[net].components) {
530 for (auto task : comp->mnaTasks()) {
531 l.push_back(task);
532 }
533 }
534 l.push_back(std::make_shared<SubnetSolveTask>(*this, net));
535 l.push_back(std::make_shared<SolveTask>(*this, net));
536 }
537
538 for (auto comp : mSimSignalComps) {
539 for (auto task : comp->getTasks()) {
540 l.push_back(task);
541 }
542 }
543 l.push_back(std::make_shared<PreSolveTask>(*this));
544 l.push_back(std::make_shared<PostSolveTask>(*this));
545 l.push_back(std::make_shared<LogTask>(*this));
546
547 return l;
548}
549
550template <typename VarType>
552 Real time) {
553
554 mSubnet.systemMatrix.setZero();
555 for (auto comp : mSubnet.components) {
556 comp->mnaApplySystemMatrixStamp(mSubnet.systemMatrix);
557 }
558 mSubnet.directLinearSolver->partialRefactorize(mSubnet.systemMatrix,
559 mSubnet.listVariableEntries);
560
561 // A change in this subnet only affects its own tear columns: re-solve and
562 // update this subnet's block of Y^-1 C. The shared Schur complement
563 // and its factorization are rebuilt once in PreSolveTask after all
564 // subnet solves
565 const auto &cols = mSubnet.tearColumns;
566 const UInt nJ = static_cast<UInt>(cols.size());
567 if (nJ == 0)
568 return;
569
570 Matrix tearTopoBlock(mSubnet.sysSize, nJ);
571 for (UInt c = 0; c < nJ; ++c)
572 tearTopoBlock.col(c) = mSolver.mTearTopology.block(mSubnet.sysOff, cols[c],
573 mSubnet.sysSize, 1);
574
575 Matrix invTopoBlock = mSubnet.directLinearSolver->solve(tearTopoBlock);
576
577 for (UInt c = 0; c < nJ; ++c)
578 mSolver.mSystemInverseTearTopology.block(
579 mSubnet.sysOff, cols[c], mSubnet.sysSize, 1) = invTopoBlock.col(c);
580
581 mSolver.mTearSchurNeedsRebuild = true;
582}
583
584template <typename VarType>
586 bool changed = false;
587 for (auto varElem : mSubnet.mVariableComps) {
588 if (varElem->hasParameterChanged()) {
589 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(varElem);
590 // if we return true here directly, not all functions are called
591 // and some internal bools lead to further recalculation at t+1
592 changed = true;
593 }
594 }
595 return changed;
596}
597
598template <typename VarType>
600 Int timeStepCount) {
601 auto rBlock =
602 mSolver.mRightSideVector.block(mSubnet.sysOff, 0, mSubnet.sysSize, 1);
603 rBlock.setZero();
604
605 for (auto stamp : mSubnet.rightVectorStamps)
606 rBlock += *stamp;
607
608 auto lBlock = (**mSolver.mOrigLeftSideVector)
609 .block(mSubnet.sysOff, 0, mSubnet.sysSize, 1);
610
613 }
614 // Solve Y' * v' = I
615 Matrix rhs = rBlock;
616 lBlock = mSubnet.directLinearSolver->solve(rhs);
617}
618
619template <typename VarType>
621 Int timeStepCount) {
622 // Rebuild tear Schur complement and factorization once if any subnet
623 // recomputed. Runs single-threaded after SubnetSolveTasks
624 if (mSolver.mTearSchurNeedsRebuild.exchange(false)) {
625 mSolver.mTearSchur =
626 mSolver.mTearImpedance +
627 mSolver.mTearTopology.transpose() * mSolver.mSystemInverseTearTopology;
628 mSolver.mTotalTearImpedance =
629 Eigen::PartialPivLU<Matrix>(mSolver.mTearSchur);
630 }
631
632 mSolver.mTearVoltages.setZero();
633 for (auto comp : mSolver.mTearComponents) {
634 auto tComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
635 tComp->mnaTearApplyVoltageStamp(mSolver.mTearVoltages);
636 }
637 // -C^T * v'
638 mSolver.mTearVoltages -=
639 mSolver.mTearTopology.transpose() * **mSolver.mOrigLeftSideVector;
640 // Solve Z' * i = E - C^T * v'
641 mSolver.mTearCurrents =
642 mSolver.mTotalTearImpedance.solve(mSolver.mTearVoltages);
643 // C * i
644 **mSolver.mMappedTearCurrents = mSolver.mTearTopology * mSolver.mTearCurrents;
645 mSolver.mLeftSideVector = **mSolver.mOrigLeftSideVector;
646}
647
648template <typename VarType>
650 Int timeStepCount) {
651 auto lBlock =
652 mSolver.mLeftSideVector.block(mSubnet.sysOff, 0, mSubnet.sysSize, 1);
653 auto rBlock = (**mSolver.mMappedTearCurrents)
654 .block(mSubnet.sysOff, 0, mSubnet.sysSize, 1);
655 // Solve Y' * x = C * i
656 // v = v' + x
657 Matrix rhs = rBlock;
658 lBlock += mSubnet.directLinearSolver->solve(rhs);
659 **mSubnet.leftVector = lBlock;
660}
661
662template <>
664 Int timeStepCount) {
665 // pass the voltages and current of the solution to the torn components
666 mSolver.mTearVoltages =
667 -mSolver.mTearTopology.transpose() * mSolver.mLeftSideVector;
668 for (UInt compIdx = 0; compIdx < mSolver.mTearComponents.size(); ++compIdx) {
669 auto comp = mSolver.mTearComponents[compIdx];
670 auto tComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
671 if (mSolver.mPhaseType == CPS::PhaseType::ABC) {
672 Matrix voltage = Matrix::Zero(3, 1);
673 voltage << mSolver.mTearVoltages(compIdx * 3),
674 mSolver.mTearVoltages(compIdx * 3 + 1),
675 mSolver.mTearVoltages(compIdx * 3 + 2);
676
677 Matrix current = Matrix::Zero(3, 1);
678 current << mSolver.mTearCurrents(compIdx * 3),
679 mSolver.mTearCurrents(compIdx * 3 + 1),
680 mSolver.mTearCurrents(compIdx * 3 + 2);
681 tComp->mnaTearPostStep(voltage, current);
682 } else {
683 Complex voltage =
684 Math::complexFromVectorElement(mSolver.mTearVoltages, compIdx);
685 Complex current =
686 Math::complexFromVectorElement(mSolver.mTearCurrents, compIdx);
687 tComp->mnaTearPostStep(voltage, current);
688 }
689 }
690
691 // TODO split into separate task? (dependent on x, updating all v attributes)
692 for (UInt net = 0; net < mSolver.mSubnets.size(); ++net) {
693 for (UInt node = 0; node < mSolver.mSubnets[net].mRealNetNodeNum; ++node) {
694 mSolver.mSubnets[net].nodes[node]->mnaUpdateVoltage(
695 *(mSolver.mSubnets[net].leftVector));
696 }
697 }
698}
699
700template <>
702 Int timeStepCount) {
703 // pass the voltages and current of the solution to the torn components
704 mSolver.mTearVoltages =
705 -mSolver.mTearTopology.transpose() * mSolver.mLeftSideVector;
706 for (UInt compIdx = 0; compIdx < mSolver.mTearComponents.size(); ++compIdx) {
707 auto comp = mSolver.mTearComponents[compIdx];
708 auto tComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
709 if (mSolver.mPhaseType == CPS::PhaseType::ABC) {
710 MatrixComp voltage = MatrixComp::Zero(3, 1);
711 voltage << Math::complexFromVectorElement(mSolver.mTearVoltages,
712 compIdx * 3),
713 Math::complexFromVectorElement(mSolver.mTearVoltages,
714 compIdx * 3 + 1),
715 Math::complexFromVectorElement(mSolver.mTearVoltages,
716 compIdx * 3 + 2);
717 MatrixComp current = MatrixComp::Zero(3, 1);
718 current << Math::complexFromVectorElement(mSolver.mTearCurrents,
719 compIdx * 3),
720 Math::complexFromVectorElement(mSolver.mTearCurrents,
721 compIdx * 3 + 1),
722 Math::complexFromVectorElement(mSolver.mTearCurrents,
723 compIdx * 3 + 2);
724 tComp->mnaTearPostStep(voltage, current);
725 } else {
726 Complex voltage =
727 Math::complexFromVectorElement(mSolver.mTearVoltages, compIdx);
728 Complex current =
729 Math::complexFromVectorElement(mSolver.mTearCurrents, compIdx);
730 tComp->mnaTearPostStep(voltage, current);
731 }
732 }
733
734 // TODO split into separate task? (dependent on x, updating all v attributes)
735 for (UInt net = 0; net < mSolver.mSubnets.size(); ++net) {
736 for (UInt node = 0; node < mSolver.mSubnets[net].mRealNetNodeNum; ++node) {
737 mSolver.mSubnets[net].nodes[node]->mnaUpdateVoltage(
738 *(mSolver.mSubnets[net].leftVector));
739 }
740 }
741}
742
743template <> void DiakopticsSolver<Real>::log(Real time, Int timeStepCount) {
744 mLeftVectorLog->logEMTNodeValues(time, mLeftSideVector);
745 mRightVectorLog->logEMTNodeValues(time, mRightSideVector);
746}
747
748template <> void DiakopticsSolver<Complex>::log(Real time, Int timeStepCount) {
749 mLeftVectorLog->logPhasorNodeValues(time, mLeftSideVector);
750 mRightVectorLog->logPhasorNodeValues(time, mRightSideVector);
751}
752
753template <typename VarType>
755 mSolver.log(time, timeStepCount);
756}
757
758template class DiakopticsSolver<Real>;
759template class DiakopticsSolver<Complex>;
760
761} // namespace DPsim
std::vector< Ptr > List
spdlog::level::level_enum Level
Definition Logger.h:33
static Complex complexFromVectorElement(const Matrix &mat, Matrix::Index row, Int maxFreq=1, Int freqIdx=0)
Definition MathUtils.cpp:94
Real mSystemFrequency
System frequency.
TopologicalNode::List mNodes
List of network nodes.
void splitSubnets(std::vector< CPS::SystemTopology > &splitSystems)
std::vector< Ptr > List
Definition Task.h:28
void execute(Real time, Int timeStepCount)
void execute(Real time, Int timeStepCount)
void execute(Real time, Int timeStepCount)
void execute(Real time, Int timeStepCount)
Bool hasVariableComponentChanged()
Check whether status of variable MNA elements has changed.
void execute(Real time, Int timeStepCount)
CPS::Task::List getTasks() override
Get tasks for scheduler.
const CPS::Attribute< Matrix >::Ptr mOrigLeftSideVector
Solutions of the split systems.
DiakopticsSolver(String name, CPS::SystemTopology system, CPS::IdentifiedObject::List tearComponents, Real timeStep, CPS::Logger::Level logLevel)
const CPS::Attribute< Matrix >::Ptr mMappedTearCurrents
Currents through the removed network (as "seen" from the other subnets)
Real mTimeStep
Time step for fixed step solvers.
Definition Solver.h:57
Solver(String name, CPS::Logger::Level logLevel)
Definition Solver.h:83
static std::shared_ptr< AttributeList > make(Args &&...args)
Definition PtrFactory.h:19
#define PI
Definition Definitions.h:43
Eigen::SparseMatrix< Real, Eigen::RowMajor > SparseMatrixRow
Sparse matrix for real numbers (row major).
Definition Definitions.h:74
CPS::Complex Complex
Definition Definitions.h:19
CPS::Real Real
Definition Definitions.h:18
CPS::String String
Definition Definitions.h:20
CPS::Int Int
Definition Definitions.h:22
CPS::Matrix Matrix
Definition Definitions.h:24
CPS::SystemTopology SystemTopology
Definition DPsim.h:34
CPS::Bool Bool
Definition Definitions.h:21
CPS::SparseMatrixRow SparseMatrix
Definition Definitions.h:26
CPS::UInt UInt
Definition Definitions.h:23
CPS::MatrixComp MatrixComp
Definition Definitions.h:25