9#include <CIMExceptions.hpp>
10#include <CIMModel.hpp>
11#include <IEC61970.hpp>
15#include <dpsim-models/CIM/Reader.h>
18using namespace CPS::CIM;
19using CIMPP::UnitMultiplier;
22 Logger::Level componentLogLevel) {
23 mSLog = Logger::get(name +
"_CIM", logLevel);
25 mModel =
new CIMModel();
26 mModel->setDependencyCheckOff();
27 mComponentLogLevel = componentLogLevel;
30Reader::~Reader() {
delete mModel; }
33 const std::list<CPS::String> &filenamesString,
34 Domain domain, PhaseType phase,
35 GeneratorType genType) {
36 std::list<fs::path> filenames;
37 for (
auto f : filenamesString)
38 filenames.emplace_back(f);
40 return loadCIM(systemFrequency, filenames, domain, phase, genType);
46 mShuntCapacitorValue = v;
47 mSetShuntCapacitor =
true;
51 mShuntConductanceValue = v;
52 mSetShuntConductance =
true;
57 mUseProtectionSwitches = value;
60Real Reader::unitValue(Real value, CIMPP::UnitMultiplier mult) {
62 case UnitMultiplier::p:
65 case UnitMultiplier::n:
68 case UnitMultiplier::micro:
71 case UnitMultiplier::m:
74 case UnitMultiplier::c:
77 case UnitMultiplier::d:
80 case UnitMultiplier::k:
83 case UnitMultiplier::M:
86 case UnitMultiplier::G:
89 case UnitMultiplier::T:
98TopologicalPowerComp::Ptr Reader::mapComponent(BaseClass *obj) {
99 if (CIMPP::ACLineSegment *line =
dynamic_cast<CIMPP::ACLineSegment *
>(obj))
100 return mapACLineSegment(line);
101 if (CIMPP::EnergyConsumer *consumer =
102 dynamic_cast<CIMPP::EnergyConsumer *
>(obj))
103 return mapEnergyConsumer(consumer);
104 if (CIMPP::PowerTransformer *trans =
105 dynamic_cast<CIMPP::PowerTransformer *
>(obj))
106 return mapPowerTransformer(trans);
107 if (CIMPP::SynchronousMachine *syncMachine =
108 dynamic_cast<CIMPP::SynchronousMachine *
>(obj))
109 return mapSynchronousMachine(syncMachine);
110 if (CIMPP::ExternalNetworkInjection *extnet =
111 dynamic_cast<CIMPP::ExternalNetworkInjection *
>(obj))
112 return mapExternalNetworkInjection(extnet);
113 if (CIMPP::EquivalentShunt *shunt =
114 dynamic_cast<CIMPP::EquivalentShunt *
>(obj))
115 return mapEquivalentShunt(shunt);
120void Reader::addFiles(
const fs::path &filename) {
121 if (!mModel->addCIMFile(filename.string()))
122 SPDLOG_LOGGER_ERROR(mSLog,
"Failed to read file {}", filename);
125void Reader::addFiles(
const std::list<fs::path> &filenames) {
126 for (
auto filename : filenames)
130void Reader::parseFiles() {
132 mModel->parseFiles();
134 SPDLOG_LOGGER_ERROR(mSLog,
"Failed to parse CIM files");
140 "#### List of TopologicalNodes, associated Terminals and Equipment");
141 for (
auto obj : mModel->Objects) {
142 if (CIMPP::TopologicalNode *topNode =
143 dynamic_cast<CIMPP::TopologicalNode *
>(obj)) {
144 if (mDomain == Domain::EMT)
145 processTopologicalNode<Real>(topNode);
147 processTopologicalNode<Complex>(topNode);
153 SPDLOG_LOGGER_INFO(mSLog,
154 "#### List of Node voltages and Terminal power flow data");
155 for (
auto obj : mModel->Objects) {
157 if (CIMPP::SvVoltage *volt =
dynamic_cast<CIMPP::SvVoltage *
>(obj)) {
158 processSvVoltage(volt);
161 else if (CIMPP::SvPowerFlow *flow =
162 dynamic_cast<CIMPP::SvPowerFlow *
>(obj)) {
163 processSvPowerFlow(flow);
167 SPDLOG_LOGGER_INFO(mSLog,
"#### Create other components");
168 for (
auto obj : mModel->Objects) {
171 if (!
dynamic_cast<CIMPP::TopologicalNode *
>(obj) &&
172 !
dynamic_cast<CIMPP::SvVoltage *
>(obj) &&
173 !
dynamic_cast<CIMPP::SvPowerFlow *
>(obj)) {
175 if (CIMPP::IdentifiedObject *idObj =
176 dynamic_cast<CIMPP::IdentifiedObject *
>(obj)) {
179 if (mPowerflowEquipment.find(idObj->mRID) ==
180 mPowerflowEquipment.end()) {
181 TopologicalPowerComp::Ptr comp = mapComponent(obj);
183 mPowerflowEquipment.insert(std::make_pair(comp->uid(), comp));
189 SPDLOG_LOGGER_INFO(mSLog,
"#### Check topology for unconnected components");
190 for (
auto pfe : mPowerflowEquipment) {
193 if (mDomain == Domain::EMT) {
194 if (SimPowerComp<Real>::Ptr powercomp =
195 std::dynamic_pointer_cast<SimPowerComp<Real>>(c)) {
196 if (powercomp->terminalNumberConnected() < powercomp->terminalNumber())
197 throw InvalidTopology();
200 if (SimPowerComp<Complex>::Ptr powercomp =
201 std::dynamic_pointer_cast<SimPowerComp<Complex>>(c)) {
202 if (powercomp->terminalNumberConnected() < powercomp->terminalNumber())
203 throw InvalidTopology();
210 Domain domain, PhaseType phase,
211 GeneratorType genType) {
212 mFrequency = systemFrequency;
213 mOmega = 2 * PI * mFrequency;
216 mGeneratorType = genType;
219 return systemTopology();
223 const std::list<fs::path> &filenames,
224 Domain domain, PhaseType phase,
225 GeneratorType genType) {
226 mFrequency = systemFrequency;
227 mOmega = 2 * PI * mFrequency;
230 mGeneratorType = genType;
233 return systemTopology();
236void Reader::processSvVoltage(CIMPP::SvVoltage *volt) {
237 CIMPP::TopologicalNode *node = volt->TopologicalNode;
240 mSLog,
"SvVoltage references missing Topological Node, ignoring");
243 auto search = mPowerflowNodes.find(node->mRID);
244 if (search == mPowerflowNodes.end()) {
245 SPDLOG_LOGGER_WARN(mSLog,
246 "SvVoltage references Topological Node {}"
247 " missing from mTopNodes, ignoring",
252 Real voltageAbs = Reader::unitValue(volt->v.value, UnitMultiplier::k);
255 SPDLOG_LOGGER_INFO(mSLog,
" Angle={}", (
float)volt->angle.value);
256 }
catch (ReadingUninitializedField *e) {
257 volt->angle.value = 0;
258 std::cerr <<
"Uninitialized Angle for SVVoltage at "
259 << volt->TopologicalNode->name <<
".Setting default value of "
260 << volt->angle.value << std::endl;
262 Real voltagePhase = volt->angle.value * PI / 180;
263 mPowerflowNodes[node->mRID]->setInitialVoltage(
264 std::polar<Real>(voltageAbs, voltagePhase));
267 mSLog,
"Node {} MatrixNodeIndex {}: {} V, {} deg",
268 mPowerflowNodes[node->mRID]->uid(),
269 mPowerflowNodes[node->mRID]->matrixNodeIndex(),
270 std::abs(mPowerflowNodes[node->mRID]->initialSingleVoltage()),
271 std::arg(mPowerflowNodes[node->mRID]->initialSingleVoltage()) * 180 / PI);
274void Reader::processSvPowerFlow(CIMPP::SvPowerFlow *flow) {
275 CIMPP::Terminal *term = flow->Terminal;
277 mPowerflowTerminals[term->mRID]->setPower(
278 Complex(Reader::unitValue(flow->p.value, UnitMultiplier::M),
279 Reader::unitValue(flow->q.value, UnitMultiplier::M)));
281 SPDLOG_LOGGER_WARN(mSLog,
"Terminal {}: {} W + j {} Var", term->mRID,
282 mPowerflowTerminals[term->mRID]->singleActivePower(),
283 mPowerflowTerminals[term->mRID]->singleReactivePower());
286SystemTopology Reader::systemTopology() {
287 SystemTopology system(mFrequency);
289 for (
auto comp : mPowerflowEquipment) {
290 system.addComponent(comp.second);
292 if (SimPowerComp<Complex>::Ptr powercomp =
293 std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp.second)) {
294 for (
auto term : powercomp->topologicalTerminals()) {
295 TopologicalNode::Ptr node = term->topologicalNodes();
297 if (system.mComponentsAtNode.find(node) ==
298 system.mComponentsAtNode.end()) {
299 TopologicalPowerComp::List complist;
300 complist.push_back(powercomp);
301 system.mComponentsAtNode.insert(std::make_pair(node, complist));
303 system.mComponentsAtNode[node].push_back(powercomp);
309 system.mNodes.resize(mPowerflowNodes.size());
311 for (
auto node : mPowerflowNodes) {
314 system.addNodeAt(node.second, node.second->matrixNodeIndex());
320Matrix::Index Reader::mapTopologicalNode(String mrid) {
321 auto search = mPowerflowNodes.find(mrid);
322 if (search == mPowerflowNodes.end()) {
325 return search->second->matrixNodeIndex();
328TopologicalPowerComp::Ptr
329Reader::mapEnergyConsumer(CIMPP::EnergyConsumer *consumer) {
330 SPDLOG_LOGGER_INFO(mSLog,
" Found EnergyConsumer {}", consumer->name);
331 if (mDomain == Domain::EMT) {
332 if (mPhase == PhaseType::ABC) {
333 return std::make_shared<EMT::Ph3::RXLoad>(consumer->mRID, consumer->name,
336 SPDLOG_LOGGER_INFO(mSLog,
" RXLoad for EMT not implemented yet");
337 return std::make_shared<DP::Ph1::RXLoad>(consumer->mRID, consumer->name,
340 }
else if (mDomain == Domain::SP) {
341 auto load = std::make_shared<SP::Ph1::Load>(consumer->mRID, consumer->name,
345 load->modifyPowerFlowBusType(
350 if (mUseProtectionSwitches)
351 return std::make_shared<DP::Ph1::RXLoadSwitch>(
352 consumer->mRID, consumer->name, mComponentLogLevel);
354 return std::make_shared<DP::Ph1::RXLoad>(consumer->mRID, consumer->name,
359TopologicalPowerComp::Ptr Reader::mapACLineSegment(CIMPP::ACLineSegment *line) {
360 SPDLOG_LOGGER_INFO(mSLog,
361 " Found ACLineSegment {} r={} x={} bch={} gch={}",
362 line->name, (
float)line->r.value, (
float)line->x.value,
363 (
float)line->bch.value, (
float)line->gch.value);
365 Real resistance = line->r.value;
366 Real inductance = line->x.value / mOmega;
370 Real capacitance = mShuntCapacitorValue;
371 Real conductance = mShuntConductanceValue;
373 if (line->bch.value > 1e-9 && !mSetShuntCapacitor)
374 capacitance = Real(line->bch.value / mOmega);
376 if (line->gch.value > 1e-9 && !mSetShuntConductance)
377 conductance = Real(line->gch.value);
379 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(line);
381 if (mDomain == Domain::EMT) {
382 if (mPhase == PhaseType::ABC) {
389 auto cpsLine = std::make_shared<EMT::Ph3::PiLine>(line->mRID, line->name,
391 cpsLine->setParameters(res_3ph, ind_3ph, cap_3ph, cond_3ph);
394 SPDLOG_LOGGER_INFO(mSLog,
" PiLine for EMT not implemented yet");
395 auto cpsLine = std::make_shared<DP::Ph1::PiLine>(line->mRID, line->name,
397 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
400 }
else if (mDomain == Domain::SP) {
401 auto cpsLine = std::make_shared<SP::Ph1::PiLine>(line->mRID, line->name,
403 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
404 cpsLine->setBaseVoltage(baseVoltage);
407 auto cpsLine = std::make_shared<DP::Ph1::PiLine>(line->mRID, line->name,
409 cpsLine->setParameters(resistance, inductance, capacitance, conductance);
414TopologicalPowerComp::Ptr
415Reader::mapPowerTransformer(CIMPP::PowerTransformer *trans) {
416 if (trans->PowerTransformerEnd.size() != 2) {
419 "PowerTransformer {} does not have exactly two windings, ignoring",
423 SPDLOG_LOGGER_INFO(mSLog,
"Found PowerTransformer {}", trans->name);
426 CIMPP::PowerTransformerEnd *end1 =
nullptr, *end2 =
nullptr;
427 for (
auto end : trans->PowerTransformerEnd) {
428 if (end->Terminal->sequenceNumber == 1)
430 else if (end->Terminal->sequenceNumber == 2)
437 SPDLOG_LOGGER_INFO(mSLog,
" PowerTransformerEnd_1 {}", end1->name);
438 SPDLOG_LOGGER_INFO(mSLog,
" Srated={} Vrated={}",
439 (
float)end1->ratedS.value, (
float)end1->ratedU.value);
441 SPDLOG_LOGGER_INFO(mSLog,
" R={}", (
float)end1->r.value);
442 }
catch (ReadingUninitializedField *e1) {
443 end1->r.value = 1e-12;
444 SPDLOG_LOGGER_WARN(mSLog,
445 " Uninitialized value for PowerTrafoEnd1 setting "
446 "default value of R={}",
447 (
float)end1->r.value);
450 SPDLOG_LOGGER_INFO(mSLog,
" X={}", (
float)end1->x.value);
451 }
catch (ReadingUninitializedField *e1) {
452 end1->x.value = 1e-12;
453 SPDLOG_LOGGER_WARN(mSLog,
454 " Uninitialized value for PowerTrafoEnd1 setting "
455 "default value of X={}",
456 (
float)end1->x.value);
458 SPDLOG_LOGGER_INFO(mSLog,
" PowerTransformerEnd_2 {}", end2->name);
459 SPDLOG_LOGGER_INFO(mSLog,
" Srated={} Vrated={}",
460 (
float)end2->ratedS.value, (
float)end2->ratedU.value);
462 SPDLOG_LOGGER_INFO(mSLog,
" R={}", (
float)end2->r.value);
463 }
catch (ReadingUninitializedField *e1) {
464 end2->r.value = 1e-12;
465 SPDLOG_LOGGER_WARN(mSLog,
466 " Uninitialized value for PowerTrafoEnd2 setting "
467 "default value of R={}",
468 (
float)end2->r.value);
471 SPDLOG_LOGGER_INFO(mSLog,
" X={}", (
float)end2->x.value);
472 }
catch (ReadingUninitializedField *e1) {
473 end2->x.value = 1e-12;
474 SPDLOG_LOGGER_WARN(mSLog,
475 " Uninitialized value for PowerTrafoEnd2 setting "
476 "default value of X={}",
477 (
float)end2->x.value);
480 if (end1->ratedS.value != end2->ratedS.value) {
483 " PowerTransformerEnds of {} come with distinct rated power values. "
484 "Using rated power of PowerTransformerEnd_1.",
487 Real ratedPower = unitValue(end1->ratedS.value, UnitMultiplier::M);
488 Real voltageNode1 = unitValue(end1->ratedU.value, UnitMultiplier::k);
489 Real voltageNode2 = unitValue(end2->ratedU.value, UnitMultiplier::k);
491 Real ratioAbsNominal = voltageNode1 / voltageNode2;
492 Real ratioAbs = ratioAbsNominal;
495 if (end1->RatioTapChanger) {
497 voltageNode1 / voltageNode2 *
498 (1 + (end1->RatioTapChanger->normalStep -
499 end1->RatioTapChanger->neutralStep) *
500 end1->RatioTapChanger->stepVoltageIncrement.value / 100);
504 if (end1->RatioTapChanger) {
505 for (
auto obj : mModel->Objects) {
506 auto tapStep =
dynamic_cast<CIMPP::SvTapStep *
>(obj);
507 if (tapStep && tapStep->TapChanger == end1->RatioTapChanger) {
509 voltageNode1 / voltageNode2 *
510 (1 + (tapStep->position - end1->RatioTapChanger->neutralStep) *
511 end1->RatioTapChanger->stepVoltageIncrement.value / 100);
522 if (voltageNode1 >= voltageNode2 && abs(end1->x.value) > 1e-12) {
523 inductance = end1->x.value / mOmega;
524 resistance = end1->r.value;
525 }
else if (voltageNode1 >= voltageNode2 && abs(end2->x.value) > 1e-12) {
526 inductance = end2->x.value / mOmega * std::pow(ratioAbsNominal, 2);
527 resistance = end2->r.value * std::pow(ratioAbsNominal, 2);
528 }
else if (voltageNode2 > voltageNode1 && abs(end2->x.value) > 1e-12) {
529 inductance = end2->x.value / mOmega;
530 resistance = end2->r.value;
531 }
else if (voltageNode2 > voltageNode1 && abs(end1->x.value) > 1e-12) {
532 inductance = end1->x.value / mOmega / std::pow(ratioAbsNominal, 2);
533 resistance = end1->r.value / std::pow(ratioAbsNominal, 2);
536 if (mDomain == Domain::EMT) {
537 if (mPhase == PhaseType::ABC) {
538 Matrix resistance_3ph =
540 Matrix inductance_3ph =
542 Bool withResistiveLosses = resistance > 0;
543 auto transformer = std::make_shared<EMT::Ph3::Transformer>(
544 trans->mRID, trans->name, mComponentLogLevel, withResistiveLosses);
545 transformer->setParameters(voltageNode1, voltageNode2, ratedPower,
546 ratioAbs, ratioPhase, resistance_3ph,
550 SPDLOG_LOGGER_INFO(mSLog,
" Transformer for EMT not implemented yet");
553 }
else if (mDomain == Domain::SP) {
554 auto transformer = std::make_shared<SP::Ph1::Transformer>(
555 trans->mRID, trans->name, mComponentLogLevel);
556 transformer->setParameters(voltageNode1, voltageNode2, ratedPower, ratioAbs,
557 ratioPhase, resistance, inductance);
558 Real baseVolt = voltageNode1 >= voltageNode2 ? voltageNode1 : voltageNode2;
559 transformer->setBaseVoltage(baseVolt);
562 Bool withResistiveLosses = resistance > 0;
563 auto transformer = std::make_shared<DP::Ph1::Transformer>(
564 trans->mRID, trans->name, mComponentLogLevel, withResistiveLosses);
565 transformer->setParameters(voltageNode1, voltageNode2, ratedPower, ratioAbs,
566 ratioPhase, resistance, inductance);
571TopologicalPowerComp::Ptr
572Reader::mapSynchronousMachine(CIMPP::SynchronousMachine *machine) {
573 SPDLOG_LOGGER_INFO(mSLog,
" Found Synchronous machine {}", machine->name);
575 if (mDomain == Domain::DP) {
576 SPDLOG_LOGGER_INFO(mSLog,
" Create generator in DP domain.");
577 if (mGeneratorType == GeneratorType::TransientStability ||
578 mGeneratorType == GeneratorType::SG6aOrderVBR ||
579 mGeneratorType == GeneratorType::SG6bOrderVBR ||
580 mGeneratorType == GeneratorType::SG4OrderVBR ||
581 mGeneratorType == GeneratorType::SG3OrderVBR ||
582 mGeneratorType == GeneratorType::SG4OrderPCM ||
583 mGeneratorType == GeneratorType::SG4OrderTPM ||
584 mGeneratorType == GeneratorType::SG6OrderPCM) {
586 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
587 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
589 for (
auto obj : mModel->Objects) {
591 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
592 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *
>(
594 if (genDyn->SynchronousMachine->mRID == machine->mRID) {
596 Real Rs = genDyn->statorResistance.value;
597 Real Ll = genDyn->statorLeakageReactance.value;
600 Real Ld = genDyn->xDirectSync.value;
601 Real Lq = genDyn->xQuadSync.value;
602 Real Ld_t = genDyn->xDirectTrans.value;
603 Real Lq_t = genDyn->xQuadTrans.value;
604 Real Ld_s = genDyn->xDirectSubtrans.value;
605 Real Lq_s = genDyn->xQuadSubtrans.value;
608 Real Td0_t = genDyn->tpdo.value;
609 Real Tq0_t = genDyn->tpqo.value;
610 Real Td0_s = genDyn->tppdo.value;
611 Real Tq0_s = genDyn->tppqo.value;
614 Real H = genDyn->inertia.value;
618 Real nomFieldCurr = 0;
620 if (mGeneratorType == GeneratorType::TransientStability) {
621 SPDLOG_LOGGER_DEBUG(mSLog,
622 " GeneratorType is TransientStability.");
623 auto gen = DP::Ph1::SynchronGeneratorTrStab::make(
624 machine->mRID, machine->name, mComponentLogLevel);
625 gen->setStandardParametersPU(ratedPower, ratedVoltage, mFrequency,
628 }
else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
630 mSLog,
" GeneratorType is SynchronGenerator6aOrderVBR.");
631 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6aOrderVBR>(
632 machine->mRID, machine->name, mComponentLogLevel);
633 gen->setOperationalParametersPerUnit(
634 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
635 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
637 }
else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
639 mSLog,
" GeneratorType is SynchronGenerator6bOrderVBR.");
640 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6bOrderVBR>(
641 machine->mRID, machine->name, mComponentLogLevel);
642 gen->setOperationalParametersPerUnit(
643 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
644 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
646 }
else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
648 mSLog,
" GeneratorType is SynchronGenerator5OrderVBR.");
649 auto gen = std::make_shared<DP::Ph1::SynchronGenerator5OrderVBR>(
650 machine->mRID, machine->name, mComponentLogLevel);
651 gen->setOperationalParametersPerUnit(
652 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
653 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
655 }
else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
657 mSLog,
" GeneratorType is SynchronGenerator4OrderVBR.");
658 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderVBR>(
659 machine->mRID, machine->name, mComponentLogLevel);
660 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
661 mFrequency, H, Ld, Lq, Ll,
662 Ld_t, Lq_t, Td0_t, Tq0_t);
664 }
else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
666 mSLog,
" GeneratorType is SynchronGenerator3OrderVBR.");
667 auto gen = std::make_shared<DP::Ph1::SynchronGenerator3OrderVBR>(
668 machine->mRID, machine->name, mComponentLogLevel);
669 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
670 mFrequency, H, Ld, Lq, Ll,
673 }
else if (mGeneratorType == GeneratorType::SG4OrderPCM) {
675 mSLog,
" GeneratorType is SynchronGenerator4OrderPCM.");
676 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderPCM>(
677 machine->mRID, machine->name, mComponentLogLevel);
678 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
679 mFrequency, H, Ld, Lq, Ll,
680 Ld_t, Lq_t, Td0_t, Tq0_t);
682 }
else if (mGeneratorType == GeneratorType::SG4OrderTPM) {
684 mSLog,
" GeneratorType is SynchronGenerator4OrderTPM.");
685 auto gen = std::make_shared<DP::Ph1::SynchronGenerator4OrderTPM>(
686 machine->mRID, machine->name, mComponentLogLevel);
687 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
688 mFrequency, H, Ld, Lq, Ll,
689 Ld_t, Lq_t, Td0_t, Tq0_t);
691 }
else if (mGeneratorType == GeneratorType::SG6OrderPCM) {
693 mSLog,
" GeneratorType is SynchronGenerator6OrderPCM.");
694 auto gen = std::make_shared<DP::Ph1::SynchronGenerator6OrderPCM>(
695 machine->mRID, machine->name, mComponentLogLevel);
696 gen->setOperationalParametersPerUnit(
697 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
698 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
704 }
else if (mGeneratorType == GeneratorType::IdealVoltageSource) {
705 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is IdealVoltageSource.");
706 return std::make_shared<DP::Ph1::SynchronGeneratorIdeal>(
707 machine->mRID, machine->name, mComponentLogLevel);
708 }
else if (mGeneratorType == GeneratorType::None) {
709 throw SystemError(
"GeneratorType is None. Specify!");
711 throw SystemError(
"GeneratorType setting unfeasible.");
713 }
else if (mDomain == Domain::SP) {
714 SPDLOG_LOGGER_INFO(mSLog,
" Create generator in SP domain.");
715 if (mGeneratorType == GeneratorType::TransientStability ||
716 mGeneratorType == GeneratorType::SG6aOrderVBR ||
717 mGeneratorType == GeneratorType::SG6bOrderVBR ||
718 mGeneratorType == GeneratorType::SG5OrderVBR ||
719 mGeneratorType == GeneratorType::SG4OrderVBR ||
720 mGeneratorType == GeneratorType::SG3OrderVBR) {
722 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
723 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
725 for (
auto obj : mModel->Objects) {
727 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
728 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *
>(
730 if (genDyn->SynchronousMachine->mRID == machine->mRID) {
732 Real Rs = genDyn->statorResistance.value;
733 Real Ll = genDyn->statorLeakageReactance.value;
736 Real Ld = genDyn->xDirectSync.value;
737 Real Lq = genDyn->xQuadSync.value;
738 Real Ld_t = genDyn->xDirectTrans.value;
739 Real Lq_t = genDyn->xQuadTrans.value;
740 Real Ld_s = genDyn->xDirectSubtrans.value;
741 Real Lq_s = genDyn->xQuadSubtrans.value;
744 Real Td0_t = genDyn->tpdo.value;
745 Real Tq0_t = genDyn->tpqo.value;
746 Real Td0_s = genDyn->tppdo.value;
747 Real Tq0_s = genDyn->tppqo.value;
750 Real H = genDyn->inertia.value;
754 Real nomFieldCurr = 0;
756 if (mGeneratorType == GeneratorType::TransientStability) {
757 SPDLOG_LOGGER_DEBUG(mSLog,
758 " GeneratorType is TransientStability.");
759 auto gen = SP::Ph1::SynchronGeneratorTrStab::make(
760 machine->mRID, machine->name, mComponentLogLevel);
761 gen->setStandardParametersPU(ratedPower, ratedVoltage, mFrequency,
764 }
else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
766 mSLog,
" GeneratorType is SynchronGenerator6aOrderVBR.");
767 auto gen = std::make_shared<SP::Ph1::SynchronGenerator6aOrderVBR>(
768 machine->mRID, machine->name, mComponentLogLevel);
769 gen->setOperationalParametersPerUnit(
770 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
771 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
773 }
else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
775 mSLog,
" GeneratorType is SynchronGenerator6bOrderVBR.");
776 auto gen = std::make_shared<SP::Ph1::SynchronGenerator6bOrderVBR>(
777 machine->mRID, machine->name, mComponentLogLevel);
778 gen->setOperationalParametersPerUnit(
779 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
780 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
782 }
else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
784 mSLog,
" GeneratorType is SynchronGenerator5OrderVBR.");
785 auto gen = std::make_shared<SP::Ph1::SynchronGenerator5OrderVBR>(
786 machine->mRID, machine->name, mComponentLogLevel);
787 gen->setOperationalParametersPerUnit(
788 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
789 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
791 }
else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
793 mSLog,
" GeneratorType is SynchronGenerator4OrderVBR.");
794 auto gen = std::make_shared<SP::Ph1::SynchronGenerator4OrderVBR>(
795 machine->mRID, machine->name, mComponentLogLevel);
796 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
797 mFrequency, H, Ld, Lq, Ll,
798 Ld_t, Lq_t, Td0_t, Tq0_t);
800 }
else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
802 mSLog,
" GeneratorType is SynchronGenerator3OrderVBR.");
803 auto gen = std::make_shared<SP::Ph1::SynchronGenerator3OrderVBR>(
804 machine->mRID, machine->name, mComponentLogLevel);
805 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
806 mFrequency, H, Ld, Lq, Ll,
813 }
else if (mGeneratorType == GeneratorType::PVNode) {
814 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is PVNode.");
815 for (
auto obj : mModel->Objects) {
816 if (CIMPP::GeneratingUnit *genUnit =
817 dynamic_cast<CIMPP::GeneratingUnit *
>(obj)) {
818 for (
auto syncGen : genUnit->RotatingMachine) {
819 if (syncGen->mRID == machine->mRID) {
821 Real setPointActivePower = 0;
822 Real setPointVoltage = 0;
823 Real maximumReactivePower = 1e12;
825 setPointActivePower =
826 unitValue(genUnit->initialP.value, UnitMultiplier::M);
827 SPDLOG_LOGGER_INFO(mSLog,
" setPointActivePower={}",
828 setPointActivePower);
829 }
catch (ReadingUninitializedField *e) {
831 <<
"Uninitalized setPointActivePower for GeneratingUnit "
832 << machine->name <<
". Using default value of "
833 << setPointActivePower << std::endl;
835 if (machine->RegulatingControl) {
837 unitValue(machine->RegulatingControl->targetValue.value,
839 SPDLOG_LOGGER_INFO(mSLog,
" setPointVoltage={}",
842 std::cerr <<
"Uninitalized setPointVoltage for GeneratingUnit "
843 << machine->name <<
". Using default value of "
844 << setPointVoltage << std::endl;
847 maximumReactivePower =
848 unitValue(machine->maxQ.value, UnitMultiplier::M);
849 SPDLOG_LOGGER_INFO(mSLog,
" maximumReactivePower={}",
850 maximumReactivePower);
851 }
catch (ReadingUninitializedField *e) {
853 <<
"Uninitalized maximumReactivePower for GeneratingUnit "
854 << machine->name <<
". Using default value of "
855 << maximumReactivePower << std::endl;
858 auto gen = std::make_shared<SP::Ph1::SynchronGenerator>(
859 machine->mRID, machine->name, mComponentLogLevel);
861 unitValue(machine->ratedS.value, UnitMultiplier::M),
862 unitValue(machine->ratedU.value, UnitMultiplier::k),
863 setPointActivePower, setPointVoltage, PowerflowBusType::PV);
865 unitValue(machine->ratedU.value, UnitMultiplier::k));
871 SPDLOG_LOGGER_INFO(mSLog,
"no corresponding initial power for {}",
873 return std::make_shared<SP::Ph1::SynchronGenerator>(
874 machine->mRID, machine->name, mComponentLogLevel);
875 }
else if (mGeneratorType == GeneratorType::None) {
876 throw SystemError(
"GeneratorType is None. Specify!");
878 throw SystemError(
"GeneratorType setting unfeasible.");
881 SPDLOG_LOGGER_INFO(mSLog,
" Create generator in EMT domain.");
882 if (mGeneratorType == GeneratorType::FullOrder ||
883 mGeneratorType == GeneratorType::FullOrderVBR ||
884 mGeneratorType == GeneratorType::SG3OrderVBR ||
885 mGeneratorType == GeneratorType::SG4OrderVBR ||
886 mGeneratorType == GeneratorType::SG6aOrderVBR ||
887 mGeneratorType == GeneratorType::SG6bOrderVBR) {
889 Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M);
890 Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k);
892 for (
auto obj : mModel->Objects) {
894 if (CIMPP::SynchronousMachineTimeConstantReactance *genDyn =
895 dynamic_cast<CIMPP::SynchronousMachineTimeConstantReactance *
>(
897 if (genDyn->SynchronousMachine->mRID == machine->mRID) {
900 Real Rs = genDyn->statorResistance.value;
901 Real Ll = genDyn->statorLeakageReactance.value;
904 Real Ld = genDyn->xDirectSync.value;
905 Real Lq = genDyn->xQuadSync.value;
906 Real Ld_t = genDyn->xDirectTrans.value;
907 Real Lq_t = genDyn->xQuadTrans.value;
908 Real Ld_s = genDyn->xDirectSubtrans.value;
909 Real Lq_s = genDyn->xQuadSubtrans.value;
912 Real Td0_t = genDyn->tpdo.value;
913 Real Tq0_t = genDyn->tpqo.value;
914 Real Td0_s = genDyn->tppdo.value;
915 Real Tq0_s = genDyn->tppqo.value;
918 Real H = genDyn->inertia.value;
922 Real nomFieldCurr = 0;
924 if (mGeneratorType == GeneratorType::FullOrder) {
925 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is FullOrder.");
926 auto gen = std::make_shared<EMT::Ph3::SynchronGeneratorDQTrapez>(
927 machine->mRID, machine->name, mComponentLogLevel);
928 gen->setParametersOperationalPerUnit(
929 ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr,
930 Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, Td0_t, Tq0_t, Td0_s,
933 }
else if (mGeneratorType == GeneratorType::FullOrderVBR) {
934 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is FullOrderVBR.");
935 auto gen = std::make_shared<EMT::Ph3::SynchronGeneratorVBR>(
936 machine->mRID, machine->name, mComponentLogLevel);
937 gen->setBaseAndOperationalPerUnitParameters(
938 ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr,
939 Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, Td0_t, Tq0_t, Td0_s,
942 }
else if (mGeneratorType == GeneratorType::SG6aOrderVBR) {
944 mSLog,
" GeneratorType is SynchronGenerator6aOrderVBR.");
946 std::make_shared<EMT::Ph3::SynchronGenerator6aOrderVBR>(
947 machine->mRID, machine->name, mComponentLogLevel);
948 gen->setOperationalParametersPerUnit(
949 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
950 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
952 }
else if (mGeneratorType == GeneratorType::SG6bOrderVBR) {
954 mSLog,
" GeneratorType is SynchronGenerator6bOrderVBR.");
956 std::make_shared<EMT::Ph3::SynchronGenerator6bOrderVBR>(
957 machine->mRID, machine->name, mComponentLogLevel);
958 gen->setOperationalParametersPerUnit(
959 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
960 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s);
962 }
else if (mGeneratorType == GeneratorType::SG5OrderVBR) {
964 mSLog,
" GeneratorType is SynchronGenerator5OrderVBR.");
965 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator5OrderVBR>(
966 machine->mRID, machine->name, mComponentLogLevel);
967 gen->setOperationalParametersPerUnit(
968 ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t,
969 Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s, 0.0);
971 }
else if (mGeneratorType == GeneratorType::SG4OrderVBR) {
973 mSLog,
" GeneratorType is SynchronGenerator4OrderVBR.");
974 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator4OrderVBR>(
975 machine->mRID, machine->name, mComponentLogLevel);
976 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
977 mFrequency, H, Ld, Lq, Ll,
978 Ld_t, Lq_t, Td0_t, Tq0_t);
980 }
else if (mGeneratorType == GeneratorType::SG3OrderVBR) {
982 mSLog,
" GeneratorType is SynchronGenerator3OrderVBR.");
983 auto gen = std::make_shared<EMT::Ph3::SynchronGenerator3OrderVBR>(
984 machine->mRID, machine->name, mComponentLogLevel);
985 gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage,
986 mFrequency, H, Ld, Lq, Ll,
993 }
else if (mGeneratorType == GeneratorType::IdealVoltageSource) {
994 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is IdealVoltageSource.");
995 return std::make_shared<EMT::Ph3::SynchronGeneratorIdeal>(
996 machine->mRID, machine->name, mComponentLogLevel,
997 GeneratorType::IdealVoltageSource);
998 }
else if (mGeneratorType == GeneratorType::IdealCurrentSource) {
999 SPDLOG_LOGGER_DEBUG(mSLog,
" GeneratorType is IdealCurrentSource.");
1000 return std::make_shared<EMT::Ph3::SynchronGeneratorIdeal>(
1001 machine->mRID, machine->name, mComponentLogLevel,
1002 GeneratorType::IdealCurrentSource);
1003 }
else if (mGeneratorType == GeneratorType::None) {
1004 throw SystemError(
"GeneratorType is None. Specify!");
1006 throw SystemError(
"GeneratorType setting unfeasible.");
1012TopologicalPowerComp::Ptr
1013Reader::mapExternalNetworkInjection(CIMPP::ExternalNetworkInjection *extnet) {
1014 SPDLOG_LOGGER_INFO(mSLog,
"Found External Network Injection {}",
1017 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(extnet);
1019 if (mDomain == Domain::EMT) {
1020 if (mPhase == PhaseType::ABC) {
1021 return std::make_shared<EMT::Ph3::NetworkInjection>(
1022 extnet->mRID, extnet->name, mComponentLogLevel);
1025 "Mapping of ExternalNetworkInjection for EMT::Ph1 not existent!");
1028 }
else if (mDomain == Domain::SP) {
1029 if (mPhase == PhaseType::Single) {
1030 auto cpsextnet = std::make_shared<SP::Ph1::NetworkInjection>(
1031 extnet->mRID, extnet->name, mComponentLogLevel);
1032 cpsextnet->modifyPowerFlowBusType(
1035 cpsextnet->setBaseVoltage(baseVoltage);
1038 if (extnet->RegulatingControl) {
1039 SPDLOG_LOGGER_INFO(mSLog,
" Voltage set-point={}",
1040 (
float)extnet->RegulatingControl->targetValue);
1041 cpsextnet->setParameters(
1042 extnet->RegulatingControl->targetValue *
1046 mSLog,
" No voltage set-point defined. Using 1 per unit.");
1047 cpsextnet->setParameters(1. * baseVoltage);
1049 }
catch (ReadingUninitializedField *e) {
1050 std::cerr <<
"Ignore incomplete RegulatingControl" << std::endl;
1056 "Mapping of ExternalNetworkInjection for SP::Ph3 not existent!");
1060 if (mPhase == PhaseType::Single) {
1061 return std::make_shared<DP::Ph1::NetworkInjection>(
1062 extnet->mRID, extnet->name, mComponentLogLevel);
1065 "Mapping of ExternalNetworkInjection for DP::Ph3 not existent!");
1071TopologicalPowerComp::Ptr
1072Reader::mapEquivalentShunt(CIMPP::EquivalentShunt *shunt) {
1073 SPDLOG_LOGGER_INFO(mSLog,
"Found shunt {}", shunt->name);
1075 Real baseVoltage = determineBaseVoltageAssociatedWithEquipment(shunt);
1077 auto cpsShunt = std::make_shared<SP::Ph1::Shunt>(shunt->mRID, shunt->name,
1078 mComponentLogLevel);
1079 cpsShunt->setParameters(shunt->g.value, shunt->b.value);
1080 cpsShunt->setBaseVoltage(baseVoltage);
1084Real Reader::determineBaseVoltageAssociatedWithEquipment(
1085 CIMPP::ConductingEquipment *equipment) {
1086 Real baseVoltage = 0;
1089 for (
auto obj : mModel->Objects) {
1090 if (CIMPP::BaseVoltage *baseVolt =
1091 dynamic_cast<CIMPP::BaseVoltage *
>(obj)) {
1092 for (
auto comp : baseVolt->ConductingEquipment) {
1093 if (comp->name == equipment->name) {
1095 unitValue(baseVolt->nominalVoltage.value, UnitMultiplier::k);
1101 if (baseVoltage == 0) {
1102 for (
auto obj : mModel->Objects) {
1103 if (CIMPP::TopologicalNode *topNode =
1104 dynamic_cast<CIMPP::TopologicalNode *
>(obj)) {
1105 for (
auto term : topNode->Terminal) {
1106 if (term->ConductingEquipment->name == equipment->name) {
1107 baseVoltage = unitValue(topNode->BaseVoltage->nominalVoltage.value,
1118template <
typename VarType>
1119void Reader::processTopologicalNode(CIMPP::TopologicalNode *topNode) {
1121 int matrixNodeIndex = Int(mPowerflowNodes.size());
1122 mPowerflowNodes[topNode->mRID] = SimNode<VarType>::make(
1123 topNode->mRID, topNode->name, matrixNodeIndex, mPhase);
1125 if (mPhase == PhaseType::ABC) {
1127 mSLog,
"TopologicalNode {} phase A as simulation node {} ",
1129 mPowerflowNodes[topNode->mRID]->matrixNodeIndex(PhaseType::A));
1131 mSLog,
"TopologicalNode {} phase B as simulation node {}",
1133 mPowerflowNodes[topNode->mRID]->matrixNodeIndex(PhaseType::B));
1135 mSLog,
"TopologicalNode {} phase C as simulation node {}",
1137 mPowerflowNodes[topNode->mRID]->matrixNodeIndex(PhaseType::C));
1139 SPDLOG_LOGGER_INFO(mSLog,
1140 "TopologicalNode id: {}, name: {} as simulation node {}",
1141 topNode->mRID, topNode->name,
1142 mPowerflowNodes[topNode->mRID]->matrixNodeIndex());
1144 for (
auto term : topNode->Terminal) {
1147 auto cpsTerm = SimTerminal<VarType>::make(term->mRID);
1148 mPowerflowTerminals.insert(std::make_pair(term->mRID, cpsTerm));
1149 cpsTerm->setNode(std::dynamic_pointer_cast<SimNode<VarType>>(
1150 mPowerflowNodes[topNode->mRID]));
1152 if (!term->sequenceNumber.initialized)
1153 term->sequenceNumber = 1;
1155 SPDLOG_LOGGER_INFO(mSLog,
" Terminal {}, sequenceNumber {}", term->mRID,
1156 (
int)term->sequenceNumber);
1159 CIMPP::ConductingEquipment *equipment = term->ConductingEquipment;
1161 SPDLOG_LOGGER_WARN(mSLog,
"Terminal {} has no Equipment, ignoring!",
1166 if (mPowerflowEquipment.find(equipment->mRID) ==
1167 mPowerflowEquipment.end()) {
1168 TopologicalPowerComp::Ptr comp = mapComponent(equipment);
1170 mPowerflowEquipment.insert(std::make_pair(equipment->mRID, comp));
1172 SPDLOG_LOGGER_WARN(mSLog,
"Could not map equipment {}",
1178 auto pfEquipment = mPowerflowEquipment.at(equipment->mRID);
1179 if (pfEquipment ==
nullptr) {
1180 SPDLOG_LOGGER_ERROR(mSLog,
"Equipment {} is null in equipment list",
1182 throw SystemError(
"Equipment is null in equipment list.");
1184 std::dynamic_pointer_cast<SimPowerComp<VarType>>(pfEquipment)
1185 ->setTerminalAt(std::dynamic_pointer_cast<SimTerminal<VarType>>(
1186 mPowerflowTerminals[term->mRID]),
1187 term->sequenceNumber - 1);
1189 SPDLOG_LOGGER_INFO(mSLog,
" Added Terminal {} to Equipment {}",
1190 term->mRID, equipment->mRID);
1196Reader::processTopologicalNode<Real>(CIMPP::TopologicalNode *topNode);
1198Reader::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.