DPsim
Reader.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 <CIMExceptions.hpp>
10 #include <CIMModel.hpp>
11 #include <IEC61970.hpp>
12 #include <memory>
13 
14 #define READER_CPP
15 #include <dpsim-models/CIM/Reader.h>
16 
17 using namespace CPS;
18 using namespace CPS::CIM;
19 using CIMPP::UnitMultiplier;
20 
21 Reader::Reader(String name, Logger::Level logLevel,
22  Logger::Level componentLogLevel) {
23  mSLog = Logger::get(name + "_CIM", logLevel);
24 
25  mModel = new CIMModel();
26  mModel->setDependencyCheckOff();
27  mComponentLogLevel = componentLogLevel;
28 }
29 
30 Reader::~Reader() { delete mModel; }
31 
32 SystemTopology Reader::loadCIM(Real systemFrequency,
33  const std::list<CPS::String> &filenamesString,
34  Domain domain, PhaseType phase,
35  GeneratorType genType) {
36  std::list<fs::path> filenames;
37  for (auto f : filenamesString)
38  filenames.emplace_back(f);
39 
40  return loadCIM(systemFrequency, filenames, domain, phase, genType);
41 }
42 
43 // #### shunt component settings ####
46  mShuntCapacitorValue = v;
47  mSetShuntCapacitor = true;
48 }
51  mShuntConductanceValue = v;
52  mSetShuntConductance = true;
53 }
54 
57  mUseProtectionSwitches = value;
58 }
59 
60 Real Reader::unitValue(Real value, CIMPP::UnitMultiplier mult) {
61  switch (mult) {
62  case UnitMultiplier::p:
63  value *= 1e-12;
64  break;
65  case UnitMultiplier::n:
66  value *= 1e-9;
67  break;
68  case UnitMultiplier::micro:
69  value *= 1e-6;
70  break;
71  case UnitMultiplier::m:
72  value *= 1e-3;
73  break;
74  case UnitMultiplier::c:
75  value *= 1e-2;
76  break;
77  case UnitMultiplier::d:
78  value *= 1e-1;
79  break;
80  case UnitMultiplier::k:
81  value *= 1e3;
82  break;
83  case UnitMultiplier::M:
84  value *= 1e6;
85  break;
86  case UnitMultiplier::G:
87  value *= 1e9;
88  break;
89  case UnitMultiplier::T:
90  value *= 1e12;
91  break;
92  default:
93  break;
94  }
95  return value;
96 }
97 
98 TopologicalPowerComp::Ptr Reader::mapComponent(BaseClass *obj) {
99  if (CIMPP::ACLineSegment *line = dynamic_cast<CIMPP::ACLineSegment *>(obj))
100  return mapACLineSegment(line);
101  if (CIMPP::EnergyConsumer *consumer =
102  dynamic_cast<CIMPP::EnergyConsumer *>(obj))
103  return mapEnergyConsumer(consumer);
104  if (CIMPP::PowerTransformer *trans =
105  dynamic_cast<CIMPP::PowerTransformer *>(obj))
106  return mapPowerTransformer(trans);
107  if (CIMPP::SynchronousMachine *syncMachine =
108  dynamic_cast<CIMPP::SynchronousMachine *>(obj))
109  return mapSynchronousMachine(syncMachine);
110  if (CIMPP::ExternalNetworkInjection *extnet =
111  dynamic_cast<CIMPP::ExternalNetworkInjection *>(obj))
112  return mapExternalNetworkInjection(extnet);
113  if (CIMPP::EquivalentShunt *shunt =
114  dynamic_cast<CIMPP::EquivalentShunt *>(obj))
115  return mapEquivalentShunt(shunt);
116 
117  return nullptr;
118 }
119 
120 void Reader::addFiles(const fs::path &filename) {
121  if (!mModel->addCIMFile(filename.string()))
122  SPDLOG_LOGGER_ERROR(mSLog, "Failed to read file {}", filename);
123 }
124 
125 void Reader::addFiles(const std::list<fs::path> &filenames) {
126  for (auto filename : filenames)
127  addFiles(filename);
128 }
129 
130 void Reader::parseFiles() {
131  try {
132  mModel->parseFiles();
133  } catch (...) {
134  SPDLOG_LOGGER_ERROR(mSLog, "Failed to parse CIM files");
135  return;
136  }
137 
138  SPDLOG_LOGGER_INFO(
139  mSLog,
140  "#### List of TopologicalNodes, associated Terminals and Equipment");
141  for (auto obj : mModel->Objects) {
142  if (CIMPP::TopologicalNode *topNode =
143  dynamic_cast<CIMPP::TopologicalNode *>(obj)) {
144  if (mDomain == Domain::EMT)
145  processTopologicalNode<Real>(topNode);
146  else
147  processTopologicalNode<Complex>(topNode);
148  }
149  }
150 
151  // Collect voltage state variables associated to nodes that are used
152  // for various components.
153  SPDLOG_LOGGER_INFO(mSLog,
154  "#### List of Node voltages and Terminal power flow data");
155  for (auto obj : mModel->Objects) {
156  // Check if object is of class SvVoltage
157  if (CIMPP::SvVoltage *volt = dynamic_cast<CIMPP::SvVoltage *>(obj)) {
158  processSvVoltage(volt);
159  }
160  // Check if object is of class SvPowerFlow
161  else if (CIMPP::SvPowerFlow *flow =
162  dynamic_cast<CIMPP::SvPowerFlow *>(obj)) {
163  processSvPowerFlow(flow);
164  }
165  }
166 
167  SPDLOG_LOGGER_INFO(mSLog, "#### Create other components");
168  for (auto obj : mModel->Objects) {
169 
170  // Check if object is not TopologicalNode, SvVoltage or SvPowerFlow
171  if (!dynamic_cast<CIMPP::TopologicalNode *>(obj) &&
172  !dynamic_cast<CIMPP::SvVoltage *>(obj) &&
173  !dynamic_cast<CIMPP::SvPowerFlow *>(obj)) {
174 
175  if (CIMPP::IdentifiedObject *idObj =
176  dynamic_cast<CIMPP::IdentifiedObject *>(obj)) {
177 
178  // Check if object is already in equipment list
179  if (mPowerflowEquipment.find(idObj->mRID) ==
180  mPowerflowEquipment.end()) {
181  TopologicalPowerComp::Ptr comp = mapComponent(obj);
182  if (comp)
183  mPowerflowEquipment.insert(std::make_pair(comp->uid(), comp));
184  }
185  }
186  }
187  }
188 
189  SPDLOG_LOGGER_INFO(mSLog, "#### Check topology for unconnected components");
190  for (auto pfe : mPowerflowEquipment) {
191  auto c = pfe.second;
192 
193  if (mDomain == Domain::EMT) {
194  if (SimPowerComp<Real>::Ptr powercomp =
195  std::dynamic_pointer_cast<SimPowerComp<Real>>(c)) {
196  if (powercomp->terminalNumberConnected() < powercomp->terminalNumber())
197  throw InvalidTopology();
198  }
199  } else {
200  if (SimPowerComp<Complex>::Ptr powercomp =
201  std::dynamic_pointer_cast<SimPowerComp<Complex>>(c)) {
202  if (powercomp->terminalNumberConnected() < powercomp->terminalNumber())
203  throw InvalidTopology();
204  }
205  }
206  }
207 }
208 
209 SystemTopology Reader::loadCIM(Real systemFrequency, const fs::path &filename,
210  Domain domain, PhaseType phase,
211  GeneratorType genType) {
212  mFrequency = systemFrequency;
213  mOmega = 2 * PI * mFrequency;
214  mDomain = domain;
215  mPhase = phase;
216  mGeneratorType = genType;
217  addFiles(filename);
218  parseFiles();
219  return systemTopology();
220 }
221 
222 SystemTopology Reader::loadCIM(Real systemFrequency,
223  const std::list<fs::path> &filenames,
224  Domain domain, PhaseType phase,
225  GeneratorType genType) {
226  mFrequency = systemFrequency;
227  mOmega = 2 * PI * mFrequency;
228  mDomain = domain;
229  mPhase = phase;
230  mGeneratorType = genType;
231  addFiles(filenames);
232  parseFiles();
233  return systemTopology();
234 }
235 
236 void Reader::processSvVoltage(CIMPP::SvVoltage *volt) {
237  CIMPP::TopologicalNode *node = volt->TopologicalNode;
238  if (!node) {
239  SPDLOG_LOGGER_WARN(
240  mSLog, "SvVoltage references missing Topological Node, ignoring");
241  return;
242  }
243  auto search = mPowerflowNodes.find(node->mRID);
244  if (search == mPowerflowNodes.end()) {
245  SPDLOG_LOGGER_WARN(mSLog,
246  "SvVoltage references Topological Node {}"
247  " missing from mTopNodes, ignoring",
248  node->mRID);
249  return;
250  }
251 
252  Real voltageAbs = Reader::unitValue(volt->v.value, UnitMultiplier::k);
253 
254  try {
255  SPDLOG_LOGGER_INFO(mSLog, " Angle={}", (float)volt->angle.value);
256  } catch (ReadingUninitializedField *e) {
257  volt->angle.value = 0;
258  std::cerr << "Uninitialized Angle for SVVoltage at "
259  << volt->TopologicalNode->name << ".Setting default value of "
260  << volt->angle.value << std::endl;
261  }
262  Real voltagePhase = volt->angle.value * PI / 180;
263  mPowerflowNodes[node->mRID]->setInitialVoltage(
264  std::polar<Real>(voltageAbs, voltagePhase));
265 
266  SPDLOG_LOGGER_INFO(
267  mSLog, "Node {} MatrixNodeIndex {}: {} V, {} deg",
268  mPowerflowNodes[node->mRID]->uid(),
269  mPowerflowNodes[node->mRID]->matrixNodeIndex(),
270  std::abs(mPowerflowNodes[node->mRID]->initialSingleVoltage()),
271  std::arg(mPowerflowNodes[node->mRID]->initialSingleVoltage()) * 180 / PI);
272 }
273 
274 void Reader::processSvPowerFlow(CIMPP::SvPowerFlow *flow) {
275  CIMPP::Terminal *term = flow->Terminal;
276 
277  mPowerflowTerminals[term->mRID]->setPower(
278  Complex(Reader::unitValue(flow->p.value, UnitMultiplier::M),
279  Reader::unitValue(flow->q.value, UnitMultiplier::M)));
280 
281  SPDLOG_LOGGER_WARN(mSLog, "Terminal {}: {} W + j {} Var", term->mRID,
282  mPowerflowTerminals[term->mRID]->singleActivePower(),
283  mPowerflowTerminals[term->mRID]->singleReactivePower());
284 }
285 
286 SystemTopology Reader::systemTopology() {
287  SystemTopology system(mFrequency);
288 
289  for (auto comp : mPowerflowEquipment) {
290  system.addComponent(comp.second);
291  // TODO support Real
292  if (SimPowerComp<Complex>::Ptr powercomp =
293  std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp.second)) {
294  for (auto term : powercomp->topologicalTerminals()) {
295  TopologicalNode::Ptr node = term->topologicalNodes();
296  //TopologicalNode::Ptr node = powercomp->topologicalTerminals().back()->topologicalNodes();
297  if (system.mComponentsAtNode.find(node) ==
298  system.mComponentsAtNode.end()) {
299  TopologicalPowerComp::List complist;
300  complist.push_back(powercomp);
301  system.mComponentsAtNode.insert(std::make_pair(node, complist));
302  } else {
303  system.mComponentsAtNode[node].push_back(powercomp);
304  }
305  }
306  }
307  }
308 
309  system.mNodes.resize(mPowerflowNodes.size());
310 
311  for (auto node : mPowerflowNodes) {
312  // The index of the node in the list should not matter anymore
313  //system.mNodes[node.second->matrixNodeIndex()] = node.second;
314  system.addNodeAt(node.second, node.second->matrixNodeIndex());
315  }
316 
317  return system;
318 }
319 
320 Matrix::Index Reader::mapTopologicalNode(String mrid) {
321  auto search = mPowerflowNodes.find(mrid);
322  if (search == mPowerflowNodes.end()) {
323  return -1;
324  }
325  return search->second->matrixNodeIndex();
326 }
327 
328 TopologicalPowerComp::Ptr
329 Reader::mapEnergyConsumer(CIMPP::EnergyConsumer *consumer) {
330  SPDLOG_LOGGER_INFO(mSLog, " Found EnergyConsumer {}", consumer->name);
331  if (mDomain == Domain::EMT) {
332  if (mPhase == PhaseType::ABC) {
333  return std::make_shared<EMT::Ph3::RXLoad>(consumer->mRID, consumer->name,
334  mComponentLogLevel);
335  } else {
336  SPDLOG_LOGGER_INFO(mSLog, " RXLoad for EMT not implemented yet");
337  return std::make_shared<DP::Ph1::RXLoad>(consumer->mRID, consumer->name,
338  mComponentLogLevel);
339  }
340  } else if (mDomain == Domain::SP) {
341  auto load = std::make_shared<SP::Ph1::Load>(consumer->mRID, consumer->name,
342  mComponentLogLevel);
343 
344  // TODO: Use EnergyConsumer.P and EnergyConsumer.Q if available, overwrite if existent SvPowerFlow data
345  /*
346  Real p = 0;
347  Real q = 0;
348  if (consumer->p.value){
349  p = unitValue(consumer->p.value,UnitMultiplier::M);
350  }
351  if (consumer->q.value){
352  q = unitValue(consumer->q.value,UnitMultiplier::M);
353  }
354  load->setParameters(p, q, 0);
355  */
356 
357  // P and Q values will be set according to SvPowerFlow data
358  load->modifyPowerFlowBusType(
359  PowerflowBusType::
360  PQ); // for powerflow solver set as PQ component as default
361  return load;
362  } else {
363  if (mUseProtectionSwitches)
364  return std::make_shared<DP::Ph1::RXLoadSwitch>(
365  consumer->mRID, consumer->name, mComponentLogLevel);
366  else
367  return std::make_shared<DP::Ph1::RXLoad>(consumer->mRID, consumer->name,
368  mComponentLogLevel);
369  }
370 }
371 
372 TopologicalPowerComp::Ptr Reader::mapACLineSegment(CIMPP::ACLineSegment *line) {
373  SPDLOG_LOGGER_INFO(mSLog,
374  " Found ACLineSegment {} r={} x={} bch={} gch={}",
375  line->name, (float)line->r.value, (float)line->x.value,
376  (float)line->bch.value, (float)line->gch.value);
377 
378  Real resistance = line->r.value;
379  Real inductance = line->x.value / mOmega;
380 
381  // By default there is always a small conductance to ground to
382  // avoid problems with floating nodes.
383  Real capacitance = mShuntCapacitorValue;
384  Real conductance = mShuntConductanceValue;
385 
386  if (line->bch.value > 1e-9 && !mSetShuntCapacitor)
387  capacitance = Real(line->bch.value / mOmega);
388 
389  if (line->gch.value > 1e-9 && !mSetShuntConductance)
390  conductance = Real(line->gch.value);
391 
392  Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(line);
393 
394  if (mDomain == Domain::EMT) {
395  if (mPhase == PhaseType::ABC) {
396  Matrix res_3ph = CPS::Math::singlePhaseParameterToThreePhase(resistance);
397  Matrix ind_3ph = CPS::Math::singlePhaseParameterToThreePhase(inductance);
398  Matrix cap_3ph = CPS::Math::singlePhaseParameterToThreePhase(capacitance);
399  Matrix cond_3ph =
401 
402  auto cpsLine = std::make_shared<EMT::Ph3::PiLine>(line->mRID, line->name,
403  mComponentLogLevel);
404  cpsLine->setParameters(res_3ph, ind_3ph, cap_3ph, cond_3ph);
405  return cpsLine;
406  } else {
407  SPDLOG_LOGGER_INFO(mSLog, " PiLine for EMT not implemented yet");
408  auto cpsLine = std::make_shared<DP::Ph1::PiLine>(line->mRID, line->name,
409  mComponentLogLevel);
410  cpsLine->setParameters(resistance, inductance, capacitance, conductance);
411  return cpsLine;
412  }
413  } else if (mDomain == Domain::SP) {
414  auto cpsLine = std::make_shared<SP::Ph1::PiLine>(line->mRID, line->name,
415  mComponentLogLevel);
416  cpsLine->setParameters(resistance, inductance, capacitance, conductance);
417  cpsLine->setBaseVoltage(baseVoltage);
418  return cpsLine;
419  } else {
420  auto cpsLine = std::make_shared<DP::Ph1::PiLine>(line->mRID, line->name,
421  mComponentLogLevel);
422  cpsLine->setParameters(resistance, inductance, capacitance, conductance);
423  return cpsLine;
424  }
425 }
426 
427 TopologicalPowerComp::Ptr
428 Reader::mapPowerTransformer(CIMPP::PowerTransformer *trans) {
429  if (trans->PowerTransformerEnd.size() != 2) {
430  SPDLOG_LOGGER_WARN(
431  mSLog,
432  "PowerTransformer {} does not have exactly two windings, ignoring",
433  trans->name);
434  return nullptr;
435  }
436  SPDLOG_LOGGER_INFO(mSLog, "Found PowerTransformer {}", trans->name);
437 
438  // assign transformer ends
439  CIMPP::PowerTransformerEnd *end1 = nullptr, *end2 = nullptr;
440  for (auto end : trans->PowerTransformerEnd) {
441  if (end->Terminal->sequenceNumber == 1)
442  end1 = end;
443  else if (end->Terminal->sequenceNumber == 2)
444  end2 = end;
445  else
446  return nullptr;
447  }
448 
449  // setting default values for non-set resistances and reactances
450  SPDLOG_LOGGER_INFO(mSLog, " PowerTransformerEnd_1 {}", end1->name);
451  SPDLOG_LOGGER_INFO(mSLog, " Srated={} Vrated={}",
452  (float)end1->ratedS.value, (float)end1->ratedU.value);
453  try {
454  SPDLOG_LOGGER_INFO(mSLog, " R={}", (float)end1->r.value);
455  } catch (ReadingUninitializedField *e1) {
456  end1->r.value = 1e-12;
457  SPDLOG_LOGGER_WARN(mSLog,
458  " Uninitialized value for PowerTrafoEnd1 setting "
459  "default value of R={}",
460  (float)end1->r.value);
461  }
462  try {
463  SPDLOG_LOGGER_INFO(mSLog, " X={}", (float)end1->x.value);
464  } catch (ReadingUninitializedField *e1) {
465  end1->x.value = 1e-12;
466  SPDLOG_LOGGER_WARN(mSLog,
467  " Uninitialized value for PowerTrafoEnd1 setting "
468  "default value of X={}",
469  (float)end1->x.value);
470  }
471  SPDLOG_LOGGER_INFO(mSLog, " PowerTransformerEnd_2 {}", end2->name);
472  SPDLOG_LOGGER_INFO(mSLog, " Srated={} Vrated={}",
473  (float)end2->ratedS.value, (float)end2->ratedU.value);
474  try {
475  SPDLOG_LOGGER_INFO(mSLog, " R={}", (float)end2->r.value);
476  } catch (ReadingUninitializedField *e1) {
477  end2->r.value = 1e-12;
478  SPDLOG_LOGGER_WARN(mSLog,
479  " Uninitialized value for PowerTrafoEnd2 setting "
480  "default value of R={}",
481  (float)end2->r.value);
482  }
483  try {
484  SPDLOG_LOGGER_INFO(mSLog, " X={}", (float)end2->x.value);
485  } catch (ReadingUninitializedField *e1) {
486  end2->x.value = 1e-12;
487  SPDLOG_LOGGER_WARN(mSLog,
488  " Uninitialized value for PowerTrafoEnd2 setting "
489  "default value of X={}",
490  (float)end2->x.value);
491  }
492 
493  if (end1->ratedS.value != end2->ratedS.value) {
494  SPDLOG_LOGGER_WARN(
495  mSLog,
496  " PowerTransformerEnds of {} come with distinct rated power values. "
497  "Using rated power of PowerTransformerEnd_1.",
498  trans->name);
499  }
500  Real ratedPower = unitValue(end1->ratedS.value, UnitMultiplier::M);
501  Real voltageNode1 = unitValue(end1->ratedU.value, UnitMultiplier::k);
502  Real voltageNode2 = unitValue(end2->ratedU.value, UnitMultiplier::k);
503 
504  Real ratioAbsNominal = voltageNode1 / voltageNode2;
505  Real ratioAbs = ratioAbsNominal;
506 
507  // use normalStep from RatioTapChanger
508  if (end1->RatioTapChanger) {
509  ratioAbs =
510  voltageNode1 / voltageNode2 *
511  (1 + (end1->RatioTapChanger->normalStep -
512  end1->RatioTapChanger->neutralStep) *
513  end1->RatioTapChanger->stepVoltageIncrement.value / 100);
514  }
515 
516  // if corresponding SvTapStep available, use instead tap position from there
517  if (end1->RatioTapChanger) {
518  for (auto obj : mModel->Objects) {
519  auto tapStep = dynamic_cast<CIMPP::SvTapStep *>(obj);
520  if (tapStep && tapStep->TapChanger == end1->RatioTapChanger) {
521  ratioAbs =
522  voltageNode1 / voltageNode2 *
523  (1 + (tapStep->position - end1->RatioTapChanger->neutralStep) *
524  end1->RatioTapChanger->stepVoltageIncrement.value / 100);
525  }
526  }
527  }
528 
529  // TODO: To be extracted from cim class
530  Real ratioPhase = 0;
531 
532  // Calculate resistance and inductance referred to higher voltage side
533  Real resistance = 0;
534  Real inductance = 0;
535  if (voltageNode1 >= voltageNode2 && abs(end1->x.value) > 1e-12) {
536  inductance = end1->x.value / mOmega;
537  resistance = end1->r.value;
538  } else if (voltageNode1 >= voltageNode2 && abs(end2->x.value) > 1e-12) {
539  inductance = end2->x.value / mOmega * std::pow(ratioAbsNominal, 2);
540  resistance = end2->r.value * std::pow(ratioAbsNominal, 2);
541  } else if (voltageNode2 > voltageNode1 && abs(end2->x.value) > 1e-12) {
542  inductance = end2->x.value / mOmega;
543  resistance = end2->r.value;
544  } else if (voltageNode2 > voltageNode1 && abs(end1->x.value) > 1e-12) {
545  inductance = end1->x.value / mOmega / std::pow(ratioAbsNominal, 2);
546  resistance = end1->r.value / std::pow(ratioAbsNominal, 2);
547  }
548 
549  if (mDomain == Domain::EMT) {
550  if (mPhase == PhaseType::ABC) {
551  Matrix resistance_3ph =
553  Matrix inductance_3ph =
555  Bool withResistiveLosses = resistance > 0;
556  auto transformer = std::make_shared<EMT::Ph3::Transformer>(
557  trans->mRID, trans->name, mComponentLogLevel, withResistiveLosses);
558  transformer->setParameters(voltageNode1, voltageNode2, ratedPower,
559  ratioAbs, ratioPhase, resistance_3ph,
560  inductance_3ph);
561  return transformer;
562  } else {
563  SPDLOG_LOGGER_INFO(mSLog, " Transformer for EMT not implemented yet");
564  return nullptr;
565  }
566  } else if (mDomain == Domain::SP) {
567  auto transformer = std::make_shared<SP::Ph1::Transformer>(
568  trans->mRID, trans->name, mComponentLogLevel);
569  transformer->setParameters(voltageNode1, voltageNode2, ratedPower, ratioAbs,
570  ratioPhase, resistance, inductance);
571  Real baseVolt = voltageNode1 >= voltageNode2 ? voltageNode1 : voltageNode2;
572  transformer->setBaseVoltage(baseVolt);
573  return transformer;
574  } else {
575  Bool withResistiveLosses = resistance > 0;
576  auto transformer = std::make_shared<DP::Ph1::Transformer>(
577  trans->mRID, trans->name, mComponentLogLevel, withResistiveLosses);
578  transformer->setParameters(voltageNode1, voltageNode2, ratedPower, ratioAbs,
579  ratioPhase, resistance, inductance);
580  return transformer;
581  }
582 }
583 
584 TopologicalPowerComp::Ptr
585 Reader::mapSynchronousMachine(CIMPP::SynchronousMachine *machine) {
586  SPDLOG_LOGGER_INFO(mSLog, " Found Synchronous machine {}", machine->name);
587 
588  if (mDomain == Domain::DP) {
589  SPDLOG_LOGGER_INFO(mSLog, " Create generator in DP domain.");
590  if (mGeneratorType == GeneratorType::TransientStability ||
591  mGeneratorType == GeneratorType::SG6aOrderVBR ||
592  mGeneratorType == GeneratorType::SG6bOrderVBR ||
593  mGeneratorType == GeneratorType::SG4OrderVBR ||
594  mGeneratorType == GeneratorType::SG3OrderVBR ||
595  mGeneratorType == GeneratorType::SG4OrderPCM ||
596  mGeneratorType == GeneratorType::SG4OrderTPM ||
597  mGeneratorType == GeneratorType::SG6OrderPCM) {
598 
599  Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
600  Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
601 
602  for (auto obj : mModel->Objects) {
603  // Check if object is not TopologicalNode, SvVoltage or SvPowerFlow
604  if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
605  dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *>(
606  obj)) {
607  if (genDyn->SynchronousMachine->mRID == machine->mRID) {
608  // stator
609  Real Rs = genDyn->statorResistance.value;
610  Real Ll = genDyn->statorLeakageReactance.value;
611 
612  // reactances
613  Real Ld = genDyn->xDirectSync.value;
614  Real Lq = genDyn->xQuadSync.value;
615  Real Ld_t = genDyn->xDirectTrans.value;
616  Real Lq_t = genDyn->xQuadTrans.value;
617  Real Ld_s = genDyn->xDirectSubtrans.value;
618  Real Lq_s = genDyn->xQuadSubtrans.value;
619 
620  // time constants
621  Real Td0_t = genDyn->tpdo.value;
622  Real Tq0_t = genDyn->tpqo.value;
623  Real Td0_s = genDyn->tppdo.value;
624  Real Tq0_s = genDyn->tppqo.value;
625 
626  // inertia
627  Real H = genDyn->inertia.value;
628 
629  // not available in CIM -> set to 0, as actually no impact on machine equations
630  Int poleNum = 0;
631  Real nomFieldCurr = 0;
632 
633  if (mGeneratorType == GeneratorType::TransientStability) {
634  SPDLOG_LOGGER_DEBUG(mSLog,
635  " GeneratorType is TransientStability.");
636  auto gen = DP::Ph1::SynchronGeneratorTrStab::make(
637  machine->mRID, machine->name, mComponentLogLevel);
638  gen->setStandardParametersPU(ratedPower, ratedVoltage, mFrequency,
639  Ld_t, H);
640  return gen;
641  } else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
642  SPDLOG_LOGGER_DEBUG(
643  mSLog, " GeneratorType is SynchronGenerator6aOrderVBR.");
644  auto gen = std::make_shared<DP::Ph1::SynchronGenerator6aOrderVBR>(
645  machine->mRID, machine->name, mComponentLogLevel);
646  gen->setOperationalParametersPerUnit(
647  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
648  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
649  return gen;
650  } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
651  SPDLOG_LOGGER_DEBUG(
652  mSLog, " GeneratorType is SynchronGenerator6bOrderVBR.");
653  auto gen = std::make_shared<DP::Ph1::SynchronGenerator6bOrderVBR>(
654  machine->mRID, machine->name, mComponentLogLevel);
655  gen->setOperationalParametersPerUnit(
656  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
657  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
658  return gen;
659  } else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
660  SPDLOG_LOGGER_DEBUG(
661  mSLog, " GeneratorType is SynchronGenerator5OrderVBR.");
662  auto gen = std::make_shared<DP::Ph1::SynchronGenerator5OrderVBR>(
663  machine->mRID, machine->name, mComponentLogLevel);
664  gen->setOperationalParametersPerUnit(
665  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
666  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
667  return gen;
668  } else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
669  SPDLOG_LOGGER_DEBUG(
670  mSLog, " GeneratorType is SynchronGenerator4OrderVBR.");
671  auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderVBR>(
672  machine->mRID, machine->name, mComponentLogLevel);
673  gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
674  mFrequency, H, Ld, Lq, Ll,
675  Ld_t, Lq_t, Td0_t, Tq0_t);
676  return gen;
677  } else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
678  SPDLOG_LOGGER_DEBUG(
679  mSLog, " GeneratorType is SynchronGenerator3OrderVBR.");
680  auto gen = std::make_shared<DP::Ph1::SynchronGenerator3OrderVBR>(
681  machine->mRID, machine->name, mComponentLogLevel);
682  gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
683  mFrequency, H, Ld, Lq, Ll,
684  Ld_t, Td0_t);
685  return gen;
686  } else if (mGeneratorType == GeneratorType::SG4OrderPCM) {
687  SPDLOG_LOGGER_DEBUG(
688  mSLog, " GeneratorType is SynchronGenerator4OrderPCM.");
689  auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderPCM>(
690  machine->mRID, machine->name, mComponentLogLevel);
691  gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
692  mFrequency, H, Ld, Lq, Ll,
693  Ld_t, Lq_t, Td0_t, Tq0_t);
694  return gen;
695  } else if (mGeneratorType == GeneratorType::SG4OrderTPM) {
696  SPDLOG_LOGGER_DEBUG(
697  mSLog, " GeneratorType is SynchronGenerator4OrderTPM.");
698  auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderTPM>(
699  machine->mRID, machine->name, mComponentLogLevel);
700  gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
701  mFrequency, H, Ld, Lq, Ll,
702  Ld_t, Lq_t, Td0_t, Tq0_t);
703  return gen;
704  } else if (mGeneratorType == GeneratorType::SG6OrderPCM) {
705  SPDLOG_LOGGER_DEBUG(
706  mSLog, " GeneratorType is SynchronGenerator6OrderPCM.");
707  auto gen = std::make_shared<DP::Ph1::SynchronGenerator6OrderPCM>(
708  machine->mRID, machine->name, mComponentLogLevel);
709  gen->setOperationalParametersPerUnit(
710  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
711  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
712  return gen;
713  }
714  }
715  }
716  }
717  } else if (mGeneratorType == GeneratorType::IdealVoltageSource) {
718  SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is IdealVoltageSource.");
719  return std::make_shared<DP::Ph1::SynchronGeneratorIdeal>(
720  machine->mRID, machine->name, mComponentLogLevel);
721  } else if (mGeneratorType == GeneratorType::None) {
722  throw SystemError("GeneratorType is None. Specify!");
723  } else {
724  throw SystemError("GeneratorType setting unfeasible.");
725  }
726  } else if (mDomain == Domain::SP) {
727  SPDLOG_LOGGER_INFO(mSLog, " Create generator in SP domain.");
728  if (mGeneratorType == GeneratorType::TransientStability ||
729  mGeneratorType == GeneratorType::SG6aOrderVBR ||
730  mGeneratorType == GeneratorType::SG6bOrderVBR ||
731  mGeneratorType == GeneratorType::SG5OrderVBR ||
732  mGeneratorType == GeneratorType::SG4OrderVBR ||
733  mGeneratorType == GeneratorType::SG3OrderVBR) {
734 
735  Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
736  Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
737 
738  for (auto obj : mModel->Objects) {
739  // Check if object is not TopologicalNode, SvVoltage or SvPowerFlow
740  if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
741  dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *>(
742  obj)) {
743  if (genDyn->SynchronousMachine->mRID == machine->mRID) {
744  // stator
745  Real Rs = genDyn->statorResistance.value;
746  Real Ll = genDyn->statorLeakageReactance.value;
747 
748  // reactances
749  Real Ld = genDyn->xDirectSync.value;
750  Real Lq = genDyn->xQuadSync.value;
751  Real Ld_t = genDyn->xDirectTrans.value;
752  Real Lq_t = genDyn->xQuadTrans.value;
753  Real Ld_s = genDyn->xDirectSubtrans.value;
754  Real Lq_s = genDyn->xQuadSubtrans.value;
755 
756  // time constants
757  Real Td0_t = genDyn->tpdo.value;
758  Real Tq0_t = genDyn->tpqo.value;
759  Real Td0_s = genDyn->tppdo.value;
760  Real Tq0_s = genDyn->tppqo.value;
761 
762  // inertia
763  Real H = genDyn->inertia.value;
764 
765  // not available in CIM -> set to 0, as actually no impact on machine equations
766  Int poleNum = 0;
767  Real nomFieldCurr = 0;
768 
769  if (mGeneratorType == GeneratorType::TransientStability) {
770  SPDLOG_LOGGER_DEBUG(mSLog,
771  " GeneratorType is TransientStability.");
772  auto gen = SP::Ph1::SynchronGeneratorTrStab::make(
773  machine->mRID, machine->name, mComponentLogLevel);
774  gen->setStandardParametersPU(ratedPower, ratedVoltage, mFrequency,
775  Ld_t, H);
776  return gen;
777  } else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
778  SPDLOG_LOGGER_DEBUG(
779  mSLog, " GeneratorType is SynchronGenerator6aOrderVBR.");
780  auto gen = std::make_shared<SP::Ph1::SynchronGenerator6aOrderVBR>(
781  machine->mRID, machine->name, mComponentLogLevel);
782  gen->setOperationalParametersPerUnit(
783  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
784  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
785  return gen;
786  } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
787  SPDLOG_LOGGER_DEBUG(
788  mSLog, " GeneratorType is SynchronGenerator6bOrderVBR.");
789  auto gen = std::make_shared<SP::Ph1::SynchronGenerator6bOrderVBR>(
790  machine->mRID, machine->name, mComponentLogLevel);
791  gen->setOperationalParametersPerUnit(
792  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
793  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
794  return gen;
795  } else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
796  SPDLOG_LOGGER_DEBUG(
797  mSLog, " GeneratorType is SynchronGenerator5OrderVBR.");
798  auto gen = std::make_shared<SP::Ph1::SynchronGenerator5OrderVBR>(
799  machine->mRID, machine->name, mComponentLogLevel);
800  gen->setOperationalParametersPerUnit(
801  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
802  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
803  return gen;
804  } else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
805  SPDLOG_LOGGER_DEBUG(
806  mSLog, " GeneratorType is SynchronGenerator4OrderVBR.");
807  auto gen = std::make_shared<SP::Ph1::SynchronGenerator4OrderVBR>(
808  machine->mRID, machine->name, mComponentLogLevel);
809  gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
810  mFrequency, H, Ld, Lq, Ll,
811  Ld_t, Lq_t, Td0_t, Tq0_t);
812  return gen;
813  } else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
814  SPDLOG_LOGGER_DEBUG(
815  mSLog, " GeneratorType is SynchronGenerator3OrderVBR.");
816  auto gen = std::make_shared<SP::Ph1::SynchronGenerator3OrderVBR>(
817  machine->mRID, machine->name, mComponentLogLevel);
818  gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
819  mFrequency, H, Ld, Lq, Ll,
820  Ld_t, Td0_t);
821  return gen;
822  }
823  }
824  }
825  }
826  } else if (mGeneratorType == GeneratorType::PVNode) {
827  SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is PVNode.");
828  for (auto obj : mModel->Objects) {
829  if (CIMPP::GeneratingUnit *genUnit =
830  dynamic_cast<CIMPP::GeneratingUnit *>(obj)) {
831  for (auto syncGen : genUnit->RotatingMachine) {
832  if (syncGen->mRID == machine->mRID) {
833  // Check whether relevant input data are set, otherwise set default values
834  Real setPointActivePower = 0;
835  Real setPointVoltage = 0;
836  Real maximumReactivePower = 1e12;
837  try {
838  setPointActivePower =
839  unitValue(genUnit->initialP.value, UnitMultiplier::M);
840  SPDLOG_LOGGER_INFO(mSLog, " setPointActivePower={}",
841  setPointActivePower);
842  } catch (ReadingUninitializedField *e) {
843  std::cerr
844  << "Uninitalized setPointActivePower for GeneratingUnit "
845  << machine->name << ". Using default value of "
846  << setPointActivePower << std::endl;
847  }
848  if (machine->RegulatingControl) {
849  setPointVoltage =
850  unitValue(machine->RegulatingControl->targetValue.value,
851  UnitMultiplier::k);
852  SPDLOG_LOGGER_INFO(mSLog, " setPointVoltage={}",
853  setPointVoltage);
854  } else {
855  std::cerr << "Uninitalized setPointVoltage for GeneratingUnit "
856  << machine->name << ". Using default value of "
857  << setPointVoltage << std::endl;
858  }
859  try {
860  maximumReactivePower =
861  unitValue(machine->maxQ.value, UnitMultiplier::M);
862  SPDLOG_LOGGER_INFO(mSLog, " maximumReactivePower={}",
863  maximumReactivePower);
864  } catch (ReadingUninitializedField *e) {
865  std::cerr
866  << "Uninitalized maximumReactivePower for GeneratingUnit "
867  << machine->name << ". Using default value of "
868  << maximumReactivePower << std::endl;
869  }
870 
871  auto gen = std::make_shared<SP::Ph1::SynchronGenerator>(
872  machine->mRID, machine->name, mComponentLogLevel);
873  gen->setParameters(
874  unitValue(machine->ratedS.value, UnitMultiplier::M),
875  unitValue(machine->ratedU.value, UnitMultiplier::k),
876  setPointActivePower, setPointVoltage, PowerflowBusType::PV);
877  gen->setBaseVoltage(
878  unitValue(machine->ratedU.value, UnitMultiplier::k));
879  return gen;
880  }
881  }
882  }
883  }
884  SPDLOG_LOGGER_INFO(mSLog, "no corresponding initial power for {}",
885  machine->name);
886  return std::make_shared<SP::Ph1::SynchronGenerator>(
887  machine->mRID, machine->name, mComponentLogLevel);
888  } else if (mGeneratorType == GeneratorType::None) {
889  throw SystemError("GeneratorType is None. Specify!");
890  } else {
891  throw SystemError("GeneratorType setting unfeasible.");
892  }
893  } else {
894  SPDLOG_LOGGER_INFO(mSLog, " Create generator in EMT domain.");
895  if (mGeneratorType == GeneratorType::FullOrder ||
896  mGeneratorType == GeneratorType::FullOrderVBR ||
897  mGeneratorType == GeneratorType::SG3OrderVBR ||
898  mGeneratorType == GeneratorType::SG4OrderVBR ||
899  mGeneratorType == GeneratorType::SG6aOrderVBR ||
900  mGeneratorType == GeneratorType::SG6bOrderVBR) {
901 
902  Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
903  Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
904 
905  for (auto obj : mModel->Objects) {
906  // Check if object is not TopologicalNode, SvVoltage or SvPowerFlow
907  if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
908  dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *>(
909  obj)) {
910  if (genDyn->SynchronousMachine->mRID == machine->mRID) {
911 
912  // stator
913  Real Rs = genDyn->statorResistance.value;
914  Real Ll = genDyn->statorLeakageReactance.value;
915 
916  // reactances
917  Real Ld = genDyn->xDirectSync.value;
918  Real Lq = genDyn->xQuadSync.value;
919  Real Ld_t = genDyn->xDirectTrans.value;
920  Real Lq_t = genDyn->xQuadTrans.value;
921  Real Ld_s = genDyn->xDirectSubtrans.value;
922  Real Lq_s = genDyn->xQuadSubtrans.value;
923 
924  // time constants
925  Real Td0_t = genDyn->tpdo.value;
926  Real Tq0_t = genDyn->tpqo.value;
927  Real Td0_s = genDyn->tppdo.value;
928  Real Tq0_s = genDyn->tppqo.value;
929 
930  // inertia
931  Real H = genDyn->inertia.value;
932 
933  // not available in CIM -> set to 0, as actually no impact on machine equations
934  Int poleNum = 0;
935  Real nomFieldCurr = 0;
936 
937  if (mGeneratorType == GeneratorType::FullOrder) {
938  SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is FullOrder.");
939  auto gen = std::make_shared<EMT::Ph3::SynchronGeneratorDQTrapez>(
940  machine->mRID, machine->name, mComponentLogLevel);
941  gen->setParametersOperationalPerUnit(
942  ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr,
943  Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, Td0_t, Tq0_t, Td0_s,
944  Tq0_s, H);
945  return gen;
946  } else if (mGeneratorType == GeneratorType::FullOrderVBR) {
947  SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is FullOrderVBR.");
948  auto gen = std::make_shared<EMT::Ph3::SynchronGeneratorVBR>(
949  machine->mRID, machine->name, mComponentLogLevel);
950  gen->setBaseAndOperationalPerUnitParameters(
951  ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr,
952  Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, Td0_t, Tq0_t, Td0_s,
953  Tq0_s, H);
954  return gen;
955  } else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
956  SPDLOG_LOGGER_DEBUG(
957  mSLog, " GeneratorType is SynchronGenerator6aOrderVBR.");
958  auto gen =
959  std::make_shared<EMT::Ph3::SynchronGenerator6aOrderVBR>(
960  machine->mRID, machine->name, mComponentLogLevel);
961  gen->setOperationalParametersPerUnit(
962  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
963  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
964  return gen;
965  } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
966  SPDLOG_LOGGER_DEBUG(
967  mSLog, " GeneratorType is SynchronGenerator6bOrderVBR.");
968  auto gen =
969  std::make_shared<EMT::Ph3::SynchronGenerator6bOrderVBR>(
970  machine->mRID, machine->name, mComponentLogLevel);
971  gen->setOperationalParametersPerUnit(
972  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
973  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
974  return gen;
975  } else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
976  SPDLOG_LOGGER_DEBUG(
977  mSLog, " GeneratorType is SynchronGenerator5OrderVBR.");
978  auto gen = std::make_shared<EMT::Ph3::SynchronGenerator5OrderVBR>(
979  machine->mRID, machine->name, mComponentLogLevel);
980  gen->setOperationalParametersPerUnit(
981  ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
982  Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
983  return gen;
984  } else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
985  SPDLOG_LOGGER_DEBUG(
986  mSLog, " GeneratorType is SynchronGenerator4OrderVBR.");
987  auto gen = std::make_shared<EMT::Ph3::SynchronGenerator4OrderVBR>(
988  machine->mRID, machine->name, mComponentLogLevel);
989  gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
990  mFrequency, H, Ld, Lq, Ll,
991  Ld_t, Lq_t, Td0_t, Tq0_t);
992  return gen;
993  } else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
994  SPDLOG_LOGGER_DEBUG(
995  mSLog, " GeneratorType is SynchronGenerator3OrderVBR.");
996  auto gen = std::make_shared<EMT::Ph3::SynchronGenerator3OrderVBR>(
997  machine->mRID, machine->name, mComponentLogLevel);
998  gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
999  mFrequency, H, Ld, Lq, Ll,
1000  Ld_t, Td0_t);
1001  return gen;
1002  }
1003  }
1004  }
1005  }
1006  } else if (mGeneratorType == GeneratorType::IdealVoltageSource) {
1007  SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is IdealVoltageSource.");
1008  return std::make_shared<EMT::Ph3::SynchronGeneratorIdeal>(
1009  machine->mRID, machine->name, mComponentLogLevel,
1010  GeneratorType::IdealVoltageSource);
1011  } else if (mGeneratorType == GeneratorType::IdealCurrentSource) {
1012  SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is IdealCurrentSource.");
1013  return std::make_shared<EMT::Ph3::SynchronGeneratorIdeal>(
1014  machine->mRID, machine->name, mComponentLogLevel,
1015  GeneratorType::IdealCurrentSource);
1016  } else if (mGeneratorType == GeneratorType::None) {
1017  throw SystemError("GeneratorType is None. Specify!");
1018  } else {
1019  throw SystemError("GeneratorType setting unfeasible.");
1020  }
1021  }
1022  return nullptr;
1023 }
1024 
1025 TopologicalPowerComp::Ptr
1026 Reader::mapExternalNetworkInjection(CIMPP::ExternalNetworkInjection *extnet) {
1027  SPDLOG_LOGGER_INFO(mSLog, "Found External Network Injection {}",
1028  extnet->name);
1029 
1030  Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(extnet);
1031 
1032  if (mDomain == Domain::EMT) {
1033  if (mPhase == PhaseType::ABC) {
1034  return std::make_shared<EMT::Ph3::NetworkInjection>(
1035  extnet->mRID, extnet->name, mComponentLogLevel);
1036  } else {
1037  throw SystemError(
1038  "Mapping of ExternalNetworkInjection for EMT::Ph1 not existent!");
1039  return nullptr;
1040  }
1041  } else if (mDomain == Domain::SP) {
1042  if (mPhase == PhaseType::Single) {
1043  auto cpsextnet = std::make_shared<SP::Ph1::NetworkInjection>(
1044  extnet->mRID, extnet->name, mComponentLogLevel);
1045  cpsextnet->modifyPowerFlowBusType(
1046  PowerflowBusType::
1047  VD); // for powerflow solver set as VD component as default
1048  cpsextnet->setBaseVoltage(baseVoltage);
1049 
1050  try {
1051  if (extnet->RegulatingControl) {
1052  SPDLOG_LOGGER_INFO(mSLog, " Voltage set-point={}",
1053  (float)extnet->RegulatingControl->targetValue);
1054  cpsextnet->setParameters(
1055  extnet->RegulatingControl->targetValue *
1056  baseVoltage); // assumes that value is specified in CIM data in per unit
1057  } else {
1058  SPDLOG_LOGGER_INFO(
1059  mSLog, " No voltage set-point defined. Using 1 per unit.");
1060  cpsextnet->setParameters(1. * baseVoltage);
1061  }
1062  } catch (ReadingUninitializedField *e) {
1063  std::cerr << "Ignore incomplete RegulatingControl" << std::endl;
1064  }
1065 
1066  return cpsextnet;
1067  } else {
1068  throw SystemError(
1069  "Mapping of ExternalNetworkInjection for SP::Ph3 not existent!");
1070  return nullptr;
1071  }
1072  } else {
1073  if (mPhase == PhaseType::Single) {
1074  return std::make_shared<DP::Ph1::NetworkInjection>(
1075  extnet->mRID, extnet->name, mComponentLogLevel);
1076  } else {
1077  throw SystemError(
1078  "Mapping of ExternalNetworkInjection for DP::Ph3 not existent!");
1079  return nullptr;
1080  }
1081  }
1082 }
1083 
1084 TopologicalPowerComp::Ptr
1085 Reader::mapEquivalentShunt(CIMPP::EquivalentShunt *shunt) {
1086  SPDLOG_LOGGER_INFO(mSLog, "Found shunt {}", shunt->name);
1087 
1088  Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(shunt);
1089 
1090  auto cpsShunt = std::make_shared<SP::Ph1::Shunt>(shunt->mRID, shunt->name,
1091  mComponentLogLevel);
1092  cpsShunt->setParameters(shunt->g.value, shunt->b.value);
1093  cpsShunt->setBaseVoltage(baseVoltage);
1094  return cpsShunt;
1095 }
1096 
1097 Real Reader::determineBaseVoltageAssociatedWithEquipment(
1098  CIMPP::ConductingEquipment *equipment) {
1099  Real baseVoltage = 0;
1100 
1101  // first look for baseVolt object to determine baseVoltage
1102  for (auto obj : mModel->Objects) {
1103  if (CIMPP::BaseVoltage *baseVolt =
1104  dynamic_cast<CIMPP::BaseVoltage *>(obj)) {
1105  for (auto comp : baseVolt->ConductingEquipment) {
1106  if (comp->name == equipment->name) {
1107  baseVoltage =
1108  unitValue(baseVolt->nominalVoltage.value, UnitMultiplier::k);
1109  }
1110  }
1111  }
1112  }
1113  // as second option take baseVoltage of topologicalNode where equipment is connected to
1114  if (baseVoltage == 0) {
1115  for (auto obj : mModel->Objects) {
1116  if (CIMPP::TopologicalNode *topNode =
1117  dynamic_cast<CIMPP::TopologicalNode *>(obj)) {
1118  for (auto term : topNode->Terminal) {
1119  if (term->ConductingEquipment->name == equipment->name) {
1120  baseVoltage = unitValue(topNode->BaseVoltage->nominalVoltage.value,
1121  UnitMultiplier::k);
1122  }
1123  }
1124  }
1125  }
1126  }
1127 
1128  return baseVoltage;
1129 }
1130 
1131 template <typename VarType>
1132 void Reader::processTopologicalNode(CIMPP::TopologicalNode *topNode) {
1133  // Add this node to global node list and assign simulation node incrementally.
1134  int matrixNodeIndex = Int(mPowerflowNodes.size());
1135  mPowerflowNodes[topNode->mRID] = SimNode<VarType>::make(
1136  topNode->mRID, topNode->name, matrixNodeIndex, mPhase);
1137 
1138  if (mPhase == PhaseType::ABC) {
1139  SPDLOG_LOGGER_INFO(
1140  mSLog, "TopologicalNode {} phase A as simulation node {} ",
1141  topNode->mRID,
1142  mPowerflowNodes[topNode->mRID]->matrixNodeIndex(PhaseType::A));
1143  SPDLOG_LOGGER_INFO(
1144  mSLog, "TopologicalNode {} phase B as simulation node {}",
1145  topNode->mRID,
1146  mPowerflowNodes[topNode->mRID]->matrixNodeIndex(PhaseType::B));
1147  SPDLOG_LOGGER_INFO(
1148  mSLog, "TopologicalNode {} phase C as simulation node {}",
1149  topNode->mRID,
1150  mPowerflowNodes[topNode->mRID]->matrixNodeIndex(PhaseType::C));
1151  } else
1152  SPDLOG_LOGGER_INFO(mSLog,
1153  "TopologicalNode id: {}, name: {} as simulation node {}",
1154  topNode->mRID, topNode->name,
1155  mPowerflowNodes[topNode->mRID]->matrixNodeIndex());
1156 
1157  for (auto term : topNode->Terminal) {
1158  // Insert Terminal if it does not exist in the map and add reference to node.
1159  // This could be optimized because the Terminal is searched twice.
1160  auto cpsTerm = SimTerminal<VarType>::make(term->mRID);
1161  mPowerflowTerminals.insert(std::make_pair(term->mRID, cpsTerm));
1162  cpsTerm->setNode(std::dynamic_pointer_cast<SimNode<VarType>>(
1163  mPowerflowNodes[topNode->mRID]));
1164 
1165  if (!term->sequenceNumber.initialized)
1166  term->sequenceNumber = 1;
1167 
1168  SPDLOG_LOGGER_INFO(mSLog, " Terminal {}, sequenceNumber {}", term->mRID,
1169  (int)term->sequenceNumber);
1170 
1171  // Try to process Equipment connected to Terminal.
1172  CIMPP::ConductingEquipment *equipment = term->ConductingEquipment;
1173  if (!equipment) {
1174  SPDLOG_LOGGER_WARN(mSLog, "Terminal {} has no Equipment, ignoring!",
1175  term->mRID);
1176  } else {
1177  // Insert Equipment if it does not exist in the map and add reference to Terminal.
1178  // This could be optimized because the Equipment is searched twice.
1179  if (mPowerflowEquipment.find(equipment->mRID) ==
1180  mPowerflowEquipment.end()) {
1181  TopologicalPowerComp::Ptr comp = mapComponent(equipment);
1182  if (comp) {
1183  mPowerflowEquipment.insert(std::make_pair(equipment->mRID, comp));
1184  } else {
1185  SPDLOG_LOGGER_WARN(mSLog, "Could not map equipment {}",
1186  equipment->mRID);
1187  continue;
1188  }
1189  }
1190 
1191  auto pfEquipment = mPowerflowEquipment.at(equipment->mRID);
1192  std::dynamic_pointer_cast<SimPowerComp<VarType>>(pfEquipment)
1193  ->setTerminalAt(std::dynamic_pointer_cast<SimTerminal<VarType>>(
1194  mPowerflowTerminals[term->mRID]),
1195  term->sequenceNumber - 1);
1196 
1197  SPDLOG_LOGGER_INFO(mSLog, " Added Terminal {} to Equipment {}",
1198  term->mRID, equipment->mRID);
1199  }
1200  }
1201 }
1202 
1203 template void
1204 Reader::processTopologicalNode<Real>(CIMPP::TopologicalNode *topNode);
1205 template void
1206 Reader::processTopologicalNode<Complex>(CIMPP::TopologicalNode *topNode);
void setShuntConductance(Real v)
set shunt conductance value
Definition: Reader.cpp:50
SystemTopology loadCIM(Real systemFrequency, const fs::path &filename, Domain domain=Domain::DP, PhaseType phase=PhaseType::Single, GeneratorType genType=GeneratorType::None)
Parses data from CIM files into the CPS data structure.
Definition: Reader.cpp:209
void useProtectionSwitches(Bool value=true)
If set, some components like loads include protection switches.
Definition: Reader.cpp:56
Reader(String name, Logger::Level logLevel=Logger::Level::info, Logger::Level componentLogLevel=Logger::Level::off)
Definition: Reader.cpp:21
void setShuntCapacitor(Real v)
set shunt capacitor value
Definition: Reader.cpp:45
static Matrix singlePhaseParameterToThreePhase(Real parameter)
To convert single phase parameters to symmetrical three phase ones.
Definition: MathUtils.cpp:211
Base class for all components that are transmitting power.
Definition: SimPowerComp.h:17