DPsim
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 
17 using namespace CPS;
18 using namespace DPsim;
19 
20 namespace DPsim {
21 
22 template <typename VarType>
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 
45 template <typename VarType>
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 
59 template <typename VarType>
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 
130 template <typename VarType>
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 
153 template <typename VarType>
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 
183 template <> void DiakopticsSolver<Real>::setSubnetSize(int net, UInt nodes) {
184  mSubnets[net].sysSize = nodes;
185  mSubnets[net].mCmplOff = 0;
186 }
187 
188 template <> void DiakopticsSolver<Complex>::setSubnetSize(int net, UInt nodes) {
189  mSubnets[net].sysSize = 2 * nodes;
190  mSubnets[net].mCmplOff = nodes;
191 }
192 
193 template <> void DiakopticsSolver<Real>::setLogColumns() {
194  // nothing to do, column names generated by DataLogger are correct
195 }
196 
197 template <> 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 
215 template <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 
236 template <> 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 
244 template <> 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 
252 template <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 
276 template <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 
326 template <> 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 
339 template <>
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 
359 template <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 
389 template <typename VarType>
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 
405 template <typename VarType>
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 
424 template <typename VarType>
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 
437 template <typename VarType>
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 
462 template <> void DiakopticsSolver<Real>::log(Real time, Int timeStepCount) {
463  mLeftVectorLog->logEMTNodeValues(time, mLeftSideVector);
464  mRightVectorLog->logEMTNodeValues(time, mRightSideVector);
465 }
466 
467 template <> void DiakopticsSolver<Complex>::log(Real time, Int timeStepCount) {
468  mLeftVectorLog->logPhasorNodeValues(time, mLeftSideVector);
469  mRightVectorLog->logPhasorNodeValues(time, mRightSideVector);
470 }
471 
472 template <typename VarType>
473 void DiakopticsSolver<VarType>::LogTask::execute(Real time, Int timeStepCount) {
474  mSolver.log(time, timeStepCount);
475 }
476 
477 template class DiakopticsSolver<Real>;
478 template class DiakopticsSolver<Complex>;
479 
480 } // namespace DPsim
Real mSystemFrequency
System frequency.
Base class for more specific solvers such as MNA, ODE or IDA.
Definition: Solver.h:30