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
17using namespace CPS;
18using namespace DPsim;
19
20namespace DPsim {
21
22template <typename VarType>
23DiakopticsSolver<VarType>::DiakopticsSolver(
24 String name, SystemTopology system, IdentifiedObject::List tearComponents,
25 Real timeStep, Logger::Level logLevel)
26 : Solver(name, logLevel),
27 mMappedTearCurrents(AttributeStatic<Matrix>::make()),
28 mOrigLeftSideVector(AttributeStatic<Matrix>::make()) {
29 mTimeStep = timeStep;
30
31 // Raw source and solution vector logging
32 mLeftVectorLog = std::make_shared<DataLogger>(
33 name + "_LeftVector", logLevel != CPS::Logger::Level::off);
34 mRightVectorLog = std::make_shared<DataLogger>(
35 name + "_RightVector", logLevel != CPS::Logger::Level::off);
36
37 for (auto comp : tearComponents) {
38 auto pcomp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
39 if (pcomp)
40 mTearComponents.push_back(pcomp);
41 }
42 init(system);
43}
44
45template <typename VarType>
46void DiakopticsSolver<VarType>::init(SystemTopology &system) {
47 std::vector<SystemTopology> subnets;
48 mSystem = system;
49 mSystemFrequency = system.mSystemFrequency;
50
51 system.splitSubnets<VarType>(subnets);
52 initSubnets(subnets);
53 setLogColumns();
54 createMatrices();
55 initComponents();
56 initMatrices();
57}
58
59template <typename VarType>
60void DiakopticsSolver<VarType>::initSubnets(
61 const std::vector<SystemTopology> &subnets) {
62 mSubnets.resize(subnets.size());
63 for (UInt i = 0; i < subnets.size(); ++i) {
64 // Add nodes to the list and ignore ground nodes.
65 for (auto baseNode : subnets[i].mNodes) {
66 if (!baseNode->isGround()) {
67 auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
68 mSubnets[i].nodes.push_back(node);
69 }
70 }
71
72 for (auto comp : subnets[i].mComponents) {
73 // TODO switches
74 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
75 if (mnaComp)
76 mSubnets[i].components.push_back(mnaComp);
77
78 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
79 if (sigComp)
80 mSimSignalComps.push_back(sigComp);
81 }
82 }
83
84 // Create map that relates nodes to subnetworks
85 for (auto &net : mSubnets) {
86 for (auto &node : net.nodes) {
87 mNodeSubnetMap[node] = &net;
88 }
89 }
90
91 for (UInt idx = 0; idx < mTearComponents.size(); ++idx) {
92 auto comp = mTearComponents[idx];
93 auto tComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
94 if (!tComp)
95 throw SystemError("Unsupported component type for diakoptics");
96
97 if (comp->hasVirtualNodes()) {
98 for (UInt node = 0; node < comp->virtualNodesNumber(); ++node) {
99 // sim node number doesn't matter here because it shouldn't be used anyway
100 // TODO adapt this to new concept
101 comp->setVirtualNodeAt(std::make_shared<CPS::SimNode<VarType>>(node),
102 node);
103 }
104 }
105 tComp->mnaTearSetIdx(idx);
106 comp->initializeFromNodesAndTerminals(mSystemFrequency);
107 tComp->mnaTearInitialize(2 * PI * mSystemFrequency, mTimeStep);
108
109 for (auto gndComp : tComp->mnaTearGroundComponents()) {
110 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(gndComp);
111 Subnet *net = nullptr;
112 if (pComp->node(0)->isGround()) {
113 net = mNodeSubnetMap[pComp->node(1)];
114 } else if (pComp->node(1)->isGround()) {
115 net = mNodeSubnetMap[pComp->node(0)];
116 } else {
117 throw SystemError(
118 "Invalid ground component passed from torn component");
119 }
120 net->components.push_back(gndComp);
121 }
122 }
123
124 for (UInt i = 0; i < subnets.size(); ++i) {
125 collectVirtualNodes(i);
126 assignMatrixNodeIndices(i);
127 }
128}
129
130template <typename VarType>
131void DiakopticsSolver<VarType>::collectVirtualNodes(int net) {
132 mSubnets[net].mVirtualNodeNum = 0;
133 mSubnets[net].mRealNetNodeNum = static_cast<UInt>(mSubnets[net].nodes.size());
134
135 for (auto comp : mSubnets[net].components) {
136 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
137 if (!pComp)
138 continue;
139
140 if (pComp->hasVirtualNodes()) {
141 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
142 ++(mSubnets[net].mVirtualNodeNum);
143 mSubnets[net].nodes.push_back(pComp->virtualNode(node));
144 }
145 }
146 }
147 SPDLOG_LOGGER_INFO(mSLog, "Subnet {} has {} real network nodes.", net,
148 mSubnets[net].mRealNetNodeNum);
149 SPDLOG_LOGGER_INFO(mSLog, "Subnet {} has {} virtual nodes.", net,
150 mSubnets[net].mVirtualNodeNum);
151}
152
153template <typename VarType>
154void DiakopticsSolver<VarType>::assignMatrixNodeIndices(int net) {
155 UInt matrixNodeIndexIdx = 0;
156 for (UInt idx = 0; idx < mSubnets[net].nodes.size(); ++idx) {
157 auto &node = mSubnets[net].nodes[idx];
158
159 node->setMatrixNodeIndex(0, matrixNodeIndexIdx);
160 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to node {}",
161 matrixNodeIndexIdx, node->name());
162 ++matrixNodeIndexIdx;
163
164 if (node->phaseType() == CPS::PhaseType::ABC) {
165 node->setMatrixNodeIndex(1, matrixNodeIndexIdx);
166 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to node {} phase B",
167 matrixNodeIndexIdx, node->name());
168 ++matrixNodeIndexIdx;
169 node->setMatrixNodeIndex(2, matrixNodeIndexIdx);
170 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to node {} phase C",
171 matrixNodeIndexIdx, node->name());
172 ++matrixNodeIndexIdx;
173 }
174 }
175 setSubnetSize(net, matrixNodeIndexIdx);
176
177 if (net == 0)
178 mSubnets[net].sysOff = 0;
179 else
180 mSubnets[net].sysOff = mSubnets[net - 1].sysOff + mSubnets[net - 1].sysSize;
181}
182
183template <> void DiakopticsSolver<Real>::setSubnetSize(int net, UInt nodes) {
184 mSubnets[net].sysSize = nodes;
185 mSubnets[net].mCmplOff = 0;
186}
187
188template <> void DiakopticsSolver<Complex>::setSubnetSize(int net, UInt nodes) {
189 mSubnets[net].sysSize = 2 * nodes;
190 mSubnets[net].mCmplOff = nodes;
191}
192
193template <> void DiakopticsSolver<Real>::setLogColumns() {
194 // nothing to do, column names generated by DataLogger are correct
195}
196
197template <> void DiakopticsSolver<Complex>::setLogColumns() {
198 std::vector<String> names;
199 for (auto &subnet : mSubnets) {
200 for (UInt i = subnet.sysOff; i < subnet.sysOff + subnet.sysSize; ++i) {
201 std::stringstream name;
202 if (i < subnet.sysOff + subnet.mCmplOff)
203 name << "node" << std::setfill('0') << std::setw(5)
204 << i - subnet.sysOff / 2 << ".real";
205 else
206 name << "node" << std::setfill('0') << std::setw(5)
207 << i - (subnet.sysOff + subnet.sysSize) / 2 << ".imag";
208 names.push_back(name.str());
209 }
210 }
211 mLeftVectorLog->setColumnNames(names);
212 mRightVectorLog->setColumnNames(names);
213}
214
215template <typename VarType> void DiakopticsSolver<VarType>::createMatrices() {
216 UInt totalSize = mSubnets.back().sysOff + mSubnets.back().sysSize;
217 mSystemMatrix = Matrix::Zero(totalSize, totalSize);
218 mSystemInverse = Matrix::Zero(totalSize, totalSize);
219
220 mRightSideVector = Matrix::Zero(totalSize, 1);
221 mLeftSideVector = Matrix::Zero(totalSize, 1);
222 **mOrigLeftSideVector = Matrix::Zero(totalSize, 1);
223 **mMappedTearCurrents = Matrix::Zero(totalSize, 1);
224
225 for (auto &net : mSubnets) {
226 // The subnets' components expect to be passed a left-side vector matching
227 // the size of the subnet, so we have to create separate vectors here and
228 // copy the solution there
229 net.leftVector = AttributeStatic<Matrix>::make();
230 net.leftVector->set(Matrix::Zero(net.sysSize, 1));
231 }
232
233 createTearMatrices(totalSize);
234}
235
236template <> void DiakopticsSolver<Real>::createTearMatrices(UInt totalSize) {
237 mTearTopology = Matrix::Zero(totalSize, mTearComponents.size());
238 mTearImpedance =
239 CPS::SparseMatrixRow(mTearComponents.size(), mTearComponents.size());
240 mTearCurrents = Matrix::Zero(mTearComponents.size(), 1);
241 mTearVoltages = Matrix::Zero(mTearComponents.size(), 1);
242}
243
244template <> void DiakopticsSolver<Complex>::createTearMatrices(UInt totalSize) {
245 mTearTopology = Matrix::Zero(totalSize, 2 * mTearComponents.size());
246 mTearImpedance = CPS::SparseMatrixRow(2 * mTearComponents.size(),
247 2 * mTearComponents.size());
248 mTearCurrents = Matrix::Zero(2 * mTearComponents.size(), 1);
249 mTearVoltages = Matrix::Zero(2 * mTearComponents.size(), 1);
250}
251
252template <typename VarType> void DiakopticsSolver<VarType>::initComponents() {
253 for (UInt net = 0; net < mSubnets.size(); ++net) {
254 for (auto comp : mSubnets[net].components) {
255 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
256 if (!pComp)
257 continue;
258 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
259 }
260
261 // Initialize MNA specific parts of components.
262 for (auto comp : mSubnets[net].components) {
263 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep,
264 mSubnets[net].leftVector);
265 const Matrix &stamp = comp->getRightVector()->get();
266 if (stamp.size() != 0) {
267 mSubnets[net].rightVectorStamps.push_back(&stamp);
268 }
269 }
270 }
271 // Initialize signal components.
272 for (auto comp : mSimSignalComps)
273 comp->initialize(mSystem.mSystemOmega, mTimeStep);
274}
275
276template <typename VarType> void DiakopticsSolver<VarType>::initMatrices() {
277 for (auto &net : mSubnets) {
278 // We can't directly pass the block reference to mnaApplySystemMatrixStamp,
279 // because it expects a concrete Matrix. It can't be changed to accept some common
280 // base class like DenseBase because that would make it a template function (as
281 // Eigen uses CRTP for polymorphism), which is impossible for virtual functions.
282 CPS::SparseMatrixRow partSys =
283 CPS::SparseMatrixRow(net.sysSize, net.sysSize);
284 for (auto comp : net.components) {
285 comp->mnaApplySystemMatrixStamp(partSys);
286 }
287 auto block =
288 mSystemMatrix.block(net.sysOff, net.sysOff, net.sysSize, net.sysSize);
289 block = partSys;
290 SPDLOG_LOGGER_INFO(mSLog, "Block: \n{}", block);
291 net.luFactorization = Eigen::PartialPivLU<Matrix>(partSys);
292 SPDLOG_LOGGER_INFO(mSLog, "Factorization: \n{}",
293 net.luFactorization.matrixLU());
294 }
295 SPDLOG_LOGGER_INFO(mSLog, "Complete system matrix: \n{}", mSystemMatrix);
296
297 // initialize tear topology matrix and impedance matrix of removed network
298 for (UInt compIdx = 0; compIdx < mTearComponents.size(); ++compIdx) {
299 applyTearComponentStamp(compIdx);
300 }
301 SPDLOG_LOGGER_INFO(mSLog, "Topology matrix: \n{}", mTearTopology);
302 SPDLOG_LOGGER_INFO(mSLog, "Removed impedance matrix: \n{}", mTearImpedance);
303 // TODO this can be sped up as well by using the block diagonal form of Yinv
304 for (auto &net : mSubnets) {
305 mSystemInverse.block(net.sysOff, net.sysOff, net.sysSize, net.sysSize) =
306 net.luFactorization.inverse();
307 }
308 mTotalTearImpedance = Eigen::PartialPivLU<Matrix>(
309 mTearImpedance +
310 mTearTopology.transpose() * mSystemInverse * mTearTopology);
311 SPDLOG_LOGGER_INFO(mSLog,
312 "Total removed impedance matrix LU decomposition: \n{}",
313 mTotalTearImpedance.matrixLU());
314
315 // Compute subnet right side (source) vectors for debugging
316 for (auto &net : mSubnets) {
317 Matrix rInit = Matrix::Zero(net.sysSize, 1);
318
319 for (auto comp : net.components) {
320 comp->mnaApplyRightSideVectorStamp(rInit);
321 }
322 SPDLOG_LOGGER_INFO(mSLog, "Source block: \n{}", rInit);
323 }
324}
325
326template <> void DiakopticsSolver<Real>::applyTearComponentStamp(UInt compIdx) {
327 auto comp = mTearComponents[compIdx];
328 mTearTopology(mNodeSubnetMap[comp->node(0)]->sysOff +
329 comp->node(0)->matrixNodeIndex(),
330 compIdx) = 1;
331 mTearTopology(mNodeSubnetMap[comp->node(1)]->sysOff +
332 comp->node(1)->matrixNodeIndex(),
333 compIdx) = -1;
334
335 auto tearComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
336 tearComp->mnaTearApplyMatrixStamp(mTearImpedance);
337}
338
339template <>
340void DiakopticsSolver<Complex>::applyTearComponentStamp(UInt compIdx) {
341 auto comp = mTearComponents[compIdx];
342
343 auto net1 = mNodeSubnetMap[comp->node(0)];
344 auto net2 = mNodeSubnetMap[comp->node(1)];
345
346 mTearTopology(net1->sysOff + comp->node(0)->matrixNodeIndex(), compIdx) = 1;
347 mTearTopology(net1->sysOff + net1->mCmplOff +
348 comp->node(0)->matrixNodeIndex(),
349 mTearComponents.size() + compIdx) = 1;
350 mTearTopology(net2->sysOff + comp->node(1)->matrixNodeIndex(), compIdx) = -1;
351 mTearTopology(net2->sysOff + net2->mCmplOff +
352 comp->node(1)->matrixNodeIndex(),
353 mTearComponents.size() + compIdx) = -1;
354
355 auto tearComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
356 tearComp->mnaTearApplyMatrixStamp(mTearImpedance);
357}
358
359template <typename VarType> Task::List DiakopticsSolver<VarType>::getTasks() {
360 Task::List l;
361
362 for (UInt net = 0; net < mSubnets.size(); ++net) {
363 for (auto node : mSubnets[net].nodes) {
364 for (auto task : node->mnaTasks())
365 l.push_back(task);
366 }
367
368 for (auto comp : mSubnets[net].components) {
369 for (auto task : comp->mnaTasks()) {
370 l.push_back(task);
371 }
372 }
373 l.push_back(std::make_shared<SubnetSolveTask>(*this, net));
374 l.push_back(std::make_shared<SolveTask>(*this, net));
375 }
376
377 for (auto comp : mSimSignalComps) {
378 for (auto task : comp->getTasks()) {
379 l.push_back(task);
380 }
381 }
382 l.push_back(std::make_shared<PreSolveTask>(*this));
383 l.push_back(std::make_shared<PostSolveTask>(*this));
384 l.push_back(std::make_shared<LogTask>(*this));
385
386 return l;
387}
388
389template <typename VarType>
390void DiakopticsSolver<VarType>::SubnetSolveTask::execute(Real time,
391 Int timeStepCount) {
392 auto rBlock =
393 mSolver.mRightSideVector.block(mSubnet.sysOff, 0, mSubnet.sysSize, 1);
394 rBlock.setZero();
395
396 for (auto stamp : mSubnet.rightVectorStamps)
397 rBlock += *stamp;
398
399 auto lBlock = (**mSolver.mOrigLeftSideVector)
400 .block(mSubnet.sysOff, 0, mSubnet.sysSize, 1);
401 // Solve Y' * v' = I
402 lBlock = mSubnet.luFactorization.solve(rBlock);
403}
404
405template <typename VarType>
406void DiakopticsSolver<VarType>::PreSolveTask::execute(Real time,
407 Int timeStepCount) {
408 mSolver.mTearVoltages.setZero();
409 for (auto comp : mSolver.mTearComponents) {
410 auto tComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
411 tComp->mnaTearApplyVoltageStamp(mSolver.mTearVoltages);
412 }
413 // -C^T * v'
414 mSolver.mTearVoltages -=
415 mSolver.mTearTopology.transpose() * **mSolver.mOrigLeftSideVector;
416 // Solve Z' * i = E - C^T * v'
417 mSolver.mTearCurrents =
418 mSolver.mTotalTearImpedance.solve(mSolver.mTearVoltages);
419 // C * i
420 **mSolver.mMappedTearCurrents = mSolver.mTearTopology * mSolver.mTearCurrents;
421 mSolver.mLeftSideVector = **mSolver.mOrigLeftSideVector;
422}
423
424template <typename VarType>
425void DiakopticsSolver<VarType>::SolveTask::execute(Real time,
426 Int timeStepCount) {
427 auto lBlock =
428 mSolver.mLeftSideVector.block(mSubnet.sysOff, 0, mSubnet.sysSize, 1);
429 auto rBlock = (**mSolver.mMappedTearCurrents)
430 .block(mSubnet.sysOff, 0, mSubnet.sysSize, 1);
431 // Solve Y' * x = C * i
432 // v = v' + x
433 lBlock += mSubnet.luFactorization.solve(rBlock);
434 **mSubnet.leftVector = lBlock;
435}
436
437template <typename VarType>
438void DiakopticsSolver<VarType>::PostSolveTask::execute(Real time,
439 Int timeStepCount) {
440 // pass the voltages and current of the solution to the torn components
441 mSolver.mTearVoltages =
442 -mSolver.mTearTopology.transpose() * mSolver.mLeftSideVector;
443 for (UInt compIdx = 0; compIdx < mSolver.mTearComponents.size(); ++compIdx) {
444 auto comp = mSolver.mTearComponents[compIdx];
445 auto tComp = std::dynamic_pointer_cast<MNATearInterface>(comp);
446 Complex voltage =
447 Math::complexFromVectorElement(mSolver.mTearVoltages, compIdx);
448 Complex current =
449 Math::complexFromVectorElement(mSolver.mTearCurrents, compIdx);
450 tComp->mnaTearPostStep(voltage, current);
451 }
452
453 // TODO split into separate task? (dependent on x, updating all v attributes)
454 for (UInt net = 0; net < mSolver.mSubnets.size(); ++net) {
455 for (UInt node = 0; node < mSolver.mSubnets[net].mRealNetNodeNum; ++node) {
456 mSolver.mSubnets[net].nodes[node]->mnaUpdateVoltage(
457 *(mSolver.mSubnets[net].leftVector));
458 }
459 }
460}
461
462template <> void DiakopticsSolver<Real>::log(Real time, Int timeStepCount) {
463 mLeftVectorLog->logEMTNodeValues(time, mLeftSideVector);
464 mRightVectorLog->logEMTNodeValues(time, mRightSideVector);
465}
466
467template <> void DiakopticsSolver<Complex>::log(Real time, Int timeStepCount) {
468 mLeftVectorLog->logPhasorNodeValues(time, mLeftSideVector);
469 mRightVectorLog->logPhasorNodeValues(time, mRightSideVector);
470}
471
472template <typename VarType>
473void DiakopticsSolver<VarType>::LogTask::execute(Real time, Int timeStepCount) {
474 mSolver.log(time, timeStepCount);
475}
476
477template class DiakopticsSolver<Real>;
478template class DiakopticsSolver<Complex>;
479
480} // namespace DPsim
Real mSystemFrequency
System frequency.
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