DPsim
Loading...
Searching...
No Matches
DiakopticsSolver.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/DiakopticsSolver.h>
10
11#include <iomanip>
12
13#include <dpsim-models/MathUtils.h>
14#include <dpsim-models/Solver/MNATearInterface.h>
15#include <dpsim/Definitions.h>
16#include <dpsim/KLUAdapter.h>
17
18using namespace CPS;
19using namespace DPsim;
20
21namespace DPsim {
22
23template <typename VarType>
24DiakopticsSolver<VarType>::DiakopticsSolver(
25 String name, SystemTopology system, IdentifiedObject::List tearComponents,
26 Real timeStep, Logger::Level logLevel)
27 : Solver(name, logLevel),
28 mMappedTearCurrents(AttributeStatic<Matrix>::make()),
29 mOrigLeftSideVector(AttributeStatic<Matrix>::make()) {
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>
551void DiakopticsSolver<VarType>::SubnetSolveTask::recomputeSubnetMatrix(
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>
599void DiakopticsSolver<VarType>::SubnetSolveTask::execute(Real time,
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
611 if (hasVariableComponentChanged()) {
612 recomputeSubnetMatrix(time);
613 }
614 // Solve Y' * v' = I
615 Matrix rhs = rBlock;
616 lBlock = mSubnet.directLinearSolver->solve(rhs);
617}
618
619template <typename VarType>
620void DiakopticsSolver<VarType>::PreSolveTask::execute(Real time,
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>
649void DiakopticsSolver<VarType>::SolveTask::execute(Real time,
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 <>
663void DiakopticsSolver<Real>::PostSolveTask::execute(Real time,
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 <>
701void DiakopticsSolver<Complex>::PostSolveTask::execute(Real time,
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>
754void DiakopticsSolver<VarType>::LogTask::execute(Real time, Int timeStepCount) {
755 mSolver.log(time, timeStepCount);
756}
757
758template class DiakopticsSolver<Real>;
759template class DiakopticsSolver<Complex>;
760
761} // namespace DPsim
Real mSystemFrequency
System frequency.
TopologicalNode::List mNodes
List of network nodes.
Bool hasVariableComponentChanged()
Check whether status of variable MNA elements has changed.
CPS::Task::List getTasks() override
Get tasks for scheduler.
Base class for more specific solvers such as MNA, ODE or IDA.
Definition Solver.h:30