18#include "../elements/controlElement/ControlElementSolver.h"
21Electromechanical::Electromechanical(wxWindow* parent, std::vector<Element*> elementList,
SimulationData data)
29 m_powerSystemBase = dummyBus.GetValueFromUnit(data.basePower, data.basePowerUnit);
30 m_systemFreq = data.stabilityFrequency;
31 m_simTime = data.stabilitySimulationTime;
32 m_timeStep = data.timeStep;
33 m_tolerance = data.stabilityTolerance;
34 m_maxIterations = data.stabilityMaxIterations;
36 m_ctrlTimeStepMultiplier = 1.0 /
static_cast<double>(data.controlTimeStepRatio);
38 m_plotTime = data.plotTime;
39 m_useCOI = data.useCOI;
43 auto loadData = load->GetElectricalData();
44 if (!loadData.useCompLoad) {
45 if (data.useCompLoads) {
46 loadData.constImpedanceActive = data.constImpedanceActive;
47 loadData.constCurrentActive = data.constCurrentActive;
48 loadData.constPowerActive = data.constPowerActive;
49 loadData.constImpedanceReactive = data.constImpedanceReactive;
50 loadData.constCurrentReactive = data.constCurrentReactive;
51 loadData.constPowerReactive = data.constPowerReactive;
54 loadData.constImpedanceActive = 100.0;
55 loadData.constCurrentActive = 0.0;
56 loadData.constPowerActive = 0.0;
57 loadData.constImpedanceReactive = 100.0;
58 loadData.constCurrentReactive = 0.0;
59 loadData.constPowerReactive = 0.0;
63 loadData.constCurrentUV = data.underVoltageConstCurrent / 100.0;
64 loadData.constPowerUV = data.underVoltageConstPower / 100.0;
65 load->SetElectricalData(loadData);
69Electromechanical::~Electromechanical() { }
71bool Electromechanical::RunStabilityCalculation()
73 wxProgressDialog pbd(_(
"Running simulation"), _(
"Initializing..."), 100, m_parent,
74 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_SMOOTH);
76 SetSyncMachinesModel();
80 m_errorMsg = _(
"It was not possible to build the admittance matrix.");
83 InsertSyncMachinesOnYBus();
84 if (!InsertIndMachinesOnYBus())
return false;
89 m_vBus.resize(m_yBus.size());
92 auto data = bus->GetElectricalData();
93 if (data.number >= 0) m_vBus[data.number] = data.voltage;
95 m_vBus.shrink_to_fit();
99 for (
unsigned int i = 0; i < m_iBus.size(); ++i) {
100 if (std::abs(m_iBus[i]) < 1e-5) m_iBus[i] = std::complex<double>(0.0, 0.0);
103 if (!InitializeDynamicElements())
return false;
104 PreallocateVectors();
108 auto data = syncGenerator->GetPUElectricalData(m_powerSystemBase);
109 m_debugMessage +=
"------\n";
110 m_debugMessage += data.name +
":\n";
111 m_debugMessage +=
"P = " + wxString::FromDouble(data.electricalPower.real(), 5) +
" p.u.\n";
112 m_debugMessage +=
"Q = " + wxString::FromDouble(data.electricalPower.imag(), 5) +
" p.u.\n";
113 m_debugMessage +=
"If0 = " + wxString::FromDouble(data.initialFieldVoltage, 5) +
" p.u.\n";
114 m_debugMessage +=
"If = " + wxString::FromDouble(data.fieldVoltage, 5) +
" p.u.\n";
115 m_debugMessage +=
"delta = " + wxString::FromDouble(data.delta, 5) +
" rad?\n";
116 m_debugMessage +=
"Vf = " + wxString::FromDouble(data.fieldVoltage, 5) +
" p.u.\n";
117 m_debugMessage +=
"Vf0 = " + wxString::FromDouble(data.initialFieldVoltage, 5) +
" p.u.\n";
118 m_debugMessage +=
"Vt = " + wxString::FromDouble(std::abs(data.terminalVoltage), 5) +
" p.u.\n";
119 m_debugMessage +=
"theta = " + wxString::FromDouble(std::arg(data.terminalVoltage), 5) +
" rad\n";
120 m_debugMessage +=
"Pe = " + wxString::FromDouble(data.pe, 5) +
" p.u.\n";
121 m_debugMessage +=
"Pm = " + wxString::FromDouble(data.pm, 5) +
" p.u.\n";
122 m_debugMessage +=
"w = " + wxString::FromDouble(data.speed, 5) +
" p.u.\n";
123 m_debugMessage +=
"Id = " + wxString::FromDouble(data.id, 5) +
" p.u.\n";
124 m_debugMessage +=
"Iq = " + wxString::FromDouble(data.iq, 5) +
" p.u.\n";
125 m_debugMessage +=
"Ed' = " + wxString::FromDouble(data.tranEd, 5) +
" p.u.\n";
126 m_debugMessage +=
"Eq' = " + wxString::FromDouble(data.tranEq, 5) +
" p.u.\n";
127 m_debugMessage +=
"Ed'' = " + wxString::FromDouble(data.subEd, 5) +
" p.u.\n";
128 m_debugMessage +=
"Eq'' = " + wxString::FromDouble(data.subEq, 5) +
" p.u.\n";
129 m_debugMessage +=
"------\n\n";
133 double pbdTime = m_plotTime;
135 double currentPlotTime = 0.0;
136 double currentPbdTime = 0.0;
137 while (m_currentTime <= m_simTime) {
138 bool hasEvent =
false;
139 if (HasEvent(m_currentTime)) {
141 SetEvent(m_currentTime);
145 if (currentPlotTime >= m_plotTime || m_currentTime == 0.0) {
146 m_timeVector.emplace_back(m_currentTime);
148 currentPlotTime = 0.0;
151 if (currentPbdTime > pbdTime) {
152 if (!pbd.Update((m_currentTime / m_simTime) * 100, wxString::Format(
"Time = %.2fs", m_currentTime))) {
153 m_errorMsg = wxString::Format(_(
"Simulation cancelled at %.2fs."), m_currentTime);
157 currentPbdTime = 0.0;
160 if (!SolveMachines())
return false;
162 CalculateBusesFrequency(hasEvent);
164 m_currentTime += m_timeStep;
165 currentPlotTime += m_timeStep;
166 currentPbdTime += m_timeStep;
172void Electromechanical::SetEventTimeList()
177 auto data = bus->GetElectricalData();
178 if (data.stabHasFault) {
179 m_eventTimeList.emplace_back(data.stabFaultTime);
180 m_eventOccurrenceList.push_back(
false);
181 m_eventTimeList.emplace_back(data.stabFaultTime + data.stabFaultLength);
182 m_eventOccurrenceList.push_back(
false);
189 for (
unsigned int i = 0; i < swData.
swTime.size(); ++i) {
190 m_eventTimeList.emplace_back(swData.
swTime[i]);
191 m_eventOccurrenceList.push_back(
false);
196bool Electromechanical::HasEvent(
double currentTime)
198 for (
unsigned int i = 0; i < m_eventTimeList.size(); ++i) {
199 if (!m_eventOccurrenceList[i]) {
200 if (EventTrigger(m_eventTimeList[i], currentTime)) {
201 m_eventOccurrenceList[i] =
true;
209void Electromechanical::SetEvent(
double currentTime)
214 auto data = bus->GetElectricalData();
216 if (data.stabHasFault && n >= 0) {
219 if (EventTrigger(data.stabFaultTime, currentTime)) {
221 r = data.stabFaultResistance;
222 x = data.stabFaultReactance;
223 if (x < 1e-5) x = 1e-5;
224 m_yBus[n][n] += std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
228 else if (EventTrigger(data.stabFaultTime + data.stabFaultLength, currentTime)) {
230 r = data.stabFaultResistance;
231 x = data.stabFaultReactance;
232 if (x < 1e-5) x = 1e-5;
233 m_yBus[n][n] -= std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
242 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
243 if (EventTrigger(swData.
swTime[i], currentTime)) {
247 int n =
static_cast<Bus*
>(generator->
GetParentList()[0])->GetElectricalData().number;
248 if (n >= 0) m_yBus[n][n] -= GetSyncMachineAdmittance(generator);
254 int n =
static_cast<Bus*
>(generator->
GetParentList()[0])->GetElectricalData().number;
255 if (n >= 0) m_yBus[n][n] += GetSyncMachineAdmittance(generator);
266 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
267 if (EventTrigger(swData.
swTime[i], currentTime)) {
270 auto data = motor->GetElectricalData();
272 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalData().number;
273 if (n >= 0) m_yBus[n][n] -= (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
278 auto data = motor->GetElectricalData();
280 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalData().number;
281 if (n >= 0) m_yBus[n][n] += (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
292 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
293 if (EventTrigger(swData.
swTime[i], currentTime)) {
297 auto data = load->GetPUElectricalData(m_powerSystemBase);
299 int n = parentBus->GetElectricalData().number;
300 std::complex<double> v = parentBus->GetElectricalData().voltage;
303 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
310 auto data = load->GetPUElectricalData(m_powerSystemBase);
312 int n = parentBus->GetElectricalData().number;
313 std::complex<double> v = parentBus->GetElectricalData().voltage;
316 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
328 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
329 if (EventTrigger(swData.
swTime[i], currentTime)) {
333 auto data = line->GetElectricalData();
335 int n1 =
static_cast<Bus*
>(line->
GetParentList()[0])->GetElectricalData().number;
336 int n2 =
static_cast<Bus*
>(line->
GetParentList()[1])->GetElectricalData().number;
337 if (n1 >= 0 && n2 >= 0) {
338 m_yBus[n1][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
339 m_yBus[n2][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
341 m_yBus[n1][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
342 m_yBus[n2][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
344 m_yBus[n1][n1] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
345 m_yBus[n2][n2] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
352 auto data = line->GetElectricalData();
354 int n1 =
static_cast<Bus*
>(line->
GetParentList()[0])->GetElectricalData().number;
355 int n2 =
static_cast<Bus*
>(line->
GetParentList()[1])->GetElectricalData().number;
356 if (n1 >= 0 && n2 >= 0) {
357 m_yBus[n1][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
358 m_yBus[n2][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
360 m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
361 m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
363 m_yBus[n1][n1] += std::complex<double>(0.0, data.capSusceptance / 2.0);
364 m_yBus[n2][n2] += std::complex<double>(0.0, data.capSusceptance / 2.0);
376 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
377 if (EventTrigger(swData.
swTime[i], currentTime)) {
381 auto data = transformer->GetElectricalData();
383 int n1 =
static_cast<Bus*
>(transformer->
GetParentList()[0])->GetElectricalData().number;
384 int n2 =
static_cast<Bus*
>(transformer->
GetParentList()[1])->GetElectricalData().number;
385 if (n1 >= 0 && n2 >= 0) {
386 if (data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
387 m_yBus[n1][n2] -= -1.0 / std::complex<double>(data.resistance, data.indReactance);
388 m_yBus[n2][n1] -= -1.0 / std::complex<double>(data.resistance, data.indReactance);
390 m_yBus[n1][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
391 m_yBus[n2][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
395 double radPhaseShift = wxDegToRad(data.phaseShift);
396 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
397 -data.turnsRatio * std::sin(radPhaseShift));
400 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
401 m_yBus[n1][n1] -= y / std::pow(std::abs(a), 2.0);
402 m_yBus[n1][n2] -= -(y / std::conj(a));
403 m_yBus[n2][n1] -= -(y / a);
413 auto data = transformer->GetElectricalData();
415 int n1 =
static_cast<Bus*
>(transformer->
GetParentList()[0])->GetElectricalData().number;
416 int n2 =
static_cast<Bus*
>(transformer->
GetParentList()[1])->GetElectricalData().number;
417 if (n1 >= 0 && n2 >= 0) {
418 if (data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
419 m_yBus[n1][n2] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
420 m_yBus[n2][n1] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
422 m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
423 m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
427 double radPhaseShift = wxDegToRad(data.phaseShift);
428 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
429 -data.turnsRatio * std::sin(radPhaseShift));
432 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
433 m_yBus[n1][n1] += y / std::pow(std::abs(a), 2.0);
434 m_yBus[n1][n2] += -(y / std::conj(a));
435 m_yBus[n2][n1] += -(y / a);
449 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
450 if (EventTrigger(swData.
swTime[i], currentTime)) {
454 auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
455 int n =
static_cast<Bus*
>(capacitor->
GetParentList()[0])->GetElectricalData().number;
456 if (n >= 0) m_yBus[n][n] -= std::complex<double>(0.0, data.reactivePower);
462 auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
463 int n =
static_cast<Bus*
>(capacitor->
GetParentList()[0])->GetElectricalData().number;
464 if (n >= 0) m_yBus[n][n] += std::complex<double>(0.0, data.reactivePower);
475 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
476 if (EventTrigger(swData.
swTime[i], currentTime)) {
480 auto data = inductor->GetPUElectricalData(m_powerSystemBase);
481 int n =
static_cast<Bus*
>(inductor->
GetParentList()[0])->GetElectricalData().number;
482 if (n >= 0) m_yBus[n][n] -= std::complex<double>(0.0, -data.reactivePower);
488 auto data = inductor->GetPUElectricalData(m_powerSystemBase);
489 int n =
static_cast<Bus*
>(inductor->
GetParentList()[0])->GetElectricalData().number;
490 if (n >= 0) m_yBus[n][n] += std::complex<double>(0.0, -data.reactivePower);
498void Electromechanical::InsertSyncMachinesOnYBus()
504 if (connBus->GetElectricalData().number < 0) {
513 auto data = syncGenerator->GetElectricalData();
514 int n = connBus->GetElectricalData().number;
515 m_yBus[n][n] += GetSyncMachineAdmittance(syncGenerator);
521bool Electromechanical::EventTrigger(
double eventTime,
double currentTime)
523 return (((eventTime - m_timeStep) < currentTime) && (eventTime >= currentTime));
526std::complex<double> Electromechanical::GetSyncMachineAdmittance(
SyncGenerator* generator)
528 auto data = generator->GetElectricalData();
530 if (data.useMachineBase) {
531 double oldBase = generator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
532 k = m_powerSystemBase / oldBase;
535 double ra = data.armResistance * k;
536 auto smModelData = GetSyncMachineModelData(generator);
537 double xd = smModelData.xd;
538 double xq = smModelData.xq;
539 double xdq = 0.5 * (xd + xq);
540 return (std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0));
543bool Electromechanical::InitializeDynamicElements()
548 auto data = bus->GetElectricalData();
549 data.stabVoltageVector.clear();
550 data.stabVoltageVector.shrink_to_fit();
551 data.stabFreqVector.clear();
552 data.stabFreqVector.shrink_to_fit();
553 data.stabFreq = m_systemFreq;
554 data.oldAngle = std::arg(data.voltage);
555 data.filteredAngle = 0.0;
557 data.filteredVelocity = 0.0;
559 bus->SetElectricalData(data);
563 auto dataPU = load->GetPUElectricalData(m_powerSystemBase);
564 auto data = load->GetElectricalData();
566 double activePower = dataPU.activePower;
567 double reactivePower = dataPU.reactivePower;
572 data.voltage = bus->GetElectricalData().voltage;
578 data.v0 = std::abs(data.voltage);
579 data.y0 = std::complex<double>(activePower, -reactivePower) / (data.v0 * data.v0);
581 if (data.loadType == CONST_IMPEDANCE) {
582 std::complex<double> s0 = std::complex<double>(activePower, -reactivePower) * (data.v0 * data.v0);
583 activePower = s0.real();
584 reactivePower = -s0.imag();
587 data.pz0 = (data.constImpedanceActive / 100.0) * activePower;
588 data.pi0 = (data.constCurrentActive / 100.0) * activePower;
589 data.pp0 = (data.constPowerActive / 100.0) * activePower;
591 data.qz0 = (data.constImpedanceReactive / 100.0) * reactivePower;
592 data.qi0 = (data.constCurrentReactive / 100.0) * reactivePower;
593 data.qp0 = (data.constPowerReactive / 100.0) * reactivePower;
595 data.voltageVector.clear();
596 data.voltageVector.shrink_to_fit();
597 data.electricalPowerVector.clear();
598 data.electricalPowerVector.shrink_to_fit();
601 data.electricalPower = std::complex<double>(activePower, reactivePower);
603 data.electricalPower = std::complex<double>(0.0, 0.0);
604 data.voltage = std::complex<double>(0.0, 0.0);
607 load->SetElectricalData(data);
612 auto dataPU = syncGenerator->GetPUElectricalData(m_powerSystemBase);
613 auto data = syncGenerator->GetElectricalData();
615 if (data.useMachineBase) {
616 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
617 k = m_powerSystemBase / oldBase;
621 data.terminalVoltage = bus->GetElectricalData().voltage;
623 data.terminalVoltage = std::complex<double>(1.0, 0.0);
625 std::complex<double> conjS(dataPU.activePower, -dataPU.reactivePower);
626 std::complex<double> vt = data.terminalVoltage;
627 std::complex<double> ia = conjS / std::conj(vt);
629 double xd = data.syncXd * k;
630 double xq = data.syncXq * k;
631 double ra = data.armResistance * k;
633 if (data.model == Machines::SM_MODEL_1) {
634 xq = data.transXd * k;
637 else if (data.syncXq == 0.0)
638 xq = data.syncXd * k;
643 double xp = data.potierReactance * k;
644 bool hasSaturation =
false;
645 if (data.satFactor != 0.0) {
646 satF = (data.satFactor - 1.2) / std::pow(1.2, 7);
647 if (xp == 0.0) xp = 0.8 * (data.transXd * k);
648 hasSaturation =
true;
652 std::complex<double> eq0 = vt + std::complex<double>(ra, xq) * ia;
653 double delta = std::arg(eq0);
655 double id0, iq0, vd0, vq0;
672 double epd = vd0 + ra * id0 + xp * iq0;
674 sq = 1.0 + satF * (xq / xd) * std::pow(epd, 6);
675 xqs = (xq - xp) / sq + xp;
676 eq0 = data.terminalVoltage + std::complex<double>(ra, xqs) * ia;
677 delta = std::arg(eq0);
678 if (std::abs(delta - oldDelta) < m_saturationTolerance) {
681 else if (numIt >= m_maxIterations) {
682 m_errorMsg = _(
"Error on initializate the saturation values of \"") + data.name + _(
"\".");
688 double epq = vq0 + ra * iq0 - xp * id0;
689 sd = 1.0 + satF * std::pow(epq, 6);
690 xds = (xd - xp) / sd + xp;
693 double ef0 = vq0 + ra * iq0 - xds * id0;
695 data.initialFieldVoltage = ef0 * sd;
696 data.fieldVoltage = data.initialFieldVoltage;
697 data.pm = std::real((data.terminalVoltage * std::conj(ia)) + (std::abs(ia) * std::abs(ia) * ra));
698 syncGenerator->
IsOnline() ? data.speed = 2.0 * M_PI * m_systemFreq : data.speed = 2.0 * M_PI * data.ocFrequency;
701 data.electricalPower = std::complex<double>(dataPU.activePower, dataPU.reactivePower);
702 if (!syncGenerator->
IsOnline()) data.electricalPower = std::complex<double>(0.0, 0.0);
711 data.oldPe = data.pe;
715 switch (data.model) {
716 case Machines::SM_MODEL_1: {
717 data.tranEq = std::abs(eq0);
723 case Machines::SM_MODEL_2: {
724 double tranXd = data.transXd * k;
726 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
731 case Machines::SM_MODEL_3: {
732 double tranXd = data.transXd * k;
733 double tranXq = data.transXq * k;
734 if (tranXq == 0.0) tranXq = tranXd;
736 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
737 data.tranEd = -(xq - tranXq) * (iq0 / sq);
742 case Machines::SM_MODEL_4: {
743 double tranXd = data.transXd * k;
744 double subXd = data.subXd * k;
745 double subXq = data.subXq * k;
746 if (subXd == 0.0) subXd = subXq;
747 if (subXq == 0.0) subXq = subXd;
749 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
751 data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
752 data.subEd = -(xq - subXq) * (iq0 / sq);
754 case Machines::SM_MODEL_5: {
755 double tranXd = data.transXd * k;
756 double tranXq = data.transXq * k;
757 double subXd = data.subXd * k;
758 double subXq = data.subXq * k;
759 if (subXd == 0.0) subXd = subXq;
760 if (subXq == 0.0) subXq = subXd;
762 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
763 data.tranEd = -(xq - tranXq) * (iq0 / sq);
764 data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
765 data.subEd = data.tranEd - (tranXq - subXq) * (iq0 / sq);
776 data.avrSolver = std::make_shared<ControlElementSolver>(data.avr, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
778 data.avrSolver->SetSwitchStatus(syncGenerator->
IsOnline());
779 data.avrSolver->SetCurrentTime(m_currentTime);
780 data.avrSolver->SetTerminalVoltage(std::abs(data.terminalVoltage));
781 data.avrSolver->SetInitialTerminalVoltage(std::abs(data.terminalVoltage));
782 data.avrSolver->SetActivePower(dataPU.activePower);
783 data.avrSolver->SetReactivePower(dataPU.reactivePower);
784 data.avrSolver->SetVelocity(data.speed);
785 data.avrSolver->SetInitialVelocity(data.speed);
786 data.avrSolver->InitializeValues(
false);
787 if (!data.avrSolver->IsOK()) {
788 m_errorMsg = _(
"Error on initializate the AVR of \"") + data.name + wxT(
"\".\n") +
789 data.avrSolver->GetErrorMessage();
790 syncGenerator->SetElectricalData(data);
794 if (data.useSpeedGovernor) {
799 data.speedGovSolver = std::make_shared<ControlElementSolver>(data.speedGov, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
800 data.speedGovSolver->SetSwitchStatus(syncGenerator->
IsOnline());
801 data.speedGovSolver->SetCurrentTime(m_currentTime);
802 data.speedGovSolver->SetActivePower(dataPU.activePower);
803 data.speedGovSolver->SetReactivePower(dataPU.reactivePower);
804 data.speedGovSolver->SetVelocity(data.speed);
805 data.speedGovSolver->SetInitialVelocity(data.speed);
806 data.speedGovSolver->SetInitialMecPower(data.pm);
807 data.speedGovSolver->InitializeValues(
false);
808 if (!data.speedGovSolver->IsOK()) {
809 m_errorMsg = _(
"Error on initializate the speed governor of \"") + data.name + wxT(
"\".\n") +
810 data.speedGovSolver->GetErrorMessage();
811 syncGenerator->SetElectricalData(data);
820 data.terminalVoltageVector.clear();
821 data.terminalVoltageVector.shrink_to_fit();
822 data.electricalPowerVector.clear();
823 data.electricalPowerVector.shrink_to_fit();
824 data.mechanicalPowerVector.clear();
825 data.mechanicalPowerVector.shrink_to_fit();
826 data.freqVector.clear();
827 data.freqVector.shrink_to_fit();
828 data.fieldVoltageVector.clear();
829 data.fieldVoltageVector.shrink_to_fit();
830 data.deltaVector.clear();
831 data.deltaVector.shrink_to_fit();
833 syncGenerator->SetElectricalData(data);
838 auto dataPU = indMotor->GetPUElectricalData(m_powerSystemBase);
839 auto data = indMotor->GetElectricalData();
842 if (connBus->GetElectricalData().number < 0) indMotor->
SetOnline(
false);
844 double w0 = 2.0 * M_PI * m_systemFreq;
845 std::complex<double> i1 = std::complex<double>(dataPU.activePower, -data.q0) / std::conj(data.terminalVoltage);
846 double ir = std::real(i1);
847 double im = std::imag(i1);
848 std::complex<double> e = data.terminalVoltage - std::complex<double>(data.r1t, data.x1t) * i1;
849 double te = std::real(e * std::conj(i1)) / w0;
851 double wi = w0 * (1 - data.s0);
852 data.as = te * (data.aw + (data.bw * w0) / wi + (data.cw * w0 * w0) / (wi * wi));
853 data.bs = te * ((data.bw * w0) / wi + (2.0 * data.cw * w0 * w0) / (wi * wi));
854 data.cs = (te * data.cw * w0 * w0) / (wi * wi);
856 data.aCalc = data.as;
857 data.bCalc = data.bs;
858 data.cCalc = data.cs;
861 std::complex<double> tranE =
862 (std::complex<double>(0, data.x0 - data.xt) * i1) / std::complex<double>(1.0, w0 * data.s0 * data.t0);
864 data.tranEr = std::real(tranE);
865 data.tranEm = std::imag(tranE);
875 data.slip = 1.0 - 1e-7;
882 data.oldTe = data.te;
883 data.oldIr = data.ir;
884 data.oldIm = data.im;
887 data.slipVector.clear();
888 data.slipVector.shrink_to_fit();
889 data.electricalTorqueVector.clear();
890 data.electricalTorqueVector.shrink_to_fit();
891 data.mechanicalTorqueVector.clear();
892 data.mechanicalTorqueVector.shrink_to_fit();
893 data.velocityVector.clear();
894 data.velocityVector.shrink_to_fit();
895 data.currentVector.clear();
896 data.currentVector.shrink_to_fit();
897 data.terminalVoltageVector.clear();
898 data.terminalVoltageVector.shrink_to_fit();
899 data.activePowerVector.clear();
900 data.activePowerVector.shrink_to_fit();
901 data.reactivePowerVector.clear();
902 data.reactivePowerVector.shrink_to_fit();
904 indMotor->SetElectricalData(data);
906 CalculateReferenceSpeed();
910bool Electromechanical::CalculateInjectedCurrents()
913 for (
unsigned int i = 0; i < m_iBus.size(); ++i) m_iBus[i] = std::complex<double>(0.0, 0.0);
918 auto data = syncGenerator->GetElectricalData();
921 if (data.useMachineBase) {
922 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
923 k = m_powerSystemBase / oldBase;
926 double ra = data.armResistance * k;
927 double xp = data.potierReactance * k;
928 if (xp == 0.0) xp = 0.8 * data.transXd * k;
930 int n =
static_cast<Bus*
>(syncGenerator->
GetParentList()[0])->GetElectricalData().number;
931 std::complex<double> e = std::complex<double>(0.0, 0.0);
932 std::complex<double> v = m_vBus[n];
933 std::complex<double> iInj = m_iBus[n];
935 auto smModelData = GetSyncMachineModelData(syncGenerator);
936 DQ0toABC(smModelData.ed, smModelData.eq, data.delta, e);
937 double xd = smModelData.xd;
938 double xq = smModelData.xq;
945 if (data.satFactor != 0.0) {
946 if (!CalculateSyncMachineSaturation(syncGenerator,
id, iq, sd, sq,
false, k))
return false;
949 double xdq, xds, xqs, xdqs;
950 xdq = 0.5 * (xd + xq);
951 xds = (xd - xp) / sd + xp;
952 xqs = (xq - xp) / sq + xp;
953 xdqs = 0.5 * (xds + xqs);
955 std::complex<double> y0 = std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0);
956 std::complex<double> iUnadjusted = y0 * v;
961 std::complex<double> iSaliency = std::complex<double>(0.0, -((0.5 * (xqs - xds)) / (ra * ra + xds * xqs))) *
962 (std::conj(e) - std::conj(v));
963 iSaliency = iSaliency * std::cos(2.0 * data.delta) +
964 iSaliency * std::complex<double>(0.0, std::sin(2.0 * data.delta));
967 std::complex<double> y0s = std::complex<double>(ra, -xdqs) / std::complex<double>(ra * ra + xds * xqs, 0.0);
968 std::complex<double> iSaturation = y0s * (e - v);
970 iInj = iUnadjusted + iSaliency + iSaturation;
976 std::complex<double> iMachine = iInj - iUnadjusted;
977 data.electricalPower = v * std::conj(iMachine);
979 ABCtoDQ0(iMachine, data.delta,
id, iq);
987 data.electricalPower = std::complex<double>(0.0, 0.0);
990 syncGenerator->SetElectricalData(data);
995 auto data = motor->GetElectricalData();
997 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalData().number;
998 std::complex<double> y0 = std::complex<double>(1.0, 0.0) / std::complex<double>(data.r1t, data.xt);
999 std::complex<double> v = m_vBus[n];
1000 std::complex<double> e = std::complex<double>(data.tranEr, data.tranEm);
1001 std::complex<double> iInj = y0 * e;
1004 std::complex<double> iMachine = y0 * (v - e);
1006 data.ir = std::real(iMachine);
1007 data.im = std::imag(iMachine);
1013 motor->SetElectricalData(data);
1019 auto data = load->GetElectricalData();
1026 data.voltage = m_vBus[bus->GetElectricalData().number];
1027 double vAbs = std::abs(data.voltage);
1029 double pz, pi, pp, qz, qi, qp;
1030 pz = data.pz0 * std::pow(vAbs / data.v0, 2);
1031 pi = data.pi0 * (vAbs / data.v0);
1033 qz = data.qz0 * std::pow(vAbs / data.v0, 2);
1034 qi = data.qi0 * (vAbs / data.v0);
1038 if (vAbs < data.constCurrentUV) {
1039 pi = data.pi0 * (data.constCurrentUV / data.v0) * std::pow(vAbs / data.constCurrentUV, 2);
1040 qi = data.qi0 * (data.constCurrentUV / data.v0) * std::pow(vAbs / data.constCurrentUV, 2);
1042 if (vAbs < data.constPowerUV) {
1043 pp *= std::pow(vAbs / data.constPowerUV, 2);
1044 qp *= std::pow(vAbs / data.constPowerUV, 2);
1047 double activePower = pz + pi + pp;
1048 double reactivePower = qz + qi + qp;
1050 std::complex<double> newY = std::complex<double>(activePower, -reactivePower) / (vAbs * vAbs);
1051 m_iBus[bus->GetElectricalData().number] += (data.y0 - newY) * data.voltage;
1053 data.electricalPower = std::complex<double>(activePower, reactivePower);
1056 data.voltage = std::complex<double>(0.0, 0.0);
1057 data.electricalPower = std::complex<double>(0.0, 0.0);
1060 load->SetElectricalData(data);
1065void Electromechanical::CalculateIntegrationConstants(
SyncGenerator* syncGenerator,
double id,
double iq,
double k)
1067 CalculateReferenceSpeed();
1068 auto data = syncGenerator->GetElectricalData();
1070 double syncXd, syncXq, transXd, transXq, subXd, subXq;
1071 syncXd = data.syncXd * k;
1072 syncXq = data.syncXq * k;
1073 transXd = data.transXd * k;
1074 transXq = data.transXq * k;
1075 subXd = data.subXd * k;
1076 subXq = data.subXq * k;
1078 if (syncXq == 0.0) syncXq = syncXd;
1079 if (transXq == 0.0) transXq = transXd;
1080 if (subXd == 0.0) subXd = subXq;
1081 if (subXq == 0.0) subXq = subXd;
1083 double transTd0, transTq0, subTd0, subTq0;
1084 transTd0 = data.transTd0;
1085 transTq0 = data.transTq0;
1086 subTd0 = data.subTd0;
1087 subTq0 = data.subTq0;
1089 if (subTd0 == 0.0) subTd0 = subTq0;
1090 if (subTq0 == 0.0) subTq0 = subTd0;
1093 data.icSpeed.m = m_timeStep / ((4.0f * data.inertia / m_refSpeed) / k + m_timeStep * data.damping * k);
1094 data.icSpeed.c = (1.0f - 2.0f * data.icSpeed.m * data.damping * k) * data.speed +
1095 data.icSpeed.m * (data.pm - data.pe + 2.0f * m_refSpeed * data.damping * k);
1098 data.icDelta.m = 0.5f * m_timeStep;
1099 data.icDelta.c = data.delta + data.icDelta.m * (data.speed - 2.0f * m_refSpeed);
1102 if (data.model == Machines::SM_MODEL_2 || data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 ||
1103 data.model == Machines::SM_MODEL_5) {
1104 data.icTranEq.m = m_timeStep / (2.0f * transTd0 + m_timeStep);
1105 data.icTranEq.c = (1.0 - data.icTranEq.m * (1.0 + data.sd)) * data.tranEq +
1106 data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id);
1110 if (data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
1111 data.icTranEd.m = m_timeStep / (2.0f * transTq0 + m_timeStep);
1113 (1.0 - data.icTranEd.m * (1.0 + data.sq)) * data.tranEd - data.icTranEd.m * (syncXq - transXq) * iq;
1117 if (data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
1118 data.icSubEq.m = m_timeStep / (2.0f * subTd0 + m_timeStep);
1119 data.icSubEq.c = (1.0 - data.icSubEq.m * (1.0 + data.sd)) * data.subEq +
1120 data.icSubEq.m * (data.sd * data.tranEq + (transXd - subXd) *
id);
1123 if (data.model == Machines::SM_MODEL_4) {
1124 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1126 (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd - data.icSubEd.m * (syncXq - subXq) * iq;
1128 if (data.model == Machines::SM_MODEL_5) {
1129 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1130 data.icSubEd.c = (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd +
1131 data.icSubEd.m * (data.sq * data.tranEd - (transXq - subXq) * iq);
1134 syncGenerator->SetElectricalData(data);
1137void Electromechanical::CalculateIntegrationConstants(
IndMotor* indMotor,
double ir,
double im,
double k)
1139 double w0 = 2.0 * M_PI * m_systemFreq;
1141 auto data = indMotor->GetElectricalData();
1144 if (data.slip > 0.99999) {
1150 data.as = data.aCalc;
1151 data.bs = data.bCalc;
1152 data.cs = data.cCalc;
1157 data.icSlip.m = m_timeStep / ((4.0f * data.inertia / w0) / k + data.bs * m_timeStep);
1158 data.icSlip.c = data.slip * (1.0 - 2.0 * data.bs * data.icSlip.m) +
1159 data.icSlip.m * (2.0 * data.as + data.cs * data.slip * data.slip - data.te);
1163 data.icTranEr.m = m_timeStep / (2.0 * data.t0 + m_timeStep);
1164 data.icTranEr.c = data.tranEr * (1.0 - 2.0 * data.icTranEr.m) +
1165 data.icTranEr.m * (w0 * data.t0 * data.slip * data.tranEm - (data.x0 - data.xt) * im);
1167 data.icTranEm.m = m_timeStep / (2.0 * data.t0 + m_timeStep);
1168 data.icTranEm.c = data.tranEm * (1.0 - 2.0 * data.icTranEm.m) -
1169 data.icTranEm.m * (w0 * data.t0 * data.slip * data.tranEr - (data.x0 - data.xt) * ir);
1171 indMotor->SetElectricalData(data);
1174bool Electromechanical::SolveMachines()
1180 auto data = syncGenerator->GetElectricalData();
1183 double id, iq, pe, sd, sq;
1191 if (data.useMachineBase) {
1192 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1193 k = m_powerSystemBase / oldBase;
1197 CalculateIntegrationConstants(syncGenerator,
id, iq, k);
1199 if (!CalculateNonIntVariables(syncGenerator,
id, iq, sd, sq, pe, k))
return false;
1201 id = 2.0 *
id - data.oldId;
1202 iq = 2.0 * iq - data.oldIq;
1203 pe = 2.0 * pe - data.oldPe;
1204 sd = 2.0 * sd - data.oldSd;
1205 sq = 2.0 * sq - data.oldSq;
1207 CalculateIntVariables(syncGenerator,
id, iq, sd, sq, pe, k);
1210 CalculateIntegrationConstants(syncGenerator, 0.0f, 0.0f);
1216 auto data = indMotor->GetElectricalData();
1225 if (data.useMachinePowerAsBase) {
1226 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1227 k = m_powerSystemBase / oldBase;
1231 CalculateIntegrationConstants(indMotor, ir, im, k);
1233 if (!CalculateNonIntVariables(indMotor, ir, im, te, k))
return false;
1235 ir = 2.0 * ir - data.oldIr;
1236 im = 2.0 * im - data.oldIm;
1237 te = 2.0 * te - data.oldTe;
1239 CalculateIntVariables(indMotor, ir, im, te, k);
1247 while (error > m_tolerance) {
1251 if (!CalculateInjectedCurrents())
return false;
1254 m_vBus =
LUEvaluate(m_yBusU, m_yBusL, m_iBus);
1260 auto data = syncGenerator->GetElectricalData();
1262 double id = data.id;
1263 double iq = data.iq;
1264 double pe = data.pe;
1265 double sd = data.sd;
1266 double sq = data.sq;
1270 if (data.useMachineBase) {
1271 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1272 k = m_powerSystemBase / oldBase;
1276 if (!CalculateNonIntVariables(syncGenerator,
id, iq, sd, sq, pe, k))
return false;
1278 double genError = CalculateIntVariables(syncGenerator,
id, iq, sd, sq, pe, k);
1280 if (genError > error) error = genError;
1287 auto data = indMotor->GetElectricalData();
1289 double ir = data.ir;
1290 double im = data.im;
1291 double te = data.te;
1295 if (data.useMachinePowerAsBase) {
1296 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1297 k = m_powerSystemBase / oldBase;
1301 if (!CalculateNonIntVariables(indMotor, ir, im, te, k))
return false;
1303 double motorError = CalculateIntVariables(indMotor, ir, im, te, k);
1305 if (motorError > error) error = motorError;
1310 if (iterations > m_maxIterations) {
1311 m_errorMsg = _(
"Impossible to solve the system machines.\nCheck the system parameters and/or "
1312 "decrease the time step.");
1316 m_iterationsNum = iterations;
1319 int ctrlRatio =
static_cast<int>(1 / m_ctrlTimeStepMultiplier);
1322 auto data = syncGenerator->GetElectricalData();
1323 if (data.useAVR && data.avrSolver) {
1324 data.avrSolver->SetSwitchStatus(syncGenerator->
IsOnline());
1325 data.avrSolver->SetCurrentTime(m_currentTime);
1326 data.avrSolver->SetTerminalVoltage(std::abs(data.terminalVoltage));
1327 data.avrSolver->SetDeltaActivePower((data.electricalPower.real() - data.avrSolver->GetActivePower()) /
1329 data.avrSolver->SetActivePower(data.electricalPower.real());
1330 data.avrSolver->SetReactivePower(data.electricalPower.imag());
1331 data.avrSolver->SetDeltaVelocity((data.speed - data.avrSolver->GetVelocity()) / m_timeStep);
1332 data.avrSolver->SetVelocity(data.speed);
1334 for (
int i = 0; i < ctrlRatio; ++i) data.avrSolver->SolveNextStep();
1336 data.fieldVoltage = data.initialFieldVoltage + data.avrSolver->GetFieldVoltage();
1339 if (data.useSpeedGovernor && data.speedGovSolver) {
1340 data.speedGovSolver->SetSwitchStatus(syncGenerator->
IsOnline());
1341 data.speedGovSolver->SetCurrentTime(m_currentTime);
1342 data.speedGovSolver->SetVelocity(data.speed);
1343 data.speedGovSolver->SetActivePower(data.electricalPower.real());
1344 data.speedGovSolver->SetReactivePower(data.electricalPower.imag());
1346 for (
int i = 0; i < ctrlRatio; ++i) data.speedGovSolver->SolveNextStep();
1348 data.pm = data.speedGovSolver->GetMechanicalPower();
1350 syncGenerator->SetElectricalData(data);
1356void Electromechanical::SaveData()
1360 auto data = syncGenerator->GetElectricalData();
1361 if (data.plotSyncMachine) { syncGenerator->SavePlotData(); }
1365 auto data = bus->GetElectricalData();
1367 data.stabVoltageVector.emplace_back(data.number >= 0 ? m_vBus[data.number] : 0.0);
1368 data.stabFreqVector.emplace_back(data.number >= 0 ? data.stabFreq : 0.0);
1369 bus->SetElectricalData(data);
1374 auto data = load->GetElectricalData();
1375 if (data.plotLoad) {
1376 data.voltageVector.emplace_back(data.voltage);
1377 data.electricalPowerVector.emplace_back(data.electricalPower);
1378 load->SetElectricalData(data);
1383 auto data = motor->GetElectricalData();
1384 if (data.plotIndMachine) {
1385 data.slipVector.emplace_back(data.slip * 100.0);
1386 data.electricalTorqueVector.emplace_back(data.te * 2.0 * M_PI * m_systemFreq);
1387 double tm = (data.as - data.bs * data.slip + data.cs * data.slip * data.slip) * 2.0 * M_PI * m_systemFreq;
1388 data.mechanicalTorqueVector.emplace_back(tm);
1389 double w = (1.0 - data.slip) * 2.0 * M_PI * m_systemFreq;
1390 data.velocityVector.emplace_back(w);
1391 std::complex<double> i1 = std::complex<double>(data.ir, data.im);
1392 data.currentVector.emplace_back(std::abs(i1));
1393 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalData().number;
1394 std::complex<double> v = n >= 0 ? m_vBus[n] : 0.0;
1395 data.terminalVoltageVector.emplace_back(std::abs(v));
1396 std::complex<double> s = v * std::conj(i1);
1397 data.activePowerVector.emplace_back(std::real(s));
1398 data.reactivePowerVector.emplace_back(std::imag(s));
1399 motor->SetElectricalData(data);
1402 m_iterationsNumVector.emplace_back(m_iterationsNum);
1405void Electromechanical::SetSyncMachinesModel()
1409 auto data = syncGenerator->GetElectricalData();
1411 syncGenerator->SetElectricalData(data);
1415bool Electromechanical::CalculateNonIntVariables(
SyncGenerator* syncGenerator,
1423 auto data = syncGenerator->GetElectricalData();
1425 if (
GetParentBus(syncGenerator, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalData().number]; }
1428 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1430 if (data.satFactor != 0.0) {
1431 if (!CalculateSyncMachineSaturation(syncGenerator,
id, iq, sd, sq,
true, k))
return false;
1439 pe =
id * vd + iq * vq + (
id *
id + iq * iq) * data.armResistance * k;
1442 pe =
id = iq = 0.0f;
1450 syncGenerator->SetElectricalData(data);
1455bool Electromechanical::CalculateNonIntVariables(
IndMotor* indMotor,
double& ir,
double& im,
double& te,
double k)
1457 auto data = indMotor->GetElectricalData();
1459 double w0 = 2.0 * M_PI * m_systemFreq;
1460 te = (data.tranEr * ir + data.tranEm * im) / w0;
1471 indMotor->SetElectricalData(data);
1476double Electromechanical::CalculateIntVariables(
SyncGenerator* syncGenerator,
1485 auto data = syncGenerator->GetElectricalData();
1488 double w = data.icSpeed.c + data.icSpeed.m * (data.pm - pe);
1489 error = std::max(error, std::abs(data.speed - w) / m_refSpeed);
1491 double delta = data.icDelta.c + data.icDelta.m * w;
1492 error = std::max(error, std::abs(data.delta - delta));
1498 switch (data.model) {
1499 case Machines::SM_MODEL_1: {
1502 case Machines::SM_MODEL_2: {
1503 double syncXd, transXd;
1504 syncXd = data.syncXd * k;
1505 transXd = data.transXd * k;
1507 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id)) /
1508 (1.0 + data.icTranEq.m * (sd - 1.0));
1509 error = std::max(error, std::abs(data.tranEq - tranEq));
1511 data.tranEq = tranEq;
1513 case Machines::SM_MODEL_3: {
1514 double syncXd, syncXq, transXd, transXq;
1515 syncXd = data.syncXd * k;
1516 syncXq = data.syncXq * k;
1517 transXd = data.transXd * k;
1518 transXq = data.transXq * k;
1519 if (syncXq == 0.0) syncXq = syncXd;
1520 if (transXq == 0.0) transXq = transXd;
1522 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id)) /
1523 (1.0 + data.icTranEq.m * (sd - 1.0));
1524 error = std::max(error, std::abs(data.tranEq - tranEq));
1527 (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
1528 error = std::max(error, std::abs(data.tranEd - tranEd));
1530 data.tranEq = tranEq;
1531 data.tranEd = tranEd;
1534 std::complex<double> e;
1535 DQ0toABC(data.tranEd, data.tranEq, data.delta, e);
1536 data.terminalVoltage = e;
1539 case Machines::SM_MODEL_4: {
1540 double syncXd, syncXq, transXd, subXd, subXq;
1541 syncXd = data.syncXd * k;
1542 syncXq = data.syncXq * k;
1543 transXd = data.transXd * k;
1544 subXd = data.subXd * k;
1545 subXq = data.subXq * k;
1546 if (syncXq == 0.0) syncXq = syncXd;
1547 if (subXd == 0.0) subXd = subXq;
1548 if (subXq == 0.0) subXq = subXd;
1550 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id)) /
1551 (1.0 + data.icTranEq.m * (sd - 1.0));
1552 error = std::max(error, std::abs(data.tranEq - tranEq));
1554 double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) *
id)) /
1555 (1.0 + data.icSubEq.m * (sd - 1.0));
1556 error = std::max(error, std::abs(data.subEq - subEq));
1559 (data.icSubEd.c - data.icSubEd.m * ((syncXq - subXq) * iq)) / (1.0 + data.icSubEd.m * (sq - 1.0));
1560 error = std::max(error, std::abs(data.subEd - subEd));
1562 data.tranEq = tranEq;
1566 case Machines::SM_MODEL_5: {
1567 double syncXd, syncXq, transXd, transXq, subXd, subXq;
1568 syncXd = data.syncXd * k;
1569 syncXq = data.syncXq * k;
1570 transXd = data.transXd * k;
1571 transXq = data.transXq * k;
1572 subXd = data.subXd * k;
1573 subXq = data.subXq * k;
1574 if (syncXq == 0.0) syncXq = syncXd;
1575 if (transXq == 0.0) transXq = transXd;
1576 if (subXd == 0.0) subXd = subXq;
1577 if (subXq == 0.0) subXq = subXd;
1579 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id)) /
1580 (1.0 + data.icTranEq.m * (sd - 1.0));
1581 error = std::max(error, std::abs(data.tranEq - tranEq));
1584 (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
1585 error = std::max(error, std::abs(data.tranEd - tranEd));
1587 double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) *
id)) /
1588 (1.0 + data.icSubEq.m * (sd - 1.0));
1589 error = std::max(error, std::abs(data.subEq - subEq));
1591 double subEd = (data.icSubEd.c + data.icSubEd.m * (sq * tranEd - (transXq - subXq) * iq)) /
1592 (1.0 + data.icSubEd.m * (sq - 1.0));
1593 error = std::max(error, std::abs(data.subEd - subEd));
1595 data.tranEq = tranEq;
1596 data.tranEd = tranEd;
1602 syncGenerator->SetElectricalData(data);
1606double Electromechanical::CalculateIntVariables(
IndMotor* indMotor,
double ir,
double im,
double te,
double k)
1609 auto data = indMotor->GetElectricalData();
1610 double w0 = 2.0 * M_PI * m_systemFreq;
1614 double slip = data.slip;
1615 double ds = (data.icSlip.c + data.icSlip.m * (data.cs * slip * slip - te) - slip) /
1616 (1.0 - 2.0 * data.icSlip.m * data.cs * slip * slip);
1618 while (std::abs(ds) > 1e-8) {
1620 ds = (data.icSlip.c + data.icSlip.m * (data.cs * slip * slip - te) - slip) /
1621 (1.0 - 2.0 * data.icSlip.m * data.cs * slip * slip);
1623 if (iteration > m_maxIterations)
break;
1627 error = std::max(error, std::abs(data.slip - slip));
1632 data.t0 = (data.x2t + data.xmt) / (2.0 * M_PI * m_systemFreq * data.r2t * (1.0 + data.kf * data.r2t));
1635 double tranEr = data.icTranEr.c + data.icTranEr.m * (w0 * data.t0 * slip * data.tranEm - (data.x0 - data.xt) * im);
1636 error = std::max(error, std::abs(data.tranEr - tranEr));
1638 double tranEm = data.icTranEm.c - data.icTranEm.m * (w0 * data.t0 * slip * data.tranEr - (data.x0 - data.xt) * ir);
1639 error = std::max(error, std::abs(data.tranEm - tranEm));
1641 data.tranEr = tranEr;
1642 data.tranEm = tranEm;
1644 indMotor->SetElectricalData(data);
1648void Electromechanical::CalculateReferenceSpeed()
1656 auto data = syncGenerator->GetElectricalData();
1658 if (data.useMachineBase) {
1659 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1660 k = m_powerSystemBase / oldBase;
1662 sumH += data.inertia / k;
1663 sumHW += data.inertia * data.speed / k;
1666 m_refSpeed = sumHW / sumH;
1669 m_refSpeed = 2.0 * M_PI * m_systemFreq;
1673bool Electromechanical::CalculateSyncMachineSaturation(
SyncGenerator* syncMachine,
1678 bool updateCurrents,
1682 auto data = syncMachine->GetElectricalData();
1683 auto smDataModel = GetSyncMachineModelData(syncMachine);
1686 if (
GetParentBus(syncMachine, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalData().number]; }
1697 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1698 double deltaVd = smDataModel.ed - vd;
1699 double deltaVq = smDataModel.eq - vq;
1701 double ra = data.armResistance * k;
1702 double xd = smDataModel.xd;
1703 double xq = smDataModel.xq;
1705 double syncXd = data.syncXd * k;
1706 double syncXq = data.syncXq * k;
1707 if (data.model == Machines::SM_MODEL_1) {
1708 syncXq = data.transXd * k;
1711 else if (data.syncXq == 0.0)
1712 syncXq = data.syncXd * k;
1714 double xp = data.potierReactance * k;
1715 if (xp == 0.0) xp = 0.8 * data.transXd * k;
1716 double satFacd = (data.satFactor - 1.2) / std::pow(1.2, 7);
1717 double satFacq = satFacd * (syncXq / syncXd);
1722 double oldSd = sdCalc;
1723 double oldSq = sqCalc;
1726 double xds = (xd - xp) / sdCalc + xp;
1727 double xqs = (xq - xp) / sqCalc + xp;
1729 double den = 1.0 / (ra * ra + xds * xqs);
1730 iqCalc = den * (ra * deltaVq + xds * deltaVd);
1731 idCalc = den * (-xqs * deltaVq + ra * deltaVd);
1733 double epq = vq + ra * iqCalc - xp * idCalc;
1734 double epd = vd + ra * idCalc + xp * iqCalc;
1741 double f1 = 1.0 - sdCalc + satFacd * std::pow(epq, 6);
1742 double f2 = 1.0 - sqCalc + satFacq * std::pow(epd, 6);
1744 (6.0 * satFacd * std::pow(epq, 5) * xp * (xd - xp) * deltaVq) / ((sdCalc - 1.0) * xp + xd) - 1.0;
1746 (6.0 * satFacq * std::pow(epd, 5) * xp * (xq - xp) * deltaVd) / ((sqCalc - 1.0) * xp + xq) - 1.0;
1748 sdCalc = sdCalc - f1 / dF1dSd;
1749 sqCalc = sqCalc - f2 / dF2dSq;
1751 double error = std::abs(sdCalc - oldSd) + std::abs(sqCalc - oldSq);
1752 if (error < m_saturationTolerance) exit =
true;
1755 if ((iterations >= m_maxIterations) && !exit) {
1757 _(
"It was not possible to solve the saturation of the synchronous machine \"") + data.name + wxT(
"\".");
1764 if (updateCurrents) {
1771void Electromechanical::CalculateBusesFrequency(
bool hasEvent)
1773 bool ignoreEventStep =
true;
1775 auto data = bus->GetElectricalData();
1776 if (!data.plotBus)
continue;
1778 if (m_simData.busFreqEstimation == BusFreqEstimation::WASHOUT_FILTER) {
1787 double tf = m_simData.tf;
1788 double tw = m_simData.tw;
1790 double theta0 = std::arg(data.number >= 0 ? data.voltage : 0.0);
1793 auto fdxt = [
this, tf, theta0](
double theta,
double xt) {
1794 return (1.0 / tf) * ((1.0 / (2.0 * M_PI * m_systemFreq)) * (theta - theta0) - xt);
1798 auto fddw = [
this, tw](
double dxt,
double dw) {
1799 return (1.0 / tw) * (dxt - dw);
1802 double theta = std::arg(data.number >= 0 ? m_vBus[data.number] : theta0);
1804 double xt = 0, dw = 0, dxt = 0, ddw = 0;
1806 while (error > 1e-6 && iterations <= 100) {
1807 double oldXT = xt, oldDW = dw;
1809 dxt = fdxt(theta, xt);
1810 xt = data.filteredAngle + 0.5 * m_timeStep * (data.dxt + dxt);
1811 error = std::abs(xt - oldXT);
1813 ddw = fddw(dxt, dw);
1814 dw = data.filteredVelocity + 0.5 * m_timeStep * (data.ddw + ddw);
1815 error = std::max(error, std::abs(dw - oldDW));
1826 data.filteredAngle = xt;
1827 data.filteredVelocity = dw;
1831 double busVelocity = m_refSpeed + dw * (2.0 * M_PI * m_systemFreq);
1832 data.stabFreq = busVelocity / (2.0 * M_PI);
1834 bus->SetElectricalData(data);
1836 else if (m_simData.busFreqEstimation == BusFreqEstimation::ANGLE_DERIVATION) {
1837 double newAngle = std::arg(data.number >= 0 ? m_vBus[data.number] : 0.0);
1841 double dw = (newAngle - data.oldAngle) / m_timeStep;
1844 if (m_simData.ignoreBusFreqEventStep && hasEvent) dw = 0.0;
1847 data.stabFreq = (m_refSpeed + dw) / (2.0 * M_PI);
1850 data.oldAngle = newAngle;
1852 bus->SetElectricalData(data);
1860 auto data = syncMachine->GetElectricalData();
1862 if (data.useMachineBase) {
1863 double oldBase = syncMachine->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1864 k = m_powerSystemBase / oldBase;
1867 switch (data.model) {
1868 case Machines::SM_MODEL_1: {
1869 smModelData.
ed = data.tranEd;
1870 smModelData.
eq = data.tranEq;
1871 smModelData.
xq = data.transXd * k;
1872 smModelData.
xd = smModelData.
xq;
1874 case Machines::SM_MODEL_2: {
1875 smModelData.
ed = data.tranEd;
1876 smModelData.
eq = data.tranEq;
1877 smModelData.
xd = data.transXd * k;
1878 smModelData.
xq = data.transXq * k;
1879 if (smModelData.
xq == 0.0) {
1880 smModelData.
xq = data.syncXq * k;
1881 if (smModelData.
xq == 0.0) { smModelData.
xq = data.syncXd * k; }
1884 case Machines::SM_MODEL_3: {
1885 smModelData.
ed = data.tranEd;
1886 smModelData.
eq = data.tranEq;
1887 smModelData.
xd = data.transXd * k;
1888 smModelData.
xq = data.transXq * k;
1889 if (smModelData.
xq == 0.0) smModelData.
xq = smModelData.
xd;
1891 case Machines::SM_MODEL_4:
1892 case Machines::SM_MODEL_5: {
1893 smModelData.
ed = data.subEd;
1894 smModelData.
eq = data.subEq;
1895 smModelData.
xd = data.subXd * k;
1896 smModelData.
xq = data.subXq * k;
1897 if (smModelData.
xd == 0.0) smModelData.
xd = smModelData.
xq;
1898 if (smModelData.
xq == 0.0) smModelData.
xq = smModelData.
xd;
1904void Electromechanical::PreallocateVectors()
1906 int numPoints =
static_cast<unsigned int>(m_simTime / m_plotTime);
1908 m_timeVector.reserve(numPoints);
1911 auto data = syncGenerator->GetElectricalData();
1912 if (data.plotSyncMachine) {
1913 data.terminalVoltageVector.reserve(numPoints);
1914 data.electricalPowerVector.reserve(numPoints);
1915 data.mechanicalPowerVector.reserve(numPoints);
1916 data.freqVector.reserve(numPoints);
1917 data.fieldVoltageVector.reserve(numPoints);
1918 data.deltaVector.reserve(numPoints);
1919 syncGenerator->SetElectricalData(data);
1924 auto data = bus->GetElectricalData();
1926 data.stabVoltageVector.reserve(numPoints);
1927 data.stabFreqVector.reserve(numPoints);
1928 bus->SetElectricalData(data);
1933 auto data = load->GetElectricalData();
1934 if (data.plotLoad) {
1935 data.voltageVector.reserve(numPoints);
1936 data.electricalPowerVector.reserve(numPoints);
1937 load->SetElectricalData(data);
1942 auto data = motor->GetElectricalData();
1943 if (data.plotIndMachine) {
1944 data.slipVector.reserve(numPoints);
1945 data.electricalTorqueVector.reserve(numPoints);
1946 motor->SetElectricalData(data);
1951std::complex<double> Electromechanical::GetIndMachineAdmittance(
IndMotor* motor)
1953 auto data = motor->GetElectricalData();
1954 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
1955 std::complex<double> v =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalData().voltage;
1956 std::complex<double> y0 = (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
1958 std::complex<double> yq = std::complex<double>(0.0, data.q0 - dataPU.reactivePower) / (std::abs(v) * std::abs(v));
1962bool Electromechanical::InsertIndMachinesOnYBus()
1966 if (!CalculateIndMachinesTransientValues(motor))
return false;
1968 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalData().number;
1969 m_yBus[n][n] += GetIndMachineAdmittance(motor);
1975bool Electromechanical::CalculateIndMachinesTransientValues(
IndMotor* motor)
1977 auto data = motor->GetElectricalData();
1978 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
1980 if (data.useMachinePowerAsBase) {
1981 double oldBase = motor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1982 k = m_powerSystemBase / oldBase;
1985 data.terminalVoltage =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalData().voltage;
1988 double r1t = data.r1 * k;
1989 double r2t = data.r2 * k;
1990 double x1t = data.x1 * k;
1991 double x2t = data.x2 * k;
1992 double xmt = data.xm * k;
1999 double xt = x1t + (x2t * xmt) / (x2t + xmt);
2000 double x0 = x1t + xmt;
2004 double p = dataPU.activePower;
2005 double v = std::abs(data.terminalVoltage);
2010 double r1 = data.r1t;
2011 double r2 = data.r2t;
2012 if (data.useKf) r2 *= (1.0 + data.kf * r2t);
2013 double x1 = data.x1t;
2014 double x2 = data.x2t;
2015 double xm = data.xmt;
2016 double k1 = x2 + xm;
2017 double k2 = -x1 * k1 - x2 * xm;
2018 double k3 = xm + x1;
2019 double k4 = r1 * k1;
2020 double a = p * (r1 * r1 + k3 * k3) - v * v * r1;
2021 double b = 2.0 * p * (r1 * k2 + k3 * k4) - v * v * (k2 + k1 * k3);
2022 double c = p * (k2 * k2 + k4 * k4) - v * v * k1 * k4;
2023 double d = (b * b - 4.0 * a * c);
2025 m_errorMsg = _(
"Error on initializate the slip of \"") + data.name + _(
"\".");
2028 double r2_s = (-b + std::sqrt(d)) / (2.0 * a);
2029 data.s0 = r2 / r2_s;
2031 double qa = k1 * (r2_s * r1 - x1 * k1 - x2 * xm);
2032 double qb = r2_s * (r2_s * (xm + x1) + r1 * k1);
2033 double qc = r2_s * r1 - x1 * k1 - x2 * xm;
2034 double qd = r2_s * (xm + x1) + r1 * k1;
2035 data.q0 = (-v * v * (qa - qb)) / (qc * qc + qd * qd);
2037 data.t0 = (x2t + xmt) / (2.0 * M_PI * m_systemFreq * r2);
2039 motor->SetElectricalData(data);
Node for power elements. All others power elements are connected through this.
Shunt capactior power element.
std::vector< std::complex< double > > ComplexMatrixTimesVector(std::vector< std::vector< std::complex< double > > > matrix, std::vector< std::complex< double > > vector)
Multiply a complex matrix by a complex vector.
std::vector< Line * > m_lineList
List of transmission lines in the system.
std::vector< PowerElement * > m_powerElementList
List of power elements in the system.
std::vector< Bus * > m_busList
List of buses in the system.
std::vector< Load * > m_loadList
List of load elements in the system.
void ABCtoDQ0(std::complex< double > complexValue, double angle, double &dValue, double &qValue)
Convert a complex phasor in ABC representation to DQ components.
std::vector< Capacitor * > m_capacitorList
List of capacitor elements in the system.
std::vector< std::complex< double > > LUEvaluate(std::vector< std::vector< std::complex< double > > > u, std::vector< std::vector< std::complex< double > > > l, std::vector< std::complex< double > > b)
Solve a linear system using LU decomposition.
std::vector< IndMotor * > m_indMotorList
List of induction motors in the system.
bool GetParentBus(Element *childElement, Bus *&parentBus)
Get the parent bus of a given shunt element.
std::vector< Transformer * > m_transformerList
List of transformers in the system.
std::vector< SyncGenerator * > m_syncGeneratorList
List of synchronous generators in the system.
virtual bool GetYBus(std::vector< std::vector< std::complex< double > > > &yBus, double systemPowerBase, YBusSequence sequence=POSITIVE_SEQ, bool includeSyncMachines=false, bool allLoadsAsImpedances=false, bool usePowerFlowVoltagesOnImpedances=false)
Get the admittance matrix from the list of elements (use GetElementsFromList first).
virtual void GetElementsFromList(std::vector< Element * > elementList)
Separate the power elements from a generic list.
std::vector< Inductor * > m_inductorList
List of inductors in the system.
Machines::SyncMachineModel GetMachineModel(SyncGenerator *generator)
Get the synchronous machine model used by the generator based on user-defined parameters.
void GetLUDecomposition(std::vector< std::vector< std::complex< double > > > matrix, std::vector< std::vector< std::complex< double > > > &matrixL, std::vector< std::vector< std::complex< double > > > &matrixU)
Compute the LU decomposition of a matrix.
void DQ0toABC(double dValue, double qValue, double angle, std::complex< double > &complexValue)
Convert DQ components to a complex phasor in ABC representation.
virtual std::vector< Element * > GetParentList() const
Get the parent list.
bool IsOnline() const
Checks if the element is online or offline.
bool SetOnline(bool online=true)
Set if the element is online or offline.
Induction motor power element.
Inductor shunt power element.
Loas shunt power element.
Abstract class of power elements.
virtual SwitchingData GetSwitchingData()
Returns the switching data of the element.
Synchronous generator power element.
Switching data of power elements.
std::vector< double > swTime
std::vector< SwitchingType > swType
Synchronous machine data for different models.