DPsim
Loading...
Searching...
No Matches
Reader.cpp
Go to the documentation of this file.
1/* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2 * EONERC, RWTH Aachen University
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 *********************************************************************************/
8
9#include <CIMExceptions.hpp>
10#include <CIMModel.hpp>
11#include <IEC61970.hpp>
12#include <memory>
13
14#define READER_CPP
16
17using namespace CPS;
18using namespace CPS::CIM;
19
20using CIMPP::String;
21using CIMPP::UnitMultiplier;
22
24 Logger::Level componentLogLevel) {
25 mSLog = Logger::get(name + "_CIM", logLevel);
26
27 mModel = new CIMModel();
28 mModel->setDependencyCheckOff();
29 mComponentLogLevel = componentLogLevel;
30}
31
32Reader::~Reader() { delete mModel; }
33
35 const std::list<CPS::String> &filenamesString,
36 Domain domain, PhaseType phase,
37 GeneratorType genType) {
38 std::list<fs::path> filenames;
39 for (auto f : filenamesString)
40 filenames.emplace_back(f);
41
42 return loadCIM(systemFrequency, filenames, domain, phase, genType);
43}
44
45// #### shunt component settings ####
48 mShuntCapacitorValue = v;
49 mSetShuntCapacitor = true;
50}
51
53 mShuntConductanceValue = v;
54 mSetShuntConductance = true;
55}
56
59 mUseProtectionSwitches = value;
60}
61
63 mExtnetVoltageTargetUnit = unit;
64}
65
66Real Reader::unitValue(Real value, CIMPP::UnitMultiplier mult) {
67 switch (mult) {
68 case UnitMultiplier::p:
69 value *= 1e-12;
70 break;
71 case UnitMultiplier::n:
72 value *= 1e-9;
73 break;
74 case UnitMultiplier::micro:
75 value *= 1e-6;
76 break;
77 case UnitMultiplier::m:
78 value *= 1e-3;
79 break;
80 case UnitMultiplier::c:
81 value *= 1e-2;
82 break;
83 case UnitMultiplier::d:
84 value *= 1e-1;
85 break;
86 case UnitMultiplier::k:
87 value *= 1e3;
88 break;
89 case UnitMultiplier::M:
90 value *= 1e6;
91 break;
92 case UnitMultiplier::G:
93 value *= 1e9;
94 break;
95 case UnitMultiplier::T:
96 value *= 1e12;
97 break;
98 default:
99 break;
100 }
101 return value;
102}
103
104Bool Reader::isSupportedConductingEquipment(BaseClass *obj) const {
105 if (!obj)
106 return false;
107
108 if (dynamic_cast<CIMPP::ACLineSegment *>(obj))
109 return true;
110
111 if (dynamic_cast<CIMPP::EnergyConsumer *>(obj))
112 return true;
113
114 if (dynamic_cast<CIMPP::PowerTransformer *>(obj))
115 return true;
116
117 if (dynamic_cast<CIMPP::SynchronousMachine *>(obj))
118 return true;
119
120 if (dynamic_cast<CIMPP::ExternalNetworkInjection *>(obj))
121 return true;
122
123 if (dynamic_cast<CIMPP::EquivalentShunt *>(obj))
124 return true;
125
126 if (dynamic_cast<CIMPP::Disconnector *>(obj))
127 return true;
128
129 if (dynamic_cast<CIMPP::Breaker *>(obj))
130 return true;
131
132 return false;
133}
134
135TopologicalPowerComp::Ptr Reader::mapComponent(BaseClass *obj) {
136 if (CIMPP::ACLineSegment *line = dynamic_cast<CIMPP::ACLineSegment *>(obj))
137 return mapACLineSegment(line);
138 if (CIMPP::EnergyConsumer *consumer =
139 dynamic_cast<CIMPP::EnergyConsumer *>(obj))
140 return mapEnergyConsumer(consumer);
141 if (CIMPP::PowerTransformer *trans =
142 dynamic_cast<CIMPP::PowerTransformer *>(obj))
143 return mapPowerTransformer(trans);
144 if (CIMPP::SynchronousMachine *syncMachine =
145 dynamic_cast<CIMPP::SynchronousMachine *>(obj))
146 return mapSynchronousMachine(syncMachine);
147 if (CIMPP::ExternalNetworkInjection *extnet =
148 dynamic_cast<CIMPP::ExternalNetworkInjection *>(obj))
149 return mapExternalNetworkInjection(extnet);
150 if (CIMPP::EquivalentShunt *shunt =
151 dynamic_cast<CIMPP::EquivalentShunt *>(obj))
152 return mapEquivalentShunt(shunt);
153 if (CIMPP::Disconnector *disc = dynamic_cast<CIMPP::Disconnector *>(obj))
154 return mapDisconnector(disc);
155 if (CIMPP::Breaker *cb = dynamic_cast<CIMPP::Breaker *>(obj))
156 return mapBreaker(cb);
157 return nullptr;
158}
159
160void Reader::addFiles(const fs::path &filename) {
161 if (!mModel->addCIMFile(filename.string()))
162 SPDLOG_LOGGER_ERROR(mSLog, "Failed to read file {}", filename);
163}
164
165void Reader::addFiles(const std::list<fs::path> &filenames) {
166 for (auto filename : filenames)
167 addFiles(filename);
168}
169
170void Reader::parseFiles() {
171 try {
172 mModel->parseFiles();
173 } catch (...) {
174 SPDLOG_LOGGER_ERROR(mSLog, "Failed to parse CIM files");
175 return;
176 }
177
178 SPDLOG_LOGGER_INFO(
179 mSLog,
180 "#### List of TopologicalNodes, associated Terminals and Equipment");
181 for (auto obj : mModel->Objects) {
182 if (CIMPP::TopologicalNode *topNode =
183 dynamic_cast<CIMPP::TopologicalNode *>(obj)) {
184 if (mDomain == Domain::EMT)
185 processTopologicalNode<Real>(topNode);
186 else
187 processTopologicalNode<Complex>(topNode);
188 }
189 }
190
191 // Collect voltage state variables associated to nodes that are used
192 // for various components.
193 SPDLOG_LOGGER_INFO(mSLog,
194 "#### List of Node voltages and Terminal power flow data");
195 for (auto obj : mModel->Objects) {
196 // Check if object is of class SvVoltage
197 if (CIMPP::SvVoltage *volt = dynamic_cast<CIMPP::SvVoltage *>(obj)) {
198 processSvVoltage(volt);
199 }
200 // Check if object is of class SvPowerFlow
201 else if (CIMPP::SvPowerFlow *flow =
202 dynamic_cast<CIMPP::SvPowerFlow *>(obj)) {
203 processSvPowerFlow(flow);
204 }
205 }
206
207 SPDLOG_LOGGER_INFO(mSLog, "#### Create other components");
208 for (auto obj : mModel->Objects) {
209
210 // Check if object is not TopologicalNode, SvVoltage or SvPowerFlow
211 if (!dynamic_cast<CIMPP::TopologicalNode *>(obj) &&
212 !dynamic_cast<CIMPP::SvVoltage *>(obj) &&
213 !dynamic_cast<CIMPP::SvPowerFlow *>(obj)) {
214
215 if (CIMPP::IdentifiedObject *idObj =
216 dynamic_cast<CIMPP::IdentifiedObject *>(obj)) {
217
218 // Check if object is already in equipment list
219 const auto &rid = cimString(idObj->mRID);
220 if (mPowerflowEquipment.find(rid) == mPowerflowEquipment.end()) {
221 TopologicalPowerComp::Ptr comp = mapComponent(obj);
222 if (comp)
223 mPowerflowEquipment.insert(std::make_pair(comp->uid(), comp));
224 }
225 }
226 }
227 }
228
229 SPDLOG_LOGGER_INFO(mSLog, "#### Check topology for unconnected components");
230 for (auto pfe : mPowerflowEquipment) {
231 auto c = pfe.second;
232
233 if (mDomain == Domain::EMT) {
234 if (SimPowerComp<Real>::Ptr powercomp =
235 std::dynamic_pointer_cast<SimPowerComp<Real>>(c)) {
236 if (powercomp->terminalNumberConnected() < powercomp->terminalNumber())
237 throw InvalidTopology();
238 }
239 } else {
240 if (SimPowerComp<Complex>::Ptr powercomp =
241 std::dynamic_pointer_cast<SimPowerComp<Complex>>(c)) {
242 if (powercomp->terminalNumberConnected() < powercomp->terminalNumber())
243 throw InvalidTopology();
244 }
245 }
246 }
247}
248
249SystemTopology Reader::loadCIM(Real systemFrequency, const fs::path &filename,
250 Domain domain, PhaseType phase,
251 GeneratorType genType) {
252 mFrequency = systemFrequency;
253 mOmega = 2 * PI * mFrequency;
254 mDomain = domain;
255 mPhase = phase;
256 mGeneratorType = genType;
257 addFiles(filename);
258 parseFiles();
259 return systemTopology();
260}
261
263 const std::list<fs::path> &filenames,
264 Domain domain, PhaseType phase,
265 GeneratorType genType) {
266 mFrequency = systemFrequency;
267 mOmega = 2 * PI * mFrequency;
268 mDomain = domain;
269 mPhase = phase;
270 mGeneratorType = genType;
271 addFiles(filenames);
272 parseFiles();
273 return systemTopology();
274}
275
276void Reader::processSvVoltage(CIMPP::SvVoltage *volt) {
277 CIMPP::TopologicalNode *node = volt->TopologicalNode;
278 if (!node) {
279 SPDLOG_LOGGER_WARN(
280 mSLog, "SvVoltage references missing Topological Node, ignoring");
281 return;
282 }
283 const auto &nodeRid = cimString(node->mRID);
284 auto search = mPowerflowNodes.find(nodeRid);
285 if (search == mPowerflowNodes.end()) {
286 SPDLOG_LOGGER_WARN(mSLog,
287 "SvVoltage references Topological Node {}"
288 " missing from mTopNodes, ignoring",
289 nodeRid);
290 return;
291 }
292
293 Real voltageAbs = Reader::unitValue(volt->v.value, UnitMultiplier::k);
294
295 try {
296 SPDLOG_LOGGER_INFO(mSLog, " Angle={}", (float)volt->angle.value);
297 } catch (ReadingUninitializedField *e) {
298 volt->angle.value = 0;
299 std::cerr << "Uninitialized Angle for SVVoltage at "
300 << volt->TopologicalNode->name << ".Setting default value of "
301 << volt->angle.value << std::endl;
302 }
303 Real voltagePhase = volt->angle.value * PI / 180;
304 mPowerflowNodes[nodeRid]->setInitialVoltage(
305 std::polar<Real>(voltageAbs, voltagePhase));
306
307 SPDLOG_LOGGER_INFO(
308 mSLog, "Node {} MatrixNodeIndex {}: {} V, {} deg",
309 mPowerflowNodes[nodeRid]->uid(),
310 mPowerflowNodes[nodeRid]->matrixNodeIndex(),
311 std::abs(mPowerflowNodes[nodeRid]->initialSingleVoltage()),
312 std::arg(mPowerflowNodes[nodeRid]->initialSingleVoltage()) * 180 / PI);
313}
314
315void Reader::processSvPowerFlow(CIMPP::SvPowerFlow *flow) {
316 if (!flow) {
317 SPDLOG_LOGGER_WARN(mSLog, "Cannot process null SvPowerFlow, ignoring");
318 return;
319 }
320
321 CIMPP::Terminal *term = flow->Terminal;
322
323 if (!term) {
324 SPDLOG_LOGGER_WARN(mSLog,
325 "SvPowerFlow references missing Terminal, ignoring");
326 return;
327 }
328
329 const auto &terminalRid = cimString(term->mRID);
330 auto terminalIt = mPowerflowTerminals.find(terminalRid);
331
332 if (terminalIt == mPowerflowTerminals.end() || !terminalIt->second) {
333 SPDLOG_LOGGER_WARN(mSLog,
334 "SvPowerFlow references Terminal {} missing from "
335 "mPowerflowTerminals, ignoring",
336 terminalRid);
337 return;
338 }
339
340 terminalIt->second->setPower(
341 Complex(Reader::unitValue(flow->p.value, UnitMultiplier::M),
342 Reader::unitValue(flow->q.value, UnitMultiplier::M)));
343
344 SPDLOG_LOGGER_INFO(mSLog, "Terminal {}: {} W + j {} Var", terminalRid,
345 terminalIt->second->singleActivePower(),
346 terminalIt->second->singleReactivePower());
347}
348
349SystemTopology Reader::systemTopology() {
350 SystemTopology system(mFrequency);
351
352 for (auto comp : mPowerflowEquipment) {
353 system.addComponent(comp.second);
354 // TODO support Real
355 if (SimPowerComp<Complex>::Ptr powercomp =
356 std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp.second)) {
357 for (auto term : powercomp->topologicalTerminals()) {
358 TopologicalNode::Ptr node = term->topologicalNodes();
359 //TopologicalNode::Ptr node = powercomp->topologicalTerminals().back()->topologicalNodes();
360 if (system.mComponentsAtNode.find(node) ==
361 system.mComponentsAtNode.end()) {
363 complist.push_back(powercomp);
364 system.mComponentsAtNode.insert(std::make_pair(node, complist));
365 } else {
366 system.mComponentsAtNode[node].push_back(powercomp);
367 }
368 }
369 }
370 }
371
372 system.mNodes.resize(mPowerflowNodes.size());
373
374 for (auto node : mPowerflowNodes) {
375 // The index of the node in the list should not matter anymore
376 //system.mNodes[node.second->matrixNodeIndex()] = node.second;
377 system.addNodeAt(node.second, node.second->matrixNodeIndex());
378 }
379
380 return system;
381}
382
383Matrix::Index Reader::mapTopologicalNode(String mrid) {
384 auto search = mPowerflowNodes.find(mrid);
385 if (search == mPowerflowNodes.end()) {
386 return -1;
387 }
388 return search->second->matrixNodeIndex();
389}
390
392Reader::mapEnergyConsumer(CIMPP::EnergyConsumer *consumer) {
393 const auto &consumerName = cimString(consumer->name);
394 const auto &consumerRid = cimString(consumer->mRID);
395
396 SPDLOG_LOGGER_INFO(mSLog, " Found EnergyConsumer {}", consumerName);
397 if (mDomain == Domain::EMT) {
398 if (mPhase == PhaseType::ABC) {
399 return std::make_shared<EMT::Ph3::RXLoad>(consumerRid, consumerName,
400 mComponentLogLevel);
401 } else {
402 SPDLOG_LOGGER_INFO(mSLog, " RXLoad for EMT not implemented yet");
403 return std::make_shared<DP::Ph1::RXLoad>(consumerRid, consumerName,
404 mComponentLogLevel);
405 }
406 } else if (mDomain == Domain::SP) {
407 auto load = std::make_shared<SP::Ph1::Load>(consumerRid, consumerName,
408 mComponentLogLevel);
409
410 // Seed P/Q from SSH when present; this makes SSH win over any
411 // SvPowerFlow data (skip if no SSH file was loaded at all, so the
412 // SV-derived terminal-power fallback still applies).
413 if (consumer->p.initialized || consumer->q.initialized) {
414 Real p = 0;
415 Real q = 0;
416 if (consumer->p.initialized)
417 p = unitValue(consumer->p.value, UnitMultiplier::M);
418 if (consumer->q.initialized)
419 q = unitValue(consumer->q.value, UnitMultiplier::M);
420 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(consumer);
421 load->setParameters(p, q, baseVoltage);
422 }
423
424 load->modifyPowerFlowBusType(
426 PQ); // for powerflow solver set as PQ component as default
427 return load;
428 } else {
429 if (mUseProtectionSwitches)
430 return std::make_shared<DP::Ph1::RXLoadSwitch>(consumerRid, consumerName,
431 mComponentLogLevel);
432 else
433 return std::make_shared<DP::Ph1::RXLoad>(consumerRid, consumerName,
434 mComponentLogLevel);
435 }
436}
437
438TopologicalPowerComp::Ptr Reader::mapACLineSegment(CIMPP::ACLineSegment *line) {
439 const auto &lineName = cimString(line->name);
440 const auto &lineRid = cimString(line->mRID);
441
442 SPDLOG_LOGGER_INFO(mSLog,
443 " Found ACLineSegment {} r={} x={} bch={} gch={}",
444 lineName, (float)line->r.value, (float)line->x.value,
445 (float)line->bch.value, (float)line->gch.value);
446
447 Real resistance = line->r.value;
448 Real inductance = line->x.value / mOmega;
449
450 // By default there is always a small conductance to ground to
451 // avoid problems with floating nodes.
452 Real capacitance = mShuntCapacitorValue;
453 Real conductance = mShuntConductanceValue;
454
455 if (line->bch.value > 1e-9 && !mSetShuntCapacitor)
456 capacitance = Real(line->bch.value / mOmega);
457
458 if (line->gch.value > 1e-9 && !mSetShuntConductance)
459 conductance = Real(line->gch.value);
460
461 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(line);
462
463 if (mDomain == Domain::EMT) {
464 if (mPhase == PhaseType::ABC) {
468 Matrix cond_3ph =
470
471 auto cpsLine = std::make_shared<EMT::Ph3::PiLine>(lineRid, lineName,
472 mComponentLogLevel);
473 cpsLine->setParameters(res_3ph, ind_3ph, cap_3ph, cond_3ph);
474 return cpsLine;
475 } else {
476 SPDLOG_LOGGER_INFO(mSLog, " PiLine for EMT not implemented yet");
477 auto cpsLine = std::make_shared<DP::Ph1::PiLine>(lineRid, lineName,
478 mComponentLogLevel);
479 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
480 return cpsLine;
481 }
482 } else if (mDomain == Domain::SP) {
483 auto cpsLine = std::make_shared<SP::Ph1::PiLine>(lineRid, lineName,
484 mComponentLogLevel);
485 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
486 cpsLine->setBaseVoltage(baseVoltage);
487 return cpsLine;
488 } else {
489 auto cpsLine = std::make_shared<DP::Ph1::PiLine>(lineRid, lineName,
490 mComponentLogLevel);
491 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
492 return cpsLine;
493 }
494}
495
497Reader::mapPowerTransformer(CIMPP::PowerTransformer *trans) {
498 const auto &transRid = cimString(trans->mRID);
499 if (trans->PowerTransformerEnd.size() != 2) {
500 SPDLOG_LOGGER_WARN(
501 mSLog,
502 "PowerTransformer {} does not have exactly two windings, ignoring",
503 cimString(trans->name));
504 return nullptr;
505 }
506 SPDLOG_LOGGER_INFO(mSLog, "Found PowerTransformer {}",
507 cimString(trans->name));
508
509 // assign transformer ends
510 CIMPP::PowerTransformerEnd *end1 = nullptr, *end2 = nullptr;
511 for (auto end : trans->PowerTransformerEnd) {
512 if (end->Terminal->sequenceNumber == 1)
513 end1 = end;
514 else if (end->Terminal->sequenceNumber == 2)
515 end2 = end;
516 else
517 return nullptr;
518 }
519
520 // setting default values for non-set resistances and reactances
521 SPDLOG_LOGGER_INFO(mSLog, " PowerTransformerEnd_1 {}",
522 cimString(end1->name));
523 SPDLOG_LOGGER_INFO(mSLog, " Srated={} Vrated={}",
524 (float)end1->ratedS.value, (float)end1->ratedU.value);
525 try {
526 SPDLOG_LOGGER_INFO(mSLog, " R={}", (float)end1->r.value);
527 } catch (ReadingUninitializedField *e1) {
528 end1->r.value = 1e-12;
529 SPDLOG_LOGGER_WARN(mSLog,
530 " Uninitialized value for PowerTrafoEnd1 setting "
531 "default value of R={}",
532 (float)end1->r.value);
533 }
534 try {
535 SPDLOG_LOGGER_INFO(mSLog, " X={}", (float)end1->x.value);
536 } catch (ReadingUninitializedField *e1) {
537 end1->x.value = 1e-12;
538 SPDLOG_LOGGER_WARN(mSLog,
539 " Uninitialized value for PowerTrafoEnd1 setting "
540 "default value of X={}",
541 (float)end1->x.value);
542 }
543 SPDLOG_LOGGER_INFO(mSLog, " PowerTransformerEnd_2 {}",
544 cimString(end2->name));
545 SPDLOG_LOGGER_INFO(mSLog, " Srated={} Vrated={}",
546 (float)end2->ratedS.value, (float)end2->ratedU.value);
547 try {
548 SPDLOG_LOGGER_INFO(mSLog, " R={}", (float)end2->r.value);
549 } catch (ReadingUninitializedField *e1) {
550 end2->r.value = 1e-12;
551 SPDLOG_LOGGER_WARN(mSLog,
552 " Uninitialized value for PowerTrafoEnd2 setting "
553 "default value of R={}",
554 (float)end2->r.value);
555 }
556 try {
557 SPDLOG_LOGGER_INFO(mSLog, " X={}", (float)end2->x.value);
558 } catch (ReadingUninitializedField *e1) {
559 end2->x.value = 1e-12;
560 SPDLOG_LOGGER_WARN(mSLog,
561 " Uninitialized value for PowerTrafoEnd2 setting "
562 "default value of X={}",
563 (float)end2->x.value);
564 }
565
566 if (end1->ratedS.value != end2->ratedS.value) {
567 SPDLOG_LOGGER_WARN(
568 mSLog,
569 " PowerTransformerEnds of {} come with distinct rated power values. "
570 "Using rated power of PowerTransformerEnd_1.",
571 cimString(trans->name));
572 }
573 Real ratedPower = unitValue(end1->ratedS.value, UnitMultiplier::M);
574 Real voltageNode1 = unitValue(end1->ratedU.value, UnitMultiplier::k);
575 Real voltageNode2 = unitValue(end2->ratedU.value, UnitMultiplier::k);
576
577 Real ratioAbsNominal = voltageNode1 / voltageNode2;
578 Real ratioAbs = ratioAbsNominal;
579
580 // use normalStep from RatioTapChanger
581 if (end1->RatioTapChanger) {
582 ratioAbs =
583 voltageNode1 / voltageNode2 *
584 (1 + (end1->RatioTapChanger->normalStep -
585 end1->RatioTapChanger->neutralStep) *
586 end1->RatioTapChanger->stepVoltageIncrement.value / 100);
587 }
588
589 // if corresponding SvTapStep available, use instead tap position from there
590 if (end1->RatioTapChanger) {
591 for (auto obj : mModel->Objects) {
592 auto tapStep = dynamic_cast<CIMPP::SvTapStep *>(obj);
593 if (tapStep && tapStep->TapChanger == end1->RatioTapChanger) {
594 ratioAbs =
595 voltageNode1 / voltageNode2 *
596 (1 + (tapStep->position - end1->RatioTapChanger->neutralStep) *
597 end1->RatioTapChanger->stepVoltageIncrement.value / 100);
598 }
599 }
600 }
601
602 // TODO: To be extracted from cim class
603 Real ratioPhase = 0;
604
605 // Calculate resistance and inductance referred to higher voltage side
606 Real resistance = 0;
607 Real inductance = 0;
608 if (voltageNode1 >= voltageNode2 && abs(end1->x.value) > 1e-12) {
609 inductance = end1->x.value / mOmega;
610 resistance = end1->r.value;
611 } else if (voltageNode1 >= voltageNode2 && abs(end2->x.value) > 1e-12) {
612 inductance = end2->x.value / mOmega * std::pow(ratioAbsNominal, 2);
613 resistance = end2->r.value * std::pow(ratioAbsNominal, 2);
614 } else if (voltageNode2 > voltageNode1 && abs(end2->x.value) > 1e-12) {
615 inductance = end2->x.value / mOmega;
616 resistance = end2->r.value;
617 } else if (voltageNode2 > voltageNode1 && abs(end1->x.value) > 1e-12) {
618 inductance = end1->x.value / mOmega / std::pow(ratioAbsNominal, 2);
619 resistance = end1->r.value / std::pow(ratioAbsNominal, 2);
620 }
621
622 CIMPP::PowerTransformerEnd *referenceEnd =
623 (voltageNode1 >= voltageNode2) ? end1 : end2;
624 Real referenceVoltage = std::max(voltageNode1, voltageNode2);
625 Real magnetizingConductance = referenceEnd->g.value;
626 Real magnetizingSusceptance = referenceEnd->b.value;
627 Real noLoadCurrent = 0;
628 Real noLoadLoss = 0;
629 Bool magnetizingFromFile = false;
630
631 if (ratedPower > 0) {
632 Real base = std::pow(referenceVoltage, 2) / ratedPower;
633 Real defaultNoLoadCurrent = 0.01;
634 Real defaultNoLoadLoss = 1e-3;
635 Real defaultSusceptancePerUnit = std::sqrt(
636 std::pow(defaultNoLoadCurrent, 2) - std::pow(defaultNoLoadLoss, 2));
637
638 Bool lossFromFile = magnetizingConductance > DOUBLE_EPSILON;
639 Bool susceptanceFromFile = magnetizingSusceptance < -DOUBLE_EPSILON;
640
641 if (magnetizingSusceptance > DOUBLE_EPSILON)
642 SPDLOG_LOGGER_WARN(
643 mSLog,
644 " {}: PowerTransformerEnd.b={} [S] is capacitive; the magnetizing "
645 "branch is inductive, so the default susceptance is used instead",
646 cimString(trans->name), magnetizingSusceptance);
647
648 noLoadLoss =
649 lossFromFile ? magnetizingConductance * base : defaultNoLoadLoss;
650 Real susceptancePerUnit = susceptanceFromFile
651 ? std::abs(magnetizingSusceptance) * base
652 : defaultSusceptancePerUnit;
653 noLoadCurrent = std::hypot(susceptancePerUnit, noLoadLoss);
654 magnetizingFromFile = lossFromFile || susceptanceFromFile;
655
656 if (magnetizingFromFile)
657 SPDLOG_LOGGER_INFO(
658 mSLog, " {}: magnetizing branch i0={} ({}) P0={} ({})",
659 cimString(trans->name), noLoadCurrent,
660 susceptanceFromFile ? "from the file" : "default susceptance",
661 noLoadLoss, lossFromFile ? "from the file" : "default loss");
662 else
663 SPDLOG_LOGGER_WARN(mSLog,
664 " {}: no magnetizing data on PowerTransformerEnd "
665 "(b={} [S], g={} [S]); using the built-in default",
666 cimString(trans->name), magnetizingSusceptance,
667 magnetizingConductance);
668 } else {
669 SPDLOG_LOGGER_WARN(mSLog,
670 " {}: rated power is {} [VA], so the magnetizing "
671 "branch cannot be sized from the file",
672 cimString(trans->name), ratedPower);
673 }
674
675 if (mDomain == Domain::EMT) {
676 if (mPhase == PhaseType::ABC) {
677 Matrix resistance_3ph =
679 Matrix inductance_3ph =
681 Bool withResistiveLosses = resistance > 0;
682 auto transformer = std::make_shared<EMT::Ph3::Transformer>(
683 transRid, cimString(trans->name), mComponentLogLevel,
684 withResistiveLosses);
685 transformer->setParameters(voltageNode1, voltageNode2, ratedPower,
686 ratioAbs, ratioPhase, resistance_3ph,
687 inductance_3ph);
688 if (magnetizingFromFile)
689 transformer->setMagnetizingBranch(noLoadCurrent, noLoadLoss);
690 return transformer;
691 } else {
692 SPDLOG_LOGGER_INFO(mSLog, " Transformer for EMT not implemented yet");
693 return nullptr;
694 }
695 } else if (mDomain == Domain::SP) {
696 auto transformer = std::make_shared<SP::Ph1::Transformer>(
697 transRid, cimString(trans->name), mComponentLogLevel);
698 transformer->setParameters(voltageNode1, voltageNode2, ratedPower, ratioAbs,
699 ratioPhase, resistance, inductance);
700 if (magnetizingFromFile)
701 transformer->setMagnetizingBranch(noLoadCurrent, noLoadLoss);
702 Real baseVolt = voltageNode1 >= voltageNode2 ? voltageNode1 : voltageNode2;
703 transformer->setBaseVoltage(baseVolt);
704 return transformer;
705 } else {
706 Bool withResistiveLosses = resistance > 0;
707 auto transformer = std::make_shared<DP::Ph1::Transformer>(
708 transRid, cimString(trans->name), mComponentLogLevel,
709 withResistiveLosses);
710 transformer->setParameters(voltageNode1, voltageNode2, ratedPower, ratioAbs,
711 ratioPhase, resistance, inductance);
712 if (magnetizingFromFile)
713 transformer->setMagnetizingBranch(noLoadCurrent, noLoadLoss);
714 return transformer;
715 }
716}
717
719Reader::mapSynchronousMachine(CIMPP::SynchronousMachine *machine) {
720 const auto &machineName = cimString(machine->name);
721 const auto &machineRid = cimString(machine->mRID);
722
723 SPDLOG_LOGGER_INFO(mSLog, " Found Synchronous machine {}", machineName);
724
725 if (mDomain == Domain::DP) {
726 SPDLOG_LOGGER_INFO(mSLog, " Create generator in DP domain.");
727 if (mGeneratorType == GeneratorType::TransientStability ||
728 mGeneratorType == GeneratorType::SG6aOrderVBR ||
729 mGeneratorType == GeneratorType::SG6bOrderVBR ||
730 mGeneratorType == GeneratorType::SG4OrderVBR ||
731 mGeneratorType == GeneratorType::SG3OrderVBR ||
732 mGeneratorType == GeneratorType::SG4OrderPCM ||
733 mGeneratorType == GeneratorType::SG4OrderTPM ||
734 mGeneratorType == GeneratorType::SG6OrderPCM) {
735
736 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
737 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
738
739 for (auto obj : mModel->Objects) {
740 // Check if object is not TopologicalNode, SvVoltage or SvPowerFlow
741 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
742 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *>(
743 obj)) {
744 if (cimString(genDyn->SynchronousMachine->mRID) == machineRid) {
745 // stator
746 Real Rs = genDyn->statorResistance.value;
747 Real Ll = genDyn->statorLeakageReactance.value;
748
749 // reactances
750 Real Ld = genDyn->xDirectSync.value;
751 Real Lq = genDyn->xQuadSync.value;
752 Real Ld_t = genDyn->xDirectTrans.value;
753 Real Lq_t = genDyn->xQuadTrans.value;
754 Real Ld_s = genDyn->xDirectSubtrans.value;
755 Real Lq_s = genDyn->xQuadSubtrans.value;
756
757 // time constants
758 Real Td0_t = genDyn->tpdo.value;
759 Real Tq0_t = genDyn->tpqo.value;
760 Real Td0_s = genDyn->tppdo.value;
761 Real Tq0_s = genDyn->tppqo.value;
762
763 // inertia
764 Real H = genDyn->inertia.value;
765
766 // not available in CIM -> set to 0, as actually no impact on machine equations
767 Int poleNum = 0;
768 Real nomFieldCurr = 0;
769
770 if (mGeneratorType == GeneratorType::TransientStability) {
771 SPDLOG_LOGGER_DEBUG(mSLog,
772 " GeneratorType is TransientStability.");
774 machineRid, machineName, mComponentLogLevel);
775 gen->setStandardParametersPU(ratedPower, ratedVoltage, mFrequency,
776 Ld_t, H);
777 return gen;
778 } else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
779 SPDLOG_LOGGER_DEBUG(
780 mSLog, " GeneratorType is SynchronGenerator6aOrderVBR.");
781 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6aOrderVBR>(
782 machineRid, machineName, mComponentLogLevel);
783 gen->setOperationalParametersPerUnit(
784 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
785 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
786 return gen;
787 } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
788 SPDLOG_LOGGER_DEBUG(
789 mSLog, " GeneratorType is SynchronGenerator6bOrderVBR.");
790 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6bOrderVBR>(
791 machineRid, machineName, mComponentLogLevel);
792 gen->setOperationalParametersPerUnit(
793 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
794 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
795 return gen;
796 } else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
797 SPDLOG_LOGGER_DEBUG(
798 mSLog, " GeneratorType is SynchronGenerator5OrderVBR.");
799 auto gen = std::make_shared<DP::Ph1::SynchronGenerator5OrderVBR>(
800 machineRid, machineName, mComponentLogLevel);
801 gen->setOperationalParametersPerUnit(
802 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
803 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
804 return gen;
805 } else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
806 SPDLOG_LOGGER_DEBUG(
807 mSLog, " GeneratorType is SynchronGenerator4OrderVBR.");
808 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderVBR>(
809 machineRid, machineName, mComponentLogLevel);
810 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
811 mFrequency, H, Ld, Lq, Ll,
812 Ld_t, Lq_t, Td0_t, Tq0_t);
813 return gen;
814 } else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
815 SPDLOG_LOGGER_DEBUG(
816 mSLog, " GeneratorType is SynchronGenerator3OrderVBR.");
817 auto gen = std::make_shared<DP::Ph1::SynchronGenerator3OrderVBR>(
818 machineRid, machineName, mComponentLogLevel);
819 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
820 mFrequency, H, Ld, Lq, Ll,
821 Ld_t, Td0_t);
822 return gen;
823 } else if (mGeneratorType == GeneratorType::SG4OrderPCM) {
824 SPDLOG_LOGGER_DEBUG(
825 mSLog, " GeneratorType is SynchronGenerator4OrderPCM.");
826 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderPCM>(
827 machineRid, machineName, mComponentLogLevel);
828 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
829 mFrequency, H, Ld, Lq, Ll,
830 Ld_t, Lq_t, Td0_t, Tq0_t);
831 return gen;
832 } else if (mGeneratorType == GeneratorType::SG4OrderTPM) {
833 SPDLOG_LOGGER_DEBUG(
834 mSLog, " GeneratorType is SynchronGenerator4OrderTPM.");
835 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderTPM>(
836 machineRid, machineName, mComponentLogLevel);
837 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
838 mFrequency, H, Ld, Lq, Ll,
839 Ld_t, Lq_t, Td0_t, Tq0_t);
840 return gen;
841 } else if (mGeneratorType == GeneratorType::SG6OrderPCM) {
842 SPDLOG_LOGGER_DEBUG(
843 mSLog, " GeneratorType is SynchronGenerator6OrderPCM.");
844 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6OrderPCM>(
845 machineRid, machineName, mComponentLogLevel);
846 gen->setOperationalParametersPerUnit(
847 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
848 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
849 return gen;
850 }
851 }
852 }
853 }
854 } else if (mGeneratorType == GeneratorType::IdealVoltageSource) {
855 SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is IdealVoltageSource.");
856 return std::make_shared<DP::Ph1::SynchronGeneratorIdeal>(
857 machineRid, machineName, mComponentLogLevel);
858 } else if (mGeneratorType == GeneratorType::None) {
859 throw SystemError("GeneratorType is None. Specify!");
860 } else {
861 throw SystemError("GeneratorType setting unfeasible.");
862 }
863 } else if (mDomain == Domain::SP) {
864 SPDLOG_LOGGER_INFO(mSLog, " Create generator in SP domain.");
865 if (mGeneratorType == GeneratorType::TransientStability ||
866 mGeneratorType == GeneratorType::SG6aOrderVBR ||
867 mGeneratorType == GeneratorType::SG6bOrderVBR ||
868 mGeneratorType == GeneratorType::SG5OrderVBR ||
869 mGeneratorType == GeneratorType::SG4OrderVBR ||
870 mGeneratorType == GeneratorType::SG3OrderVBR) {
871
872 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
873 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
874
875 for (auto obj : mModel->Objects) {
876 // Check if object is not TopologicalNode, SvVoltage or SvPowerFlow
877 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
878 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *>(
879 obj)) {
880 if (cimString(genDyn->SynchronousMachine->mRID) == machineRid) {
881 // stator
882 Real Rs = genDyn->statorResistance.value;
883 Real Ll = genDyn->statorLeakageReactance.value;
884
885 // reactances
886 Real Ld = genDyn->xDirectSync.value;
887 Real Lq = genDyn->xQuadSync.value;
888 Real Ld_t = genDyn->xDirectTrans.value;
889 Real Lq_t = genDyn->xQuadTrans.value;
890 Real Ld_s = genDyn->xDirectSubtrans.value;
891 Real Lq_s = genDyn->xQuadSubtrans.value;
892
893 // time constants
894 Real Td0_t = genDyn->tpdo.value;
895 Real Tq0_t = genDyn->tpqo.value;
896 Real Td0_s = genDyn->tppdo.value;
897 Real Tq0_s = genDyn->tppqo.value;
898
899 // inertia
900 Real H = genDyn->inertia.value;
901
902 // not available in CIM -> set to 0, as actually no impact on machine equations
903 Int poleNum = 0;
904 Real nomFieldCurr = 0;
905
906 if (mGeneratorType == GeneratorType::TransientStability) {
907 SPDLOG_LOGGER_DEBUG(mSLog,
908 " GeneratorType is TransientStability.");
910 machineRid, machineName, mComponentLogLevel);
911 gen->setStandardParametersPU(ratedPower, ratedVoltage, mFrequency,
912 Ld_t, H);
913 return gen;
914 } else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
915 SPDLOG_LOGGER_DEBUG(
916 mSLog, " GeneratorType is SynchronGenerator6aOrderVBR.");
917 auto gen = std::make_shared<SP::Ph1::SynchronGenerator6aOrderVBR>(
918 machineRid, machineName, mComponentLogLevel);
919 gen->setOperationalParametersPerUnit(
920 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
921 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
922 return gen;
923 } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
924 SPDLOG_LOGGER_DEBUG(
925 mSLog, " GeneratorType is SynchronGenerator6bOrderVBR.");
926 auto gen = std::make_shared<SP::Ph1::SynchronGenerator6bOrderVBR>(
927 machineRid, machineName, mComponentLogLevel);
928 gen->setOperationalParametersPerUnit(
929 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
930 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
931 return gen;
932 } else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
933 SPDLOG_LOGGER_DEBUG(
934 mSLog, " GeneratorType is SynchronGenerator5OrderVBR.");
935 auto gen = std::make_shared<SP::Ph1::SynchronGenerator5OrderVBR>(
936 machineRid, machineName, mComponentLogLevel);
937 gen->setOperationalParametersPerUnit(
938 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
939 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
940 return gen;
941 } else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
942 SPDLOG_LOGGER_DEBUG(
943 mSLog, " GeneratorType is SynchronGenerator4OrderVBR.");
944 auto gen = std::make_shared<SP::Ph1::SynchronGenerator4OrderVBR>(
945 machineRid, machineName, mComponentLogLevel);
946 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
947 mFrequency, H, Ld, Lq, Ll,
948 Ld_t, Lq_t, Td0_t, Tq0_t);
949 return gen;
950 } else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
951 SPDLOG_LOGGER_DEBUG(
952 mSLog, " GeneratorType is SynchronGenerator3OrderVBR.");
953 auto gen = std::make_shared<SP::Ph1::SynchronGenerator3OrderVBR>(
954 machineRid, machineName, mComponentLogLevel);
955 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
956 mFrequency, H, Ld, Lq, Ll,
957 Ld_t, Td0_t);
958 return gen;
959 }
960 }
961 }
962 }
963 } else if (mGeneratorType == GeneratorType::PVNode) {
964 SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is PVNode.");
965 for (auto obj : mModel->Objects) {
966 if (CIMPP::GeneratingUnit *genUnit =
967 dynamic_cast<CIMPP::GeneratingUnit *>(obj)) {
968 for (auto syncGen : genUnit->RotatingMachine) {
969 if (cimString(syncGen->mRID) == machineRid) {
970 // Check whether relevant input data are set, otherwise set default values
971 Real setPointActivePower = 0;
972 Real setPointVoltage = 0;
973 Real maximumReactivePower = 1e12;
975 try {
976 setPointActivePower =
977 unitValue(genUnit->initialP.value, UnitMultiplier::M);
978 SPDLOG_LOGGER_INFO(mSLog, " setPointActivePower={}",
979 setPointActivePower);
980 } catch (ReadingUninitializedField *e) {
981 std::cerr
982 << "Uninitalized setPointActivePower for GeneratingUnit "
983 << machineName << ". Using default value of "
984 << setPointActivePower << std::endl;
985 }
986 if (machine->RegulatingControl) {
987 setPointVoltage =
988 unitValue(machine->RegulatingControl->targetValue.value,
989 UnitMultiplier::k);
990 SPDLOG_LOGGER_INFO(mSLog, " setPointVoltage={}",
991 setPointVoltage);
992 } else {
993 // No RegulatingControl -> not voltage-regulating; map as PQ instead of guessing a PV voltage.
994 busType = PowerflowBusType::PQ;
995 SPDLOG_LOGGER_INFO(
996 mSLog,
997 " No RegulatingControl for {}, mapping as PQ generator",
998 machineName);
999 }
1000 try {
1001 maximumReactivePower =
1002 unitValue(machine->maxQ.value, UnitMultiplier::M);
1003 SPDLOG_LOGGER_INFO(mSLog, " maximumReactivePower={}",
1004 maximumReactivePower);
1005 } catch (ReadingUninitializedField *e) {
1006 std::cerr
1007 << "Uninitalized maximumReactivePower for GeneratingUnit "
1008 << machineName << ". Using default value of "
1009 << maximumReactivePower << std::endl;
1010 }
1011
1012 auto gen = std::make_shared<SP::Ph1::SynchronGenerator>(
1013 machineRid, machineName, mComponentLogLevel);
1014 gen->setParameters(
1015 unitValue(machine->ratedS.value, UnitMultiplier::M),
1016 unitValue(machine->ratedU.value, UnitMultiplier::k),
1017 setPointActivePower, setPointVoltage, busType);
1018 gen->setBaseVoltage(
1019 unitValue(machine->ratedU.value, UnitMultiplier::k));
1020 return gen;
1021 }
1022 }
1023 }
1024 }
1025 SPDLOG_LOGGER_INFO(mSLog, "no corresponding initial power for {}",
1026 machineName);
1027 return std::make_shared<SP::Ph1::SynchronGenerator>(
1028 machineRid, machineName, mComponentLogLevel);
1029 } else if (mGeneratorType == GeneratorType::None) {
1030 throw SystemError("GeneratorType is None. Specify!");
1031 } else {
1032 throw SystemError("GeneratorType setting unfeasible.");
1033 }
1034 } else {
1035 SPDLOG_LOGGER_INFO(mSLog, " Create generator in EMT domain.");
1036 if (mGeneratorType == GeneratorType::FullOrder ||
1037 mGeneratorType == GeneratorType::FullOrderVBR ||
1038 mGeneratorType == GeneratorType::SG3OrderVBR ||
1039 mGeneratorType == GeneratorType::SG4OrderVBR ||
1040 mGeneratorType == GeneratorType::SG6aOrderVBR ||
1041 mGeneratorType == GeneratorType::SG6bOrderVBR) {
1042
1043 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
1044 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
1045
1046 for (auto obj : mModel->Objects) {
1047 // Check if object is not TopologicalNode, SvVoltage or SvPowerFlow
1048 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
1049 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *>(
1050 obj)) {
1051 if (cimString(genDyn->SynchronousMachine->mRID) == machineRid) {
1052
1053 // stator
1054 Real Rs = genDyn->statorResistance.value;
1055 Real Ll = genDyn->statorLeakageReactance.value;
1056
1057 // reactances
1058 Real Ld = genDyn->xDirectSync.value;
1059 Real Lq = genDyn->xQuadSync.value;
1060 Real Ld_t = genDyn->xDirectTrans.value;
1061 Real Lq_t = genDyn->xQuadTrans.value;
1062 Real Ld_s = genDyn->xDirectSubtrans.value;
1063 Real Lq_s = genDyn->xQuadSubtrans.value;
1064
1065 // time constants
1066 Real Td0_t = genDyn->tpdo.value;
1067 Real Tq0_t = genDyn->tpqo.value;
1068 Real Td0_s = genDyn->tppdo.value;
1069 Real Tq0_s = genDyn->tppqo.value;
1070
1071 // inertia
1072 Real H = genDyn->inertia.value;
1073
1074 // not available in CIM -> set to 0, as actually no impact on machine equations
1075 Int poleNum = 0;
1076 Real nomFieldCurr = 0;
1077
1078 if (mGeneratorType == GeneratorType::FullOrder) {
1079 SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is FullOrder.");
1080 auto gen = std::make_shared<EMT::Ph3::SynchronGeneratorDQTrapez>(
1081 machineRid, machineName, mComponentLogLevel);
1082 gen->setParametersOperationalPerUnit(
1083 ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr,
1084 Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, Td0_t, Tq0_t, Td0_s,
1085 Tq0_s, H);
1086 return gen;
1087 } else if (mGeneratorType == GeneratorType::FullOrderVBR) {
1088 SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is FullOrderVBR.");
1089 auto gen = std::make_shared<EMT::Ph3::SynchronGeneratorVBR>(
1090 machineRid, machineName, mComponentLogLevel);
1091 gen->setBaseAndOperationalPerUnitParameters(
1092 ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr,
1093 Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, Td0_t, Tq0_t, Td0_s,
1094 Tq0_s, H);
1095 return gen;
1096 } else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
1097 SPDLOG_LOGGER_DEBUG(
1098 mSLog, " GeneratorType is SynchronGenerator6aOrderVBR.");
1099 auto gen =
1100 std::make_shared<EMT::Ph3::SynchronGenerator6aOrderVBR>(
1101 machineRid, machineName, mComponentLogLevel);
1102 gen->setOperationalParametersPerUnit(
1103 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
1104 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
1105 return gen;
1106 } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
1107 SPDLOG_LOGGER_DEBUG(
1108 mSLog, " GeneratorType is SynchronGenerator6bOrderVBR.");
1109 auto gen =
1110 std::make_shared<EMT::Ph3::SynchronGenerator6bOrderVBR>(
1111 machineRid, machineName, mComponentLogLevel);
1112 gen->setOperationalParametersPerUnit(
1113 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
1114 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
1115 return gen;
1116 } else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
1117 SPDLOG_LOGGER_DEBUG(
1118 mSLog, " GeneratorType is SynchronGenerator5OrderVBR.");
1119 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator5OrderVBR>(
1120 machineRid, machineName, mComponentLogLevel);
1121 gen->setOperationalParametersPerUnit(
1122 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
1123 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
1124 return gen;
1125 } else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
1126 SPDLOG_LOGGER_DEBUG(
1127 mSLog, " GeneratorType is SynchronGenerator4OrderVBR.");
1128 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator4OrderVBR>(
1129 machineRid, machineName, mComponentLogLevel);
1130 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
1131 mFrequency, H, Ld, Lq, Ll,
1132 Ld_t, Lq_t, Td0_t, Tq0_t);
1133 return gen;
1134 } else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
1135 SPDLOG_LOGGER_DEBUG(
1136 mSLog, " GeneratorType is SynchronGenerator3OrderVBR.");
1137 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator3OrderVBR>(
1138 machineRid, machineName, mComponentLogLevel);
1139 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
1140 mFrequency, H, Ld, Lq, Ll,
1141 Ld_t, Td0_t);
1142 return gen;
1143 }
1144 }
1145 }
1146 }
1147 } else if (mGeneratorType == GeneratorType::IdealVoltageSource) {
1148 SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is IdealVoltageSource.");
1149 return std::make_shared<EMT::Ph3::SynchronGeneratorIdeal>(
1150 machineRid, machineName, mComponentLogLevel,
1152 } else if (mGeneratorType == GeneratorType::IdealCurrentSource) {
1153 SPDLOG_LOGGER_DEBUG(mSLog, " GeneratorType is IdealCurrentSource.");
1154 return std::make_shared<EMT::Ph3::SynchronGeneratorIdeal>(
1155 machineRid, machineName, mComponentLogLevel,
1157 } else if (mGeneratorType == GeneratorType::None) {
1158 throw SystemError("GeneratorType is None. Specify!");
1159 } else {
1160 throw SystemError("GeneratorType setting unfeasible.");
1161 }
1162 }
1163 return nullptr;
1164}
1165
1167Reader::mapExternalNetworkInjection(CIMPP::ExternalNetworkInjection *extnet) {
1168 SPDLOG_LOGGER_INFO(mSLog, "Found External Network Injection {}",
1169 cimString(extnet->name));
1170
1171 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(extnet);
1172
1173 if (mDomain == Domain::EMT) {
1174 if (mPhase == PhaseType::ABC) {
1175 return std::make_shared<EMT::Ph3::NetworkInjection>(
1176 extnet->mRID, extnet->name, mComponentLogLevel);
1177 } else {
1178 throw SystemError(
1179 "Mapping of ExternalNetworkInjection for EMT::Ph1 not existent!");
1180 return nullptr;
1181 }
1182 } else if (mDomain == Domain::SP) {
1183 if (mPhase == PhaseType::Single) {
1184 auto cpsextnet = std::make_shared<SP::Ph1::NetworkInjection>(
1185 extnet->mRID, extnet->name, mComponentLogLevel);
1186 cpsextnet->modifyPowerFlowBusType(
1188 VD); // for powerflow solver set as VD component as default
1189 cpsextnet->setBaseVoltage(baseVoltage);
1190
1191 try {
1192 if (extnet->RegulatingControl) {
1193 // targetValueUnitMultiplier is uninitialized if unset, so Auto guesses by magnitude
1194 Real rawTarget = extnet->RegulatingControl->targetValue.value;
1195 Bool perUnit;
1196 switch (mExtnetVoltageTargetUnit) {
1198 perUnit = true;
1199 break;
1201 perUnit = false;
1202 break;
1204 default:
1205 perUnit = std::abs(rawTarget) >= 0.5 && std::abs(rawTarget) <= 1.5;
1206 break;
1207 }
1208 Real voltageSetPoint = perUnit
1209 ? rawTarget * baseVoltage
1210 : unitValue(rawTarget, UnitMultiplier::k);
1211 SPDLOG_LOGGER_INFO(mSLog, " Voltage set-point={}",
1212 voltageSetPoint);
1213 cpsextnet->setParameters(voltageSetPoint);
1214 } else {
1215 SPDLOG_LOGGER_INFO(
1216 mSLog, " No voltage set-point defined. Using 1 per unit.");
1217 cpsextnet->setParameters(1. * baseVoltage);
1218 }
1219 } catch (ReadingUninitializedField *e) {
1220 std::cerr << "Ignore incomplete RegulatingControl" << std::endl;
1221 }
1222
1223 return cpsextnet;
1224 } else {
1225 throw SystemError(
1226 "Mapping of ExternalNetworkInjection for SP::Ph3 not existent!");
1227 return nullptr;
1228 }
1229 } else {
1230 if (mPhase == PhaseType::Single) {
1231 return std::make_shared<DP::Ph1::NetworkInjection>(
1232 extnet->mRID, extnet->name, mComponentLogLevel);
1233 } else {
1234 throw SystemError(
1235 "Mapping of ExternalNetworkInjection for DP::Ph3 not existent!");
1236 return nullptr;
1237 }
1238 }
1239}
1240
1242Reader::mapEquivalentShunt(CIMPP::EquivalentShunt *shunt) {
1243 SPDLOG_LOGGER_INFO(mSLog, "Found shunt {}", cimString(shunt->name));
1244
1245 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(shunt);
1246
1247 auto cpsShunt = std::make_shared<SP::Ph1::Shunt>(shunt->mRID, shunt->name,
1248 mComponentLogLevel);
1249 cpsShunt->setParameters(shunt->g.value, shunt->b.value);
1250 cpsShunt->setBaseVoltage(baseVoltage);
1251 return cpsShunt;
1252}
1253
1254TopologicalPowerComp::Ptr Reader::mapDisconnector(CIMPP::Disconnector *disc) {
1255
1256 SPDLOG_LOGGER_INFO(mSLog, "Found Disconnector {} with status {}",
1257 cimString(disc->name), (bool)disc->open.value);
1258
1259 Real openResistance = 1e12;
1260 Real closedResistance = 1e-6;
1261
1262 Bool status = disc->open.value;
1263
1264 if (mPhase == PhaseType::ABC) {
1265 Matrix openResistance3Ph =
1267 Matrix closedResistance3Ph =
1269
1270 if (mDomain == Domain::EMT) {
1271 auto cpsSwitch = std::make_shared<EMT::Ph3::Switch>(
1272 disc->mRID, disc->name, mComponentLogLevel);
1273
1274 cpsSwitch->setParameters(openResistance3Ph, closedResistance3Ph);
1275
1276 if (status == true) {
1277 cpsSwitch->openSwitch();
1278 } else {
1279 cpsSwitch->closeSwitch();
1280 }
1281
1282 return cpsSwitch;
1283 } else if (mDomain == Domain::DP) {
1284 SPDLOG_LOGGER_INFO(mSLog,
1285 "Mapping of Disconnector for DP::Ph3 not existent!");
1286
1287 return nullptr;
1288 } else {
1289 SPDLOG_LOGGER_INFO(mSLog,
1290 "Mapping of Disconnector for SP::Ph3 not existent!");
1291 return nullptr;
1292 }
1293 } else {
1294 std::shared_ptr<CPS::TopologicalPowerComp> topoSwitch;
1295 std::shared_ptr<CPS::Base::Ph1::Switch> cpsSwitch;
1296
1297 if (mDomain == Domain::EMT) {
1298 auto sw = std::make_shared<EMT::Ph1::Switch>(disc->mRID, disc->name,
1299 mComponentLogLevel);
1300 cpsSwitch = sw;
1301 topoSwitch = sw;
1302 } else if (mDomain == Domain::DP) {
1303 auto sw = std::make_shared<DP::Ph1::Switch>(disc->mRID, disc->name,
1304 mComponentLogLevel);
1305 cpsSwitch = sw;
1306 topoSwitch = sw;
1307 } else {
1308 auto sw = std::make_shared<SP::Ph1::Switch>(disc->mRID, disc->name,
1309 mComponentLogLevel);
1310 cpsSwitch = sw;
1311 topoSwitch = sw;
1312 }
1313
1314 cpsSwitch->setParameters(openResistance, closedResistance);
1315 if (status) {
1316 cpsSwitch->open();
1317 } else {
1318 cpsSwitch->close();
1319 }
1320
1321 return topoSwitch;
1322 }
1323}
1324
1325TopologicalPowerComp::Ptr Reader::mapBreaker(CIMPP::Breaker *cb) {
1326 SPDLOG_LOGGER_INFO(mSLog, "Found Breaker {} with status {}",
1327 cimString(cb->name), (bool)cb->open.value);
1328
1329 Real openResistance = 1e12;
1330 Real closedResistance = 1e-6;
1331
1332 Bool status = cb->open.value;
1333
1334 if (mPhase == PhaseType::ABC) {
1335 Matrix openResistance3Ph =
1337 Matrix closedResistance3Ph =
1339
1340 if (mDomain == Domain::EMT) {
1341 auto cpsSwitch = std::make_shared<EMT::Ph3::Switch>(cb->mRID, cb->name,
1342 mComponentLogLevel);
1343
1344 cpsSwitch->setParameters(openResistance3Ph, closedResistance3Ph);
1345
1346 if (status == true) {
1347 cpsSwitch->openSwitch();
1348 } else {
1349 cpsSwitch->closeSwitch();
1350 }
1351
1352 return cpsSwitch;
1353 } else if (mDomain == Domain::DP) {
1354 SPDLOG_LOGGER_INFO(mSLog, "Mapping of Breaker for DP::Ph3 not existent!");
1355
1356 return nullptr;
1357 } else {
1358 SPDLOG_LOGGER_INFO(mSLog, "Mapping of Breaker for SP::Ph3 not existent!");
1359 return nullptr;
1360 }
1361 } else {
1362 std::shared_ptr<CPS::TopologicalPowerComp> topoSwitch;
1363 std::shared_ptr<CPS::Base::Ph1::Switch> cpsSwitch;
1364
1365 if (mDomain == Domain::EMT) {
1366 auto sw = std::make_shared<EMT::Ph1::Switch>(cb->mRID, cb->name,
1367 mComponentLogLevel);
1368 cpsSwitch = sw;
1369 topoSwitch = sw;
1370 } else if (mDomain == Domain::DP) {
1371 auto sw = std::make_shared<DP::Ph1::Switch>(cb->mRID, cb->name,
1372 mComponentLogLevel);
1373 cpsSwitch = sw;
1374 topoSwitch = sw;
1375 } else {
1376 auto sw = std::make_shared<SP::Ph1::Switch>(cb->mRID, cb->name,
1377 mComponentLogLevel);
1378 cpsSwitch = sw;
1379 topoSwitch = sw;
1380 }
1381
1382 cpsSwitch->setParameters(openResistance, closedResistance);
1383 if (status) {
1384 cpsSwitch->open();
1385 } else {
1386 cpsSwitch->close();
1387 }
1388
1389 return topoSwitch;
1390 }
1391}
1392
1393Real Reader::determineBaseVoltageAssociatedWithEquipment(
1394 CIMPP::ConductingEquipment *equipment) {
1395 Real baseVoltage = 0;
1396
1397 // first look for baseVolt object to determine baseVoltage
1398 for (auto obj : mModel->Objects) {
1399 if (CIMPP::BaseVoltage *baseVolt =
1400 dynamic_cast<CIMPP::BaseVoltage *>(obj)) {
1401 for (auto comp : baseVolt->ConductingEquipment) {
1402 if (cimString(comp->name) == cimString(equipment->name)) {
1403 baseVoltage =
1404 unitValue(baseVolt->nominalVoltage.value, UnitMultiplier::k);
1405 }
1406 }
1407 }
1408 }
1409 // as second option take baseVoltage of topologicalNode where equipment is connected to
1410 if (baseVoltage == 0) {
1411 for (auto obj : mModel->Objects) {
1412 if (CIMPP::TopologicalNode *topNode =
1413 dynamic_cast<CIMPP::TopologicalNode *>(obj)) {
1414 for (auto term : topNode->Terminal) {
1415 if (cimString(term->ConductingEquipment->name) ==
1416 cimString(equipment->name)) {
1417 baseVoltage = unitValue(topNode->BaseVoltage->nominalVoltage.value,
1418 UnitMultiplier::k);
1419 }
1420 }
1421 }
1422 }
1423 }
1424
1425 return baseVoltage;
1426}
1427
1428template <typename VarType>
1429void Reader::processTopologicalNode(CIMPP::TopologicalNode *topNode) {
1430 // Add this node to global node list and assign simulation node incrementally.
1431
1432 if (!topNode) {
1433 SPDLOG_LOGGER_WARN(mSLog, "Cannot process null TopologicalNode, ignoring");
1434 return;
1435 }
1436
1437 Bool hasSupportedEquipment = false;
1438
1439 for (auto term : topNode->Terminal) {
1440 if (!term || !term->ConductingEquipment)
1441 continue;
1442
1443 if (isSupportedConductingEquipment(term->ConductingEquipment)) {
1444 hasSupportedEquipment = true;
1445 break;
1446 }
1447 }
1448
1449 if (!hasSupportedEquipment) {
1450 SPDLOG_LOGGER_WARN(
1451 mSLog,
1452 "Skipping TopologicalNode {} ({}) because it has no terminals "
1453 "connected to supported equipment",
1454 cimString(topNode->name), cimString(topNode->mRID));
1455 return;
1456 }
1457
1458 int matrixNodeIndex = Int(mPowerflowNodes.size());
1459 const auto &nodeRid = cimString(topNode->mRID);
1460 mPowerflowNodes[nodeRid] = SimNode<VarType>::make(
1461 nodeRid, cimString(topNode->name), matrixNodeIndex, mPhase);
1462
1463 if (mPhase == PhaseType::ABC) {
1464 SPDLOG_LOGGER_INFO(
1465 mSLog, "TopologicalNode {} phase A as simulation node {} ", nodeRid,
1466 mPowerflowNodes[nodeRid]->matrixNodeIndex(PhaseType::A));
1467 SPDLOG_LOGGER_INFO(
1468 mSLog, "TopologicalNode {} phase B as simulation node {}", nodeRid,
1469 mPowerflowNodes[nodeRid]->matrixNodeIndex(PhaseType::B));
1470 SPDLOG_LOGGER_INFO(
1471 mSLog, "TopologicalNode {} phase C as simulation node {}", nodeRid,
1472 mPowerflowNodes[nodeRid]->matrixNodeIndex(PhaseType::C));
1473 } else
1474 SPDLOG_LOGGER_INFO(mSLog,
1475 "TopologicalNode id: {}, name: {} as simulation node {}",
1476 nodeRid, cimString(topNode->name),
1477 mPowerflowNodes[nodeRid]->matrixNodeIndex());
1478
1479 for (auto term : topNode->Terminal) {
1480 if (!term) {
1481 SPDLOG_LOGGER_WARN(
1482 mSLog, "TopologicalNode {} contains a null Terminal, ignoring",
1483 nodeRid);
1484 continue;
1485 }
1486
1487 const auto &termRid = cimString(term->mRID);
1488 auto cpsTerm = SimTerminal<VarType>::make(termRid);
1489 mPowerflowTerminals.insert(std::make_pair(termRid, cpsTerm));
1490 cpsTerm->setNode(
1491 std::dynamic_pointer_cast<SimNode<VarType>>(mPowerflowNodes[nodeRid]));
1492
1493 if (!term->sequenceNumber.initialized)
1494 term->sequenceNumber = 1;
1495
1496 SPDLOG_LOGGER_INFO(mSLog, " Terminal {}, sequenceNumber {}", termRid,
1497 (int)term->sequenceNumber);
1498
1499 // Try to process Equipment connected to Terminal.
1500 CIMPP::ConductingEquipment *equipment = term->ConductingEquipment;
1501 if (!equipment) {
1502 SPDLOG_LOGGER_WARN(mSLog, "Terminal {} has no Equipment, ignoring!",
1503 termRid);
1504 continue;
1505 }
1506
1507 // Unsupported Equipment check
1508 if (!isSupportedConductingEquipment(equipment)) {
1509 SPDLOG_LOGGER_WARN(
1510 mSLog,
1511 "Terminal {} references unsupported ConductingEquipment {}, ignoring",
1512 termRid, cimString(equipment->mRID));
1513 continue;
1514 }
1515
1516 // Insert Equipment if it does not exist in the map and add reference to Terminal.
1517 // This could be optimized because the Equipment is searched twice.
1518 const auto &equipmentRid = cimString(equipment->mRID);
1519 if (mPowerflowEquipment.find(equipmentRid) == mPowerflowEquipment.end()) {
1520 TopologicalPowerComp::Ptr comp = mapComponent(equipment);
1521 if (comp) {
1522 mPowerflowEquipment.insert(std::make_pair(equipmentRid, comp));
1523 } else {
1524 SPDLOG_LOGGER_WARN(mSLog, "Could not map equipment {}", equipmentRid);
1525 continue;
1526 }
1527 }
1528
1529 auto pfEquipment = mPowerflowEquipment.at(equipmentRid);
1530 if (pfEquipment == nullptr) {
1531 SPDLOG_LOGGER_ERROR(mSLog, "Equipment {} is null in equipment list",
1532 equipmentRid);
1533 throw SystemError("Equipment is null in equipment list.");
1534 }
1535 std::dynamic_pointer_cast<SimPowerComp<VarType>>(pfEquipment)
1536 ->setTerminalAt(std::dynamic_pointer_cast<SimTerminal<VarType>>(
1537 mPowerflowTerminals[termRid]),
1538 term->sequenceNumber - 1);
1539
1540 SPDLOG_LOGGER_INFO(mSLog, " Added Terminal {} to Equipment {}",
1541 termRid, equipmentRid);
1542 }
1543}
1544
1545template void
1546Reader::processTopologicalNode<Real>(CIMPP::TopologicalNode *topNode);
1547template void
1548Reader::processTopologicalNode<Complex>(CIMPP::TopologicalNode *topNode);
void setExtnetVoltageTargetUnit(VoltageTargetUnit unit)
Definition Reader.cpp:62
void setShuntConductance(Real v)
set shunt conductance value
Definition Reader.cpp:52
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:249
void useProtectionSwitches(Bool value=true)
If set, some components like loads include protection switches.
Definition Reader.cpp:58
Reader(String name, Logger::Level logLevel=Logger::Level::info, Logger::Level componentLogLevel=Logger::Level::off)
Definition Reader.cpp:23
virtual ~Reader()
Definition Reader.cpp:32
void setShuntCapacitor(Real v)
set shunt capacitor value
Definition Reader.cpp:47
spdlog::level::level_enum Level
Definition Logger.h:33
static Log get(const std::string &name, Level filelevel=Level::info, Level clilevel=Level::off)
Definition Logger.cpp:139
static Matrix singlePhaseParameterToThreePhase(Real parameter)
To convert single phase parameters to symmetrical three phase ones.
std::shared_ptr< SimPowerComp< VarType > > Ptr
std::shared_ptr< TopologicalNode > Ptr
std::shared_ptr< TopologicalPowerComp > Ptr
static std::shared_ptr< SynchronGeneratorTrStab > make(Args &&...args)
Definition PtrFactory.h:19
#define PI
Definition Definitions.h:43
#define DOUBLE_EPSILON
Definition Definitions.h:14
const auto & cimString(const T &field)
Definition Reader.h:69
PowerflowBusType
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > Matrix
Dense matrix for real numbers.
Definition Definitions.h:79
std::string String
Definition Definitions.h:63
double Real
Definition Definitions.h:60
int Int
Definition Definitions.h:59
std::complex< Real > Complex
Definition Definitions.h:61
bool Bool
Definition Definitions.h:62
GeneratorType
CPS::SystemTopology SystemTopology
Definition DPsim.h:37