9#include <CIMExceptions.hpp>
10#include <CIMModel.hpp>
11#include <IEC61970.hpp>
15#include <dpsim-models/CIM/Reader.h>
18using namespace CPS::CIM;
21using CIMPP::UnitMultiplier;
24 Logger::Level componentLogLevel) {
25 mSLog = Logger::get(name +
"_CIM", logLevel);
27 mModel =
new CIMModel();
28 mModel->setDependencyCheckOff();
29 mComponentLogLevel = componentLogLevel;
32Reader::~Reader() {
delete mModel; }
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);
42 return loadCIM(systemFrequency, filenames, domain, phase, genType);
48 mShuntCapacitorValue = v;
49 mSetShuntCapacitor =
true;
53 mShuntConductanceValue = v;
54 mSetShuntConductance =
true;
59 mUseProtectionSwitches = value;
62Real Reader::unitValue(Real value, CIMPP::UnitMultiplier mult) {
64 case UnitMultiplier::p:
67 case UnitMultiplier::n:
70 case UnitMultiplier::micro:
73 case UnitMultiplier::m:
76 case UnitMultiplier::c:
79 case UnitMultiplier::d:
82 case UnitMultiplier::k:
85 case UnitMultiplier::M:
88 case UnitMultiplier::G:
91 case UnitMultiplier::T:
100TopologicalPowerComp::Ptr Reader::mapComponent(BaseClass *obj) {
101 if (CIMPP::ACLineSegment *line =
dynamic_cast<CIMPP::ACLineSegment *
>(obj))
102 return mapACLineSegment(line);
103 if (CIMPP::EnergyConsumer *consumer =
104 dynamic_cast<CIMPP::EnergyConsumer *
>(obj))
105 return mapEnergyConsumer(consumer);
106 if (CIMPP::PowerTransformer *trans =
107 dynamic_cast<CIMPP::PowerTransformer *
>(obj))
108 return mapPowerTransformer(trans);
109 if (CIMPP::SynchronousMachine *syncMachine =
110 dynamic_cast<CIMPP::SynchronousMachine *
>(obj))
111 return mapSynchronousMachine(syncMachine);
112 if (CIMPP::ExternalNetworkInjection *extnet =
113 dynamic_cast<CIMPP::ExternalNetworkInjection *
>(obj))
114 return mapExternalNetworkInjection(extnet);
115 if (CIMPP::EquivalentShunt *shunt =
116 dynamic_cast<CIMPP::EquivalentShunt *
>(obj))
117 return mapEquivalentShunt(shunt);
122void Reader::addFiles(
const fs::path &filename) {
123 if (!mModel->addCIMFile(filename.string()))
124 SPDLOG_LOGGER_ERROR(mSLog,
"Failed to read file {}", filename);
127void Reader::addFiles(
const std::list<fs::path> &filenames) {
128 for (
auto filename : filenames)
132void Reader::parseFiles() {
134 mModel->parseFiles();
136 SPDLOG_LOGGER_ERROR(mSLog,
"Failed to parse CIM files");
142 "#### List of TopologicalNodes, associated Terminals and Equipment");
143 for (
auto obj : mModel->Objects) {
144 if (CIMPP::TopologicalNode *topNode =
145 dynamic_cast<CIMPP::TopologicalNode *
>(obj)) {
146 if (mDomain == Domain::EMT)
147 processTopologicalNode<Real>(topNode);
149 processTopologicalNode<Complex>(topNode);
155 SPDLOG_LOGGER_INFO(mSLog,
156 "#### List of Node voltages and Terminal power flow data");
157 for (
auto obj : mModel->Objects) {
159 if (CIMPP::SvVoltage *volt =
dynamic_cast<CIMPP::SvVoltage *
>(obj)) {
160 processSvVoltage(volt);
163 else if (CIMPP::SvPowerFlow *flow =
164 dynamic_cast<CIMPP::SvPowerFlow *
>(obj)) {
165 processSvPowerFlow(flow);
169 SPDLOG_LOGGER_INFO(mSLog,
"#### Create other components");
170 for (
auto obj : mModel->Objects) {
173 if (!
dynamic_cast<CIMPP::TopologicalNode *
>(obj) &&
174 !
dynamic_cast<CIMPP::SvVoltage *
>(obj) &&
175 !
dynamic_cast<CIMPP::SvPowerFlow *
>(obj)) {
177 if (CIMPP::IdentifiedObject *idObj =
178 dynamic_cast<CIMPP::IdentifiedObject *
>(obj)) {
181 const auto &rid = cimString(idObj->mRID);
182 if (mPowerflowEquipment.find(rid) == mPowerflowEquipment.end()) {
183 TopologicalPowerComp::Ptr comp = mapComponent(obj);
185 mPowerflowEquipment.insert(std::make_pair(comp->uid(), comp));
191 SPDLOG_LOGGER_INFO(mSLog,
"#### Check topology for unconnected components");
192 for (
auto pfe : mPowerflowEquipment) {
195 if (mDomain == Domain::EMT) {
196 if (SimPowerComp<Real>::Ptr powercomp =
197 std::dynamic_pointer_cast<SimPowerComp<Real>>(c)) {
198 if (powercomp->terminalNumberConnected() < powercomp->terminalNumber())
199 throw InvalidTopology();
202 if (SimPowerComp<Complex>::Ptr powercomp =
203 std::dynamic_pointer_cast<SimPowerComp<Complex>>(c)) {
204 if (powercomp->terminalNumberConnected() < powercomp->terminalNumber())
205 throw InvalidTopology();
212 Domain domain, PhaseType phase,
213 GeneratorType genType) {
214 mFrequency = systemFrequency;
215 mOmega = 2 * PI * mFrequency;
218 mGeneratorType = genType;
221 return systemTopology();
225 const std::list<fs::path> &filenames,
226 Domain domain, PhaseType phase,
227 GeneratorType genType) {
228 mFrequency = systemFrequency;
229 mOmega = 2 * PI * mFrequency;
232 mGeneratorType = genType;
235 return systemTopology();
238void Reader::processSvVoltage(CIMPP::SvVoltage *volt) {
239 CIMPP::TopologicalNode *node = volt->TopologicalNode;
242 mSLog,
"SvVoltage references missing Topological Node, ignoring");
245 const auto &nodeRid = cimString(node->mRID);
246 auto search = mPowerflowNodes.find(nodeRid);
247 if (search == mPowerflowNodes.end()) {
248 SPDLOG_LOGGER_WARN(mSLog,
249 "SvVoltage references Topological Node {}"
250 " missing from mTopNodes, ignoring",
255 Real voltageAbs = Reader::unitValue(volt->v.value, UnitMultiplier::k);
258 SPDLOG_LOGGER_INFO(mSLog,
" Angle={}", (
float)volt->angle.value);
259 }
catch (ReadingUninitializedField *e) {
260 volt->angle.value = 0;
261 std::cerr <<
"Uninitialized Angle for SVVoltage at "
262 << volt->TopologicalNode->name <<
".Setting default value of "
263 << volt->angle.value << std::endl;
265 Real voltagePhase = volt->angle.value * PI / 180;
266 mPowerflowNodes[nodeRid]->setInitialVoltage(
267 std::polar<Real>(voltageAbs, voltagePhase));
270 mSLog,
"Node {} MatrixNodeIndex {}: {} V, {} deg",
271 mPowerflowNodes[nodeRid]->uid(),
272 mPowerflowNodes[nodeRid]->matrixNodeIndex(),
273 std::abs(mPowerflowNodes[nodeRid]->initialSingleVoltage()),
274 std::arg(mPowerflowNodes[nodeRid]->initialSingleVoltage()) * 180 / PI);
277void Reader::processSvPowerFlow(CIMPP::SvPowerFlow *flow) {
278 CIMPP::Terminal *term = flow->Terminal;
280 const auto &terminalRid = cimString(term->mRID);
282 mPowerflowTerminals[terminalRid]->setPower(
283 Complex(Reader::unitValue(flow->p.value, UnitMultiplier::M),
284 Reader::unitValue(flow->q.value, UnitMultiplier::M)));
286 SPDLOG_LOGGER_WARN(mSLog,
"Terminal {}: {} W + j {} Var", terminalRid,
287 mPowerflowTerminals[terminalRid]->singleActivePower(),
288 mPowerflowTerminals[terminalRid]->singleReactivePower());
291SystemTopology Reader::systemTopology() {
292 SystemTopology system(mFrequency);
294 for (
auto comp : mPowerflowEquipment) {
295 system.addComponent(comp.second);
297 if (SimPowerComp<Complex>::Ptr powercomp =
298 std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp.second)) {
299 for (
auto term : powercomp->topologicalTerminals()) {
300 TopologicalNode::Ptr node = term->topologicalNodes();
302 if (system.mComponentsAtNode.find(node) ==
303 system.mComponentsAtNode.end()) {
304 TopologicalPowerComp::List complist;
305 complist.push_back(powercomp);
306 system.mComponentsAtNode.insert(std::make_pair(node, complist));
308 system.mComponentsAtNode[node].push_back(powercomp);
314 system.mNodes.resize(mPowerflowNodes.size());
316 for (
auto node : mPowerflowNodes) {
319 system.addNodeAt(node.second, node.second->matrixNodeIndex());
325Matrix::Index Reader::mapTopologicalNode(String mrid) {
326 auto search = mPowerflowNodes.find(mrid);
327 if (search == mPowerflowNodes.end()) {
330 return search->second->matrixNodeIndex();
333TopologicalPowerComp::Ptr
334Reader::mapEnergyConsumer(CIMPP::EnergyConsumer *consumer) {
335 const auto &consumerName = cimString(consumer->name);
336 const auto &consumerRid = cimString(consumer->mRID);
338 SPDLOG_LOGGER_INFO(mSLog,
" Found EnergyConsumer {}", consumerName);
339 if (mDomain == Domain::EMT) {
340 if (mPhase == PhaseType::ABC) {
341 return std::make_shared<EMT::Ph3::RXLoad>(consumerRid, consumerName,
344 SPDLOG_LOGGER_INFO(mSLog,
" RXLoad for EMT not implemented yet");
345 return std::make_shared<DP::Ph1::RXLoad>(consumerRid, consumerName,
348 }
else if (mDomain == Domain::SP) {
349 auto load = std::make_shared<SP::Ph1::Load>(consumerRid, consumerName,
353 load->modifyPowerFlowBusType(
358 if (mUseProtectionSwitches)
359 return std::make_shared<DP::Ph1::RXLoadSwitch>(consumerRid, consumerName,
362 return std::make_shared<DP::Ph1::RXLoad>(consumerRid, consumerName,
367TopologicalPowerComp::Ptr Reader::mapACLineSegment(CIMPP::ACLineSegment *line) {
368 const auto &lineName = cimString(line->name);
369 const auto &lineRid = cimString(line->mRID);
371 SPDLOG_LOGGER_INFO(mSLog,
372 " Found ACLineSegment {} r={} x={} bch={} gch={}",
373 lineName, (
float)line->r.value, (
float)line->x.value,
374 (
float)line->bch.value, (
float)line->gch.value);
376 Real resistance = line->r.value;
377 Real inductance = line->x.value / mOmega;
381 Real capacitance = mShuntCapacitorValue;
382 Real conductance = mShuntConductanceValue;
384 if (line->bch.value > 1e-9 && !mSetShuntCapacitor)
385 capacitance = Real(line->bch.value / mOmega);
387 if (line->gch.value > 1e-9 && !mSetShuntConductance)
388 conductance = Real(line->gch.value);
390 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(line);
392 if (mDomain == Domain::EMT) {
393 if (mPhase == PhaseType::ABC) {
400 auto cpsLine = std::make_shared<EMT::Ph3::PiLine>(lineRid, lineName,
402 cpsLine->setParameters(res_3ph, ind_3ph, cap_3ph, cond_3ph);
405 SPDLOG_LOGGER_INFO(mSLog,
" PiLine for EMT not implemented yet");
406 auto cpsLine = std::make_shared<DP::Ph1::PiLine>(lineRid, lineName,
408 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
411 }
else if (mDomain == Domain::SP) {
412 auto cpsLine = std::make_shared<SP::Ph1::PiLine>(lineRid, lineName,
414 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
415 cpsLine->setBaseVoltage(baseVoltage);
418 auto cpsLine = std::make_shared<DP::Ph1::PiLine>(lineRid, lineName,
420 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
425TopologicalPowerComp::Ptr
426Reader::mapPowerTransformer(CIMPP::PowerTransformer *trans) {
427 const auto &transRid = cimString(trans->mRID);
428 if (trans->PowerTransformerEnd.size() != 2) {
431 "PowerTransformer {} does not have exactly two windings, ignoring",
432 cimString(trans->name));
435 SPDLOG_LOGGER_INFO(mSLog,
"Found PowerTransformer {}",
436 cimString(trans->name));
439 CIMPP::PowerTransformerEnd *end1 =
nullptr, *end2 =
nullptr;
440 for (
auto end : trans->PowerTransformerEnd) {
441 if (end->Terminal->sequenceNumber == 1)
443 else if (end->Terminal->sequenceNumber == 2)
450 SPDLOG_LOGGER_INFO(mSLog,
" PowerTransformerEnd_1 {}",
451 cimString(end1->name));
452 SPDLOG_LOGGER_INFO(mSLog,
" Srated={} Vrated={}",
453 (
float)end1->ratedS.value, (
float)end1->ratedU.value);
455 SPDLOG_LOGGER_INFO(mSLog,
" R={}", (
float)end1->r.value);
456 }
catch (ReadingUninitializedField *e1) {
457 end1->r.value = 1e-12;
458 SPDLOG_LOGGER_WARN(mSLog,
459 " Uninitialized value for PowerTrafoEnd1 setting "
460 "default value of R={}",
461 (
float)end1->r.value);
464 SPDLOG_LOGGER_INFO(mSLog,
" X={}", (
float)end1->x.value);
465 }
catch (ReadingUninitializedField *e1) {
466 end1->x.value = 1e-12;
467 SPDLOG_LOGGER_WARN(mSLog,
468 " Uninitialized value for PowerTrafoEnd1 setting "
469 "default value of X={}",
470 (
float)end1->x.value);
472 SPDLOG_LOGGER_INFO(mSLog,
" PowerTransformerEnd_2 {}",
473 cimString(end2->name));
474 SPDLOG_LOGGER_INFO(mSLog,
" Srated={} Vrated={}",
475 (
float)end2->ratedS.value, (
float)end2->ratedU.value);
477 SPDLOG_LOGGER_INFO(mSLog,
" R={}", (
float)end2->r.value);
478 }
catch (ReadingUninitializedField *e1) {
479 end2->r.value = 1e-12;
480 SPDLOG_LOGGER_WARN(mSLog,
481 " Uninitialized value for PowerTrafoEnd2 setting "
482 "default value of R={}",
483 (
float)end2->r.value);
486 SPDLOG_LOGGER_INFO(mSLog,
" X={}", (
float)end2->x.value);
487 }
catch (ReadingUninitializedField *e1) {
488 end2->x.value = 1e-12;
489 SPDLOG_LOGGER_WARN(mSLog,
490 " Uninitialized value for PowerTrafoEnd2 setting "
491 "default value of X={}",
492 (
float)end2->x.value);
495 if (end1->ratedS.value != end2->ratedS.value) {
498 " PowerTransformerEnds of {} come with distinct rated power values. "
499 "Using rated power of PowerTransformerEnd_1.",
500 cimString(trans->name));
502 Real ratedPower = unitValue(end1->ratedS.value, UnitMultiplier::M);
503 Real voltageNode1 = unitValue(end1->ratedU.value, UnitMultiplier::k);
504 Real voltageNode2 = unitValue(end2->ratedU.value, UnitMultiplier::k);
506 Real ratioAbsNominal = voltageNode1 / voltageNode2;
507 Real ratioAbs = ratioAbsNominal;
510 if (end1->RatioTapChanger) {
512 voltageNode1 / voltageNode2 *
513 (1 + (end1->RatioTapChanger->normalStep -
514 end1->RatioTapChanger->neutralStep) *
515 end1->RatioTapChanger->stepVoltageIncrement.value / 100);
519 if (end1->RatioTapChanger) {
520 for (
auto obj : mModel->Objects) {
521 auto tapStep =
dynamic_cast<CIMPP::SvTapStep *
>(obj);
522 if (tapStep && tapStep->TapChanger == end1->RatioTapChanger) {
524 voltageNode1 / voltageNode2 *
525 (1 + (tapStep->position - end1->RatioTapChanger->neutralStep) *
526 end1->RatioTapChanger->stepVoltageIncrement.value / 100);
537 if (voltageNode1 >= voltageNode2 && abs(end1->x.value) > 1e-12) {
538 inductance = end1->x.value / mOmega;
539 resistance = end1->r.value;
540 }
else if (voltageNode1 >= voltageNode2 && abs(end2->x.value) > 1e-12) {
541 inductance = end2->x.value / mOmega * std::pow(ratioAbsNominal, 2);
542 resistance = end2->r.value * std::pow(ratioAbsNominal, 2);
543 }
else if (voltageNode2 > voltageNode1 && abs(end2->x.value) > 1e-12) {
544 inductance = end2->x.value / mOmega;
545 resistance = end2->r.value;
546 }
else if (voltageNode2 > voltageNode1 && abs(end1->x.value) > 1e-12) {
547 inductance = end1->x.value / mOmega / std::pow(ratioAbsNominal, 2);
548 resistance = end1->r.value / std::pow(ratioAbsNominal, 2);
551 if (mDomain == Domain::EMT) {
552 if (mPhase == PhaseType::ABC) {
553 Matrix resistance_3ph =
555 Matrix inductance_3ph =
557 Bool withResistiveLosses = resistance > 0;
558 auto transformer = std::make_shared<EMT::Ph3::Transformer>(
559 transRid, cimString(trans->name), mComponentLogLevel,
560 withResistiveLosses);
561 transformer->setParameters(voltageNode1, voltageNode2, ratedPower,
562 ratioAbs, ratioPhase, resistance_3ph,
566 SPDLOG_LOGGER_INFO(mSLog,
" Transformer for EMT not implemented yet");
569 }
else if (mDomain == Domain::SP) {
570 auto transformer = std::make_shared<SP::Ph1::Transformer>(
571 transRid, cimString(trans->name), mComponentLogLevel);
572 transformer->setParameters(voltageNode1, voltageNode2, ratedPower, ratioAbs,
573 ratioPhase, resistance, inductance);
574 Real baseVolt = voltageNode1 >= voltageNode2 ? voltageNode1 : voltageNode2;
575 transformer->setBaseVoltage(baseVolt);
578 Bool withResistiveLosses = resistance > 0;
579 auto transformer = std::make_shared<DP::Ph1::Transformer>(
580 transRid, cimString(trans->name), mComponentLogLevel,
581 withResistiveLosses);
582 transformer->setParameters(voltageNode1, voltageNode2, ratedPower, ratioAbs,
583 ratioPhase, resistance, inductance);
588TopologicalPowerComp::Ptr
589Reader::mapSynchronousMachine(CIMPP::SynchronousMachine *machine) {
590 const auto &machineName = cimString(machine->name);
591 const auto &machineRid = cimString(machine->mRID);
593 SPDLOG_LOGGER_INFO(mSLog,
" Found Synchronous machine {}", machineName);
595 if (mDomain == Domain::DP) {
596 SPDLOG_LOGGER_INFO(mSLog,
" Create generator in DP domain.");
597 if (mGeneratorType == GeneratorType::TransientStability ||
598 mGeneratorType == GeneratorType::SG6aOrderVBR ||
599 mGeneratorType == GeneratorType::SG6bOrderVBR ||
600 mGeneratorType == GeneratorType::SG4OrderVBR ||
601 mGeneratorType == GeneratorType::SG3OrderVBR ||
602 mGeneratorType == GeneratorType::SG4OrderPCM ||
603 mGeneratorType == GeneratorType::SG4OrderTPM ||
604 mGeneratorType == GeneratorType::SG6OrderPCM) {
606 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
607 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
609 for (
auto obj : mModel->Objects) {
611 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
612 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *
>(
614 if (cimString(genDyn->SynchronousMachine->mRID) == machineRid) {
616 Real Rs = genDyn->statorResistance.value;
617 Real Ll = genDyn->statorLeakageReactance.value;
620 Real Ld = genDyn->xDirectSync.value;
621 Real Lq = genDyn->xQuadSync.value;
622 Real Ld_t = genDyn->xDirectTrans.value;
623 Real Lq_t = genDyn->xQuadTrans.value;
624 Real Ld_s = genDyn->xDirectSubtrans.value;
625 Real Lq_s = genDyn->xQuadSubtrans.value;
628 Real Td0_t = genDyn->tpdo.value;
629 Real Tq0_t = genDyn->tpqo.value;
630 Real Td0_s = genDyn->tppdo.value;
631 Real Tq0_s = genDyn->tppqo.value;
634 Real H = genDyn->inertia.value;
638 Real nomFieldCurr = 0;
640 if (mGeneratorType == GeneratorType::TransientStability) {
641 SPDLOG_LOGGER_DEBUG(mSLog,
642 " GeneratorType is TransientStability.");
643 auto gen = DP::Ph1::SynchronGeneratorTrStab::make(
644 machineRid, machineName, mComponentLogLevel);
645 gen->setStandardParametersPU(ratedPower, ratedVoltage, mFrequency,
648 }
else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
650 mSLog,
" GeneratorType is SynchronGenerator6aOrderVBR.");
651 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6aOrderVBR>(
652 machineRid, machineName, mComponentLogLevel);
653 gen->setOperationalParametersPerUnit(
654 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
655 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
657 }
else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
659 mSLog,
" GeneratorType is SynchronGenerator6bOrderVBR.");
660 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6bOrderVBR>(
661 machineRid, machineName, mComponentLogLevel);
662 gen->setOperationalParametersPerUnit(
663 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
664 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
666 }
else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
668 mSLog,
" GeneratorType is SynchronGenerator5OrderVBR.");
669 auto gen = std::make_shared<DP::Ph1::SynchronGenerator5OrderVBR>(
670 machineRid, machineName, mComponentLogLevel);
671 gen->setOperationalParametersPerUnit(
672 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
673 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
675 }
else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
677 mSLog,
" GeneratorType is SynchronGenerator4OrderVBR.");
678 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderVBR>(
679 machineRid, machineName, mComponentLogLevel);
680 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
681 mFrequency, H, Ld, Lq, Ll,
682 Ld_t, Lq_t, Td0_t, Tq0_t);
684 }
else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
686 mSLog,
" GeneratorType is SynchronGenerator3OrderVBR.");
687 auto gen = std::make_shared<DP::Ph1::SynchronGenerator3OrderVBR>(
688 machineRid, machineName, mComponentLogLevel);
689 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
690 mFrequency, H, Ld, Lq, Ll,
693 }
else if (mGeneratorType == GeneratorType::SG4OrderPCM) {
695 mSLog,
" GeneratorType is SynchronGenerator4OrderPCM.");
696 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderPCM>(
697 machineRid, machineName, mComponentLogLevel);
698 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
699 mFrequency, H, Ld, Lq, Ll,
700 Ld_t, Lq_t, Td0_t, Tq0_t);
702 }
else if (mGeneratorType == GeneratorType::SG4OrderTPM) {
704 mSLog,
" GeneratorType is SynchronGenerator4OrderTPM.");
705 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderTPM>(
706 machineRid, machineName, mComponentLogLevel);
707 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
708 mFrequency, H, Ld, Lq, Ll,
709 Ld_t, Lq_t, Td0_t, Tq0_t);
711 }
else if (mGeneratorType == GeneratorType::SG6OrderPCM) {
713 mSLog,
" GeneratorType is SynchronGenerator6OrderPCM.");
714 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6OrderPCM>(
715 machineRid, machineName, mComponentLogLevel);
716 gen->setOperationalParametersPerUnit(
717 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
718 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
724 }
else if (mGeneratorType == GeneratorType::IdealVoltageSource) {
725 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is IdealVoltageSource.");
726 return std::make_shared<DP::Ph1::SynchronGeneratorIdeal>(
727 machineRid, machineName, mComponentLogLevel);
728 }
else if (mGeneratorType == GeneratorType::None) {
729 throw SystemError(
"GeneratorType is None. Specify!");
731 throw SystemError(
"GeneratorType setting unfeasible.");
733 }
else if (mDomain == Domain::SP) {
734 SPDLOG_LOGGER_INFO(mSLog,
" Create generator in SP domain.");
735 if (mGeneratorType == GeneratorType::TransientStability ||
736 mGeneratorType == GeneratorType::SG6aOrderVBR ||
737 mGeneratorType == GeneratorType::SG6bOrderVBR ||
738 mGeneratorType == GeneratorType::SG5OrderVBR ||
739 mGeneratorType == GeneratorType::SG4OrderVBR ||
740 mGeneratorType == GeneratorType::SG3OrderVBR) {
742 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
743 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
745 for (
auto obj : mModel->Objects) {
747 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
748 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *
>(
750 if (cimString(genDyn->SynchronousMachine->mRID) == machineRid) {
752 Real Rs = genDyn->statorResistance.value;
753 Real Ll = genDyn->statorLeakageReactance.value;
756 Real Ld = genDyn->xDirectSync.value;
757 Real Lq = genDyn->xQuadSync.value;
758 Real Ld_t = genDyn->xDirectTrans.value;
759 Real Lq_t = genDyn->xQuadTrans.value;
760 Real Ld_s = genDyn->xDirectSubtrans.value;
761 Real Lq_s = genDyn->xQuadSubtrans.value;
764 Real Td0_t = genDyn->tpdo.value;
765 Real Tq0_t = genDyn->tpqo.value;
766 Real Td0_s = genDyn->tppdo.value;
767 Real Tq0_s = genDyn->tppqo.value;
770 Real H = genDyn->inertia.value;
774 Real nomFieldCurr = 0;
776 if (mGeneratorType == GeneratorType::TransientStability) {
777 SPDLOG_LOGGER_DEBUG(mSLog,
778 " GeneratorType is TransientStability.");
779 auto gen = SP::Ph1::SynchronGeneratorTrStab::make(
780 machineRid, machineName, mComponentLogLevel);
781 gen->setStandardParametersPU(ratedPower, ratedVoltage, mFrequency,
784 }
else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
786 mSLog,
" GeneratorType is SynchronGenerator6aOrderVBR.");
787 auto gen = std::make_shared<SP::Ph1::SynchronGenerator6aOrderVBR>(
788 machineRid, machineName, mComponentLogLevel);
789 gen->setOperationalParametersPerUnit(
790 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
791 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
793 }
else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
795 mSLog,
" GeneratorType is SynchronGenerator6bOrderVBR.");
796 auto gen = std::make_shared<SP::Ph1::SynchronGenerator6bOrderVBR>(
797 machineRid, machineName, mComponentLogLevel);
798 gen->setOperationalParametersPerUnit(
799 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
800 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
802 }
else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
804 mSLog,
" GeneratorType is SynchronGenerator5OrderVBR.");
805 auto gen = std::make_shared<SP::Ph1::SynchronGenerator5OrderVBR>(
806 machineRid, machineName, mComponentLogLevel);
807 gen->setOperationalParametersPerUnit(
808 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
809 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
811 }
else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
813 mSLog,
" GeneratorType is SynchronGenerator4OrderVBR.");
814 auto gen = std::make_shared<SP::Ph1::SynchronGenerator4OrderVBR>(
815 machineRid, machineName, mComponentLogLevel);
816 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
817 mFrequency, H, Ld, Lq, Ll,
818 Ld_t, Lq_t, Td0_t, Tq0_t);
820 }
else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
822 mSLog,
" GeneratorType is SynchronGenerator3OrderVBR.");
823 auto gen = std::make_shared<SP::Ph1::SynchronGenerator3OrderVBR>(
824 machineRid, machineName, mComponentLogLevel);
825 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
826 mFrequency, H, Ld, Lq, Ll,
833 }
else if (mGeneratorType == GeneratorType::PVNode) {
834 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is PVNode.");
835 for (
auto obj : mModel->Objects) {
836 if (CIMPP::GeneratingUnit *genUnit =
837 dynamic_cast<CIMPP::GeneratingUnit *
>(obj)) {
838 for (
auto syncGen : genUnit->RotatingMachine) {
839 if (cimString(syncGen->mRID) == machineRid) {
841 Real setPointActivePower = 0;
842 Real setPointVoltage = 0;
843 Real maximumReactivePower = 1e12;
845 setPointActivePower =
846 unitValue(genUnit->initialP.value, UnitMultiplier::M);
847 SPDLOG_LOGGER_INFO(mSLog,
" setPointActivePower={}",
848 setPointActivePower);
849 }
catch (ReadingUninitializedField *e) {
851 <<
"Uninitalized setPointActivePower for GeneratingUnit "
852 << machineName <<
". Using default value of "
853 << setPointActivePower << std::endl;
855 if (machine->RegulatingControl) {
857 unitValue(machine->RegulatingControl->targetValue.value,
859 SPDLOG_LOGGER_INFO(mSLog,
" setPointVoltage={}",
862 std::cerr <<
"Uninitalized setPointVoltage for GeneratingUnit "
863 << machineName <<
". Using default value of "
864 << setPointVoltage << std::endl;
867 maximumReactivePower =
868 unitValue(machine->maxQ.value, UnitMultiplier::M);
869 SPDLOG_LOGGER_INFO(mSLog,
" maximumReactivePower={}",
870 maximumReactivePower);
871 }
catch (ReadingUninitializedField *e) {
873 <<
"Uninitalized maximumReactivePower for GeneratingUnit "
874 << machineName <<
". Using default value of "
875 << maximumReactivePower << std::endl;
878 auto gen = std::make_shared<SP::Ph1::SynchronGenerator>(
879 machineRid, machineName, mComponentLogLevel);
881 unitValue(machine->ratedS.value, UnitMultiplier::M),
882 unitValue(machine->ratedU.value, UnitMultiplier::k),
883 setPointActivePower, setPointVoltage, PowerflowBusType::PV);
885 unitValue(machine->ratedU.value, UnitMultiplier::k));
891 SPDLOG_LOGGER_INFO(mSLog,
"no corresponding initial power for {}",
893 return std::make_shared<SP::Ph1::SynchronGenerator>(
894 machineRid, machineName, mComponentLogLevel);
895 }
else if (mGeneratorType == GeneratorType::None) {
896 throw SystemError(
"GeneratorType is None. Specify!");
898 throw SystemError(
"GeneratorType setting unfeasible.");
901 SPDLOG_LOGGER_INFO(mSLog,
" Create generator in EMT domain.");
902 if (mGeneratorType == GeneratorType::FullOrder ||
903 mGeneratorType == GeneratorType::FullOrderVBR ||
904 mGeneratorType == GeneratorType::SG3OrderVBR ||
905 mGeneratorType == GeneratorType::SG4OrderVBR ||
906 mGeneratorType == GeneratorType::SG6aOrderVBR ||
907 mGeneratorType == GeneratorType::SG6bOrderVBR) {
909 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
910 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
912 for (
auto obj : mModel->Objects) {
914 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
915 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *
>(
917 if (cimString(genDyn->SynchronousMachine->mRID) == machineRid) {
920 Real Rs = genDyn->statorResistance.value;
921 Real Ll = genDyn->statorLeakageReactance.value;
924 Real Ld = genDyn->xDirectSync.value;
925 Real Lq = genDyn->xQuadSync.value;
926 Real Ld_t = genDyn->xDirectTrans.value;
927 Real Lq_t = genDyn->xQuadTrans.value;
928 Real Ld_s = genDyn->xDirectSubtrans.value;
929 Real Lq_s = genDyn->xQuadSubtrans.value;
932 Real Td0_t = genDyn->tpdo.value;
933 Real Tq0_t = genDyn->tpqo.value;
934 Real Td0_s = genDyn->tppdo.value;
935 Real Tq0_s = genDyn->tppqo.value;
938 Real H = genDyn->inertia.value;
942 Real nomFieldCurr = 0;
944 if (mGeneratorType == GeneratorType::FullOrder) {
945 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is FullOrder.");
946 auto gen = std::make_shared<EMT::Ph3::SynchronGeneratorDQTrapez>(
947 machineRid, machineName, mComponentLogLevel);
948 gen->setParametersOperationalPerUnit(
949 ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr,
950 Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, Td0_t, Tq0_t, Td0_s,
953 }
else if (mGeneratorType == GeneratorType::FullOrderVBR) {
954 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is FullOrderVBR.");
955 auto gen = std::make_shared<EMT::Ph3::SynchronGeneratorVBR>(
956 machineRid, machineName, mComponentLogLevel);
957 gen->setBaseAndOperationalPerUnitParameters(
958 ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr,
959 Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, Td0_t, Tq0_t, Td0_s,
962 }
else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
964 mSLog,
" GeneratorType is SynchronGenerator6aOrderVBR.");
966 std::make_shared<EMT::Ph3::SynchronGenerator6aOrderVBR>(
967 machineRid, machineName, mComponentLogLevel);
968 gen->setOperationalParametersPerUnit(
969 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
970 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
972 }
else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
974 mSLog,
" GeneratorType is SynchronGenerator6bOrderVBR.");
976 std::make_shared<EMT::Ph3::SynchronGenerator6bOrderVBR>(
977 machineRid, machineName, mComponentLogLevel);
978 gen->setOperationalParametersPerUnit(
979 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
980 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
982 }
else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
984 mSLog,
" GeneratorType is SynchronGenerator5OrderVBR.");
985 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator5OrderVBR>(
986 machineRid, machineName, mComponentLogLevel);
987 gen->setOperationalParametersPerUnit(
988 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
989 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
991 }
else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
993 mSLog,
" GeneratorType is SynchronGenerator4OrderVBR.");
994 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator4OrderVBR>(
995 machineRid, machineName, mComponentLogLevel);
996 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
997 mFrequency, H, Ld, Lq, Ll,
998 Ld_t, Lq_t, Td0_t, Tq0_t);
1000 }
else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
1001 SPDLOG_LOGGER_DEBUG(
1002 mSLog,
" GeneratorType is SynchronGenerator3OrderVBR.");
1003 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator3OrderVBR>(
1004 machineRid, machineName, mComponentLogLevel);
1005 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
1006 mFrequency, H, Ld, Lq, Ll,
1013 }
else if (mGeneratorType == GeneratorType::IdealVoltageSource) {
1014 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is IdealVoltageSource.");
1015 return std::make_shared<EMT::Ph3::SynchronGeneratorIdeal>(
1016 machineRid, machineName, mComponentLogLevel,
1017 GeneratorType::IdealVoltageSource);
1018 }
else if (mGeneratorType == GeneratorType::IdealCurrentSource) {
1019 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is IdealCurrentSource.");
1020 return std::make_shared<EMT::Ph3::SynchronGeneratorIdeal>(
1021 machineRid, machineName, mComponentLogLevel,
1022 GeneratorType::IdealCurrentSource);
1023 }
else if (mGeneratorType == GeneratorType::None) {
1024 throw SystemError(
"GeneratorType is None. Specify!");
1026 throw SystemError(
"GeneratorType setting unfeasible.");
1032TopologicalPowerComp::Ptr
1033Reader::mapExternalNetworkInjection(CIMPP::ExternalNetworkInjection *extnet) {
1034 SPDLOG_LOGGER_INFO(mSLog,
"Found External Network Injection {}",
1035 cimString(extnet->name));
1037 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(extnet);
1039 if (mDomain == Domain::EMT) {
1040 if (mPhase == PhaseType::ABC) {
1041 return std::make_shared<EMT::Ph3::NetworkInjection>(
1042 extnet->mRID, extnet->name, mComponentLogLevel);
1045 "Mapping of ExternalNetworkInjection for EMT::Ph1 not existent!");
1048 }
else if (mDomain == Domain::SP) {
1049 if (mPhase == PhaseType::Single) {
1050 auto cpsextnet = std::make_shared<SP::Ph1::NetworkInjection>(
1051 extnet->mRID, extnet->name, mComponentLogLevel);
1052 cpsextnet->modifyPowerFlowBusType(
1055 cpsextnet->setBaseVoltage(baseVoltage);
1058 if (extnet->RegulatingControl) {
1059 SPDLOG_LOGGER_INFO(mSLog,
" Voltage set-point={}",
1060 (
float)extnet->RegulatingControl->targetValue);
1061 cpsextnet->setParameters(
1062 extnet->RegulatingControl->targetValue *
1066 mSLog,
" No voltage set-point defined. Using 1 per unit.");
1067 cpsextnet->setParameters(1. * baseVoltage);
1069 }
catch (ReadingUninitializedField *e) {
1070 std::cerr <<
"Ignore incomplete RegulatingControl" << std::endl;
1076 "Mapping of ExternalNetworkInjection for SP::Ph3 not existent!");
1080 if (mPhase == PhaseType::Single) {
1081 return std::make_shared<DP::Ph1::NetworkInjection>(
1082 extnet->mRID, extnet->name, mComponentLogLevel);
1085 "Mapping of ExternalNetworkInjection for DP::Ph3 not existent!");
1091TopologicalPowerComp::Ptr
1092Reader::mapEquivalentShunt(CIMPP::EquivalentShunt *shunt) {
1093 SPDLOG_LOGGER_INFO(mSLog,
"Found shunt {}", cimString(shunt->name));
1095 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(shunt);
1097 auto cpsShunt = std::make_shared<SP::Ph1::Shunt>(shunt->mRID, shunt->name,
1098 mComponentLogLevel);
1099 cpsShunt->setParameters(shunt->g.value, shunt->b.value);
1100 cpsShunt->setBaseVoltage(baseVoltage);
1104Real Reader::determineBaseVoltageAssociatedWithEquipment(
1105 CIMPP::ConductingEquipment *equipment) {
1106 Real baseVoltage = 0;
1109 for (
auto obj : mModel->Objects) {
1110 if (CIMPP::BaseVoltage *baseVolt =
1111 dynamic_cast<CIMPP::BaseVoltage *
>(obj)) {
1112 for (
auto comp : baseVolt->ConductingEquipment) {
1113 if (cimString(comp->name) == cimString(equipment->name)) {
1115 unitValue(baseVolt->nominalVoltage.value, UnitMultiplier::k);
1121 if (baseVoltage == 0) {
1122 for (
auto obj : mModel->Objects) {
1123 if (CIMPP::TopologicalNode *topNode =
1124 dynamic_cast<CIMPP::TopologicalNode *
>(obj)) {
1125 for (
auto term : topNode->Terminal) {
1126 if (cimString(term->ConductingEquipment->name) ==
1127 cimString(equipment->name)) {
1128 baseVoltage = unitValue(topNode->BaseVoltage->nominalVoltage.value,
1139template <
typename VarType>
1140void Reader::processTopologicalNode(CIMPP::TopologicalNode *topNode) {
1142 int matrixNodeIndex = Int(mPowerflowNodes.size());
1143 const auto &nodeRid = cimString(topNode->mRID);
1144 mPowerflowNodes[nodeRid] = SimNode<VarType>::make(
1145 nodeRid, cimString(topNode->name), matrixNodeIndex, mPhase);
1147 if (mPhase == PhaseType::ABC) {
1149 mSLog,
"TopologicalNode {} phase A as simulation node {} ", nodeRid,
1150 mPowerflowNodes[nodeRid]->matrixNodeIndex(PhaseType::A));
1152 mSLog,
"TopologicalNode {} phase B as simulation node {}", nodeRid,
1153 mPowerflowNodes[nodeRid]->matrixNodeIndex(PhaseType::B));
1155 mSLog,
"TopologicalNode {} phase C as simulation node {}", nodeRid,
1156 mPowerflowNodes[nodeRid]->matrixNodeIndex(PhaseType::C));
1158 SPDLOG_LOGGER_INFO(mSLog,
1159 "TopologicalNode id: {}, name: {} as simulation node {}",
1160 nodeRid, cimString(topNode->name),
1161 mPowerflowNodes[nodeRid]->matrixNodeIndex());
1163 for (
auto term : topNode->Terminal) {
1166 const auto &termRid = cimString(term->mRID);
1167 auto cpsTerm = SimTerminal<VarType>::make(termRid);
1168 mPowerflowTerminals.insert(std::make_pair(termRid, cpsTerm));
1170 std::dynamic_pointer_cast<SimNode<VarType>>(mPowerflowNodes[nodeRid]));
1172 if (!term->sequenceNumber.initialized)
1173 term->sequenceNumber = 1;
1175 SPDLOG_LOGGER_INFO(mSLog,
" Terminal {}, sequenceNumber {}", termRid,
1176 (
int)term->sequenceNumber);
1179 CIMPP::ConductingEquipment *equipment = term->ConductingEquipment;
1181 SPDLOG_LOGGER_WARN(mSLog,
"Terminal {} has no Equipment, ignoring!",
1186 const auto &equipmentRid = cimString(equipment->mRID);
1187 if (mPowerflowEquipment.find(equipmentRid) == mPowerflowEquipment.end()) {
1188 TopologicalPowerComp::Ptr comp = mapComponent(equipment);
1190 mPowerflowEquipment.insert(std::make_pair(equipmentRid, comp));
1192 SPDLOG_LOGGER_WARN(mSLog,
"Could not map equipment {}", equipmentRid);
1197 auto pfEquipment = mPowerflowEquipment.at(equipmentRid);
1198 if (pfEquipment ==
nullptr) {
1199 SPDLOG_LOGGER_ERROR(mSLog,
"Equipment {} is null in equipment list",
1201 throw SystemError(
"Equipment is null in equipment list.");
1203 std::dynamic_pointer_cast<SimPowerComp<VarType>>(pfEquipment)
1204 ->setTerminalAt(std::dynamic_pointer_cast<SimTerminal<VarType>>(
1205 mPowerflowTerminals[termRid]),
1206 term->sequenceNumber - 1);
1208 SPDLOG_LOGGER_INFO(mSLog,
" Added Terminal {} to Equipment {}",
1209 termRid, equipmentRid);
1215Reader::processTopologicalNode<Real>(CIMPP::TopologicalNode *topNode);
1217Reader::processTopologicalNode<Complex>(CIMPP::TopologicalNode *topNode);
void setShuntConductance(Real v)
set shunt conductance value
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.
void useProtectionSwitches(Bool value=true)
If set, some components like loads include protection switches.
Reader(String name, Logger::Level logLevel=Logger::Level::info, Logger::Level componentLogLevel=Logger::Level::off)
void setShuntCapacitor(Real v)
set shunt capacitor value
static Matrix singlePhaseParameterToThreePhase(Real parameter)
To convert single phase parameters to symmetrical three phase ones.