18#include "../elements/controlElement/ControlElementSolver.h"
24double totalSaveData = 0.0;
25double totalSolveMachines = 0.0;
26double totalCalcFreq = 0.0;
28size_t saveDataCalls = 0;
29size_t solveMachinesCalls = 0;
30size_t calcFreqCalls = 0;
33Electromechanical::Electromechanical(wxWindow* parent, std::vector<Element*> elementList,
SimulationData data)
41 m_powerSystemBase = dummyBus.GetValueFromUnit(data.basePower, data.basePowerUnit);
42 m_systemFreq = data.stabilityFrequency;
43 m_simTime = data.stabilitySimulationTime;
44 m_timeStep = data.timeStep;
45 m_tolerance = data.stabilityTolerance;
46 m_maxIterations = data.stabilityMaxIterations;
48 m_ctrlTimeStepMultiplier = 1.0 /
static_cast<double>(data.controlTimeStepRatio);
50 m_plotTime = data.plotTime;
51 m_useCOI = data.useCOI;
55 auto& loadData = load->GetElectricalDataRef();
56 if (!loadData.useCompLoad) {
57 if (data.useCompLoads) {
58 loadData.constImpedanceActive = data.constImpedanceActive;
59 loadData.constCurrentActive = data.constCurrentActive;
60 loadData.constPowerActive = data.constPowerActive;
61 loadData.constImpedanceReactive = data.constImpedanceReactive;
62 loadData.constCurrentReactive = data.constCurrentReactive;
63 loadData.constPowerReactive = data.constPowerReactive;
66 loadData.constImpedanceActive = 100.0;
67 loadData.constCurrentActive = 0.0;
68 loadData.constPowerActive = 0.0;
69 loadData.constImpedanceReactive = 100.0;
70 loadData.constCurrentReactive = 0.0;
71 loadData.constPowerReactive = 0.0;
75 loadData.constCurrentUV = data.underVoltageConstCurrent / 100.0;
76 loadData.constPowerUV = data.underVoltageConstPower / 100.0;
81Electromechanical::~Electromechanical() { }
83bool Electromechanical::RunStabilityCalculation()
85 wxProgressDialog pbd(_(
"Running simulation"), _(
"Initializing..."), 100, m_parent,
86 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_SMOOTH);
88 SetSyncMachinesModel();
92 m_errorMsg = _(
"It was not possible to build the admittance matrix.");
95 InsertSyncMachinesOnYBus();
96 if (!InsertIndMachinesOnYBus())
return false;
101 m_vBus.resize(m_yBus.size());
104 const auto& data = bus->GetElectricalDataRef();
105 if (data.number >= 0) m_vBus[data.number] = data.voltage;
107 m_vBus.shrink_to_fit();
111 for (
unsigned int i = 0; i < m_iBus.size(); ++i) {
112 if (std::abs(m_iBus[i]) < 1e-5) m_iBus[i] = std::complex<double>(0.0, 0.0);
115 if (!InitializeDynamicElements())
return false;
116 PreallocateVectors();
120 auto data = syncGenerator->GetPUElectricalData(m_powerSystemBase);
121 m_debugMessage +=
"------\n";
122 m_debugMessage += data.name +
":\n";
123 m_debugMessage +=
"P = " + wxString::FromDouble(data.electricalPower.real(), 5) +
" p.u.\n";
124 m_debugMessage +=
"Q = " + wxString::FromDouble(data.electricalPower.imag(), 5) +
" p.u.\n";
125 m_debugMessage +=
"If0 = " + wxString::FromDouble(data.initialFieldVoltage, 5) +
" p.u.\n";
126 m_debugMessage +=
"If = " + wxString::FromDouble(data.fieldVoltage, 5) +
" p.u.\n";
127 m_debugMessage +=
"delta = " + wxString::FromDouble(data.delta, 5) +
" rad?\n";
128 m_debugMessage +=
"Vf = " + wxString::FromDouble(data.fieldVoltage, 5) +
" p.u.\n";
129 m_debugMessage +=
"Vf0 = " + wxString::FromDouble(data.initialFieldVoltage, 5) +
" p.u.\n";
130 m_debugMessage +=
"Vt = " + wxString::FromDouble(std::abs(data.terminalVoltage), 5) +
" p.u.\n";
131 m_debugMessage +=
"theta = " + wxString::FromDouble(std::arg(data.terminalVoltage), 5) +
" rad\n";
132 m_debugMessage +=
"Pe = " + wxString::FromDouble(data.pe, 5) +
" p.u.\n";
133 m_debugMessage +=
"Pm = " + wxString::FromDouble(data.pm, 5) +
" p.u.\n";
134 m_debugMessage +=
"w = " + wxString::FromDouble(data.speed, 5) +
" p.u.\n";
135 m_debugMessage +=
"Id = " + wxString::FromDouble(data.id, 5) +
" p.u.\n";
136 m_debugMessage +=
"Iq = " + wxString::FromDouble(data.iq, 5) +
" p.u.\n";
137 m_debugMessage +=
"Ed' = " + wxString::FromDouble(data.tranEd, 5) +
" p.u.\n";
138 m_debugMessage +=
"Eq' = " + wxString::FromDouble(data.tranEq, 5) +
" p.u.\n";
139 m_debugMessage +=
"Ed'' = " + wxString::FromDouble(data.subEd, 5) +
" p.u.\n";
140 m_debugMessage +=
"Eq'' = " + wxString::FromDouble(data.subEq, 5) +
" p.u.\n";
141 m_debugMessage +=
"------\n\n";
145 double pbdTime = m_plotTime;
147 double currentPlotTime = 0.0;
148 double currentPbdTime = 0.0;
149 while (m_currentTime <= m_simTime) {
150 bool hasEvent =
false;
151 if (HasEvent(m_currentTime)) {
153 SetEvent(m_currentTime);
159 if (currentPlotTime >= m_plotTime || m_currentTime == 0.0) {
160 m_timeVector.emplace_back(m_currentTime);
163 auto t0 = std::chrono::high_resolution_clock::now();
169 auto t1 = std::chrono::high_resolution_clock::now();
170 totalSaveData += std::chrono::duration<double, std::milli>(t1 - t0).count();
174 currentPlotTime = 0.0;
177 if (currentPbdTime > pbdTime) {
178 if (!pbd.Update((m_currentTime / m_simTime) * 100, wxString::Format(
"Time = %.2fs", m_currentTime))) {
179 m_errorMsg = wxString::Format(_(
"Simulation cancelled at %.2fs."), m_currentTime);
183 currentPbdTime = 0.0;
187 auto t0 = std::chrono::high_resolution_clock::now();
190 if (!SolveMachines())
return false;
193 auto t1 = std::chrono::high_resolution_clock::now();
194 totalSolveMachines += std::chrono::duration<double, std::milli>(t1 - t0).count();
195 solveMachinesCalls++;
197 t0 = std::chrono::high_resolution_clock::now();
200 CalculateBusesFrequency(hasEvent);
203 t1 = std::chrono::high_resolution_clock::now();
204 totalCalcFreq += std::chrono::duration<double, std::milli>(t1 - t0).count();
208 m_currentTime += m_timeStep;
209 currentPlotTime += m_timeStep;
210 currentPbdTime += m_timeStep;
213 wxLogMessage(
"SaveData: chamadas=%zu total=%.2f ms media=%.3f ms", saveDataCalls, totalSaveData, totalSaveData / saveDataCalls);
214 wxLogMessage(
"SolveMachines: chamadas=%zu total=%.2f ms media=%.3f ms", solveMachinesCalls, totalSolveMachines, totalSolveMachines / solveMachinesCalls);
215 wxLogMessage(
"CalculateBusesFrequency: chamadas=%zu total=%.2f ms media=%.3f ms", calcFreqCalls, totalCalcFreq, totalCalcFreq / calcFreqCalls);
221void Electromechanical::SetEventTimeList()
226 const auto& data = bus->GetElectricalDataRef();
227 if (data.stabHasFault) {
228 m_eventTimeList.emplace_back(data.stabFaultTime);
229 m_eventOccurrenceList.push_back(
false);
230 m_eventTimeList.emplace_back(data.stabFaultTime + data.stabFaultLength);
231 m_eventOccurrenceList.push_back(
false);
238 for (
unsigned int i = 0; i < swData.
swTime.size(); ++i) {
239 m_eventTimeList.emplace_back(swData.
swTime[i]);
240 m_eventOccurrenceList.push_back(
false);
245bool Electromechanical::HasEvent(
double currentTime)
247 for (
unsigned int i = 0; i < m_eventTimeList.size(); ++i) {
248 if (!m_eventOccurrenceList[i]) {
249 if (EventTrigger(m_eventTimeList[i], currentTime)) {
250 m_eventOccurrenceList[i] =
true;
258void Electromechanical::SetEvent(
double currentTime)
263 const auto& data = bus->GetElectricalDataRef();
265 if (data.stabHasFault && n >= 0) {
268 if (EventTrigger(data.stabFaultTime, currentTime)) {
270 r = data.stabFaultResistance;
271 x = data.stabFaultReactance;
272 if (x < 1e-5) x = 1e-5;
273 m_yBus[n][n] += std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
277 else if (EventTrigger(data.stabFaultTime + data.stabFaultLength, currentTime)) {
279 r = data.stabFaultResistance;
280 x = data.stabFaultReactance;
281 if (x < 1e-5) x = 1e-5;
282 m_yBus[n][n] -= std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
291 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
292 if (EventTrigger(swData.
swTime[i], currentTime)) {
296 int n =
static_cast<Bus*
>(generator->
GetParentList()[0])->GetElectricalDataRef().number;
297 if (n >= 0) m_yBus[n][n] -= GetSyncMachineAdmittance(generator);
303 int n =
static_cast<Bus*
>(generator->
GetParentList()[0])->GetElectricalDataRef().number;
304 if (n >= 0) m_yBus[n][n] += GetSyncMachineAdmittance(generator);
315 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
316 if (EventTrigger(swData.
swTime[i], currentTime)) {
319 const auto& data = motor->GetElectricalDataRef();
321 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalDataRef().number;
322 if (n >= 0) m_yBus[n][n] -= (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
327 const auto& data = motor->GetElectricalDataRef();
329 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalDataRef().number;
330 if (n >= 0) m_yBus[n][n] += (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
341 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
342 if (EventTrigger(swData.
swTime[i], currentTime)) {
346 auto data = load->GetPUElectricalData(m_powerSystemBase);
348 int n = parentBus->GetElectricalDataRef().number;
349 std::complex<double> v = parentBus->GetElectricalDataRef().voltage;
352 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
359 auto data = load->GetPUElectricalData(m_powerSystemBase);
361 int n = parentBus->GetElectricalDataRef().number;
362 std::complex<double> v = parentBus->GetElectricalDataRef().voltage;
365 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
377 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
378 if (EventTrigger(swData.
swTime[i], currentTime)) {
382 const auto& data = line->GetElectricalDataRef();
384 int n1 =
static_cast<Bus*
>(line->
GetParentList()[0])->GetElectricalDataRef().number;
385 int n2 =
static_cast<Bus*
>(line->
GetParentList()[1])->GetElectricalDataRef().number;
386 if (n1 >= 0 && n2 >= 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);
393 m_yBus[n1][n1] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
394 m_yBus[n2][n2] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
401 auto& data = line->GetElectricalDataRef();
403 int n1 =
static_cast<Bus*
>(line->
GetParentList()[0])->GetElectricalDataRef().number;
404 int n2 =
static_cast<Bus*
>(line->
GetParentList()[1])->GetElectricalDataRef().number;
405 if (n1 >= 0 && n2 >= 0) {
406 m_yBus[n1][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
407 m_yBus[n2][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
409 m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
410 m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
412 m_yBus[n1][n1] += std::complex<double>(0.0, data.capSusceptance / 2.0);
413 m_yBus[n2][n2] += std::complex<double>(0.0, data.capSusceptance / 2.0);
425 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
426 if (EventTrigger(swData.
swTime[i], currentTime)) {
430 const auto& data = transformer->GetElectricalDataRef();
432 int n1 =
static_cast<Bus*
>(transformer->
GetParentList()[0])->GetElectricalDataRef().number;
433 int n2 =
static_cast<Bus*
>(transformer->
GetParentList()[1])->GetElectricalDataRef().number;
434 if (n1 >= 0 && n2 >= 0) {
435 if (data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
436 m_yBus[n1][n2] -= -1.0 / std::complex<double>(data.resistance, data.indReactance);
437 m_yBus[n2][n1] -= -1.0 / std::complex<double>(data.resistance, data.indReactance);
439 m_yBus[n1][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
440 m_yBus[n2][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
444 double radPhaseShift = wxDegToRad(data.phaseShift);
445 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
446 -data.turnsRatio * std::sin(radPhaseShift));
449 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
450 m_yBus[n1][n1] -= y / std::pow(std::abs(a), 2.0);
451 m_yBus[n1][n2] -= -(y / std::conj(a));
452 m_yBus[n2][n1] -= -(y / a);
462 const auto& data = transformer->GetElectricalDataRef();
464 int n1 =
static_cast<Bus*
>(transformer->
GetParentList()[0])->GetElectricalDataRef().number;
465 int n2 =
static_cast<Bus*
>(transformer->
GetParentList()[1])->GetElectricalDataRef().number;
466 if (n1 >= 0 && n2 >= 0) {
467 if (data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
468 m_yBus[n1][n2] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
469 m_yBus[n2][n1] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
471 m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
472 m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
476 double radPhaseShift = wxDegToRad(data.phaseShift);
477 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
478 -data.turnsRatio * std::sin(radPhaseShift));
481 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
482 m_yBus[n1][n1] += y / std::pow(std::abs(a), 2.0);
483 m_yBus[n1][n2] += -(y / std::conj(a));
484 m_yBus[n2][n1] += -(y / a);
498 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
499 if (EventTrigger(swData.
swTime[i], currentTime)) {
503 auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
504 int n =
static_cast<Bus*
>(capacitor->
GetParentList()[0])->GetElectricalDataRef().number;
505 if (n >= 0) m_yBus[n][n] -= std::complex<double>(0.0, data.reactivePower);
511 auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
512 int n =
static_cast<Bus*
>(capacitor->
GetParentList()[0])->GetElectricalDataRef().number;
513 if (n >= 0) m_yBus[n][n] += std::complex<double>(0.0, data.reactivePower);
524 for (
unsigned int i = 0; i < swData.
swType.size(); ++i) {
525 if (EventTrigger(swData.
swTime[i], currentTime)) {
529 auto data = inductor->GetPUElectricalData(m_powerSystemBase);
530 int n =
static_cast<Bus*
>(inductor->
GetParentList()[0])->GetElectricalDataRef().number;
531 if (n >= 0) m_yBus[n][n] -= std::complex<double>(0.0, -data.reactivePower);
537 auto data = inductor->GetPUElectricalData(m_powerSystemBase);
538 int n =
static_cast<Bus*
>(inductor->
GetParentList()[0])->GetElectricalDataRef().number;
539 if (n >= 0) m_yBus[n][n] += std::complex<double>(0.0, -data.reactivePower);
547void Electromechanical::InsertSyncMachinesOnYBus()
553 if (connBus->GetElectricalDataRef().number < 0) {
562 const auto& data = syncGenerator->GetElectricalDataRef();
563 int n = connBus->GetElectricalDataRef().number;
564 m_yBus[n][n] += GetSyncMachineAdmittance(syncGenerator);
570bool Electromechanical::EventTrigger(
double eventTime,
double currentTime)
572 return (((eventTime - m_timeStep) < currentTime) && (eventTime >= currentTime));
575std::complex<double> Electromechanical::GetSyncMachineAdmittance(
SyncGenerator* generator)
577 const auto& data = generator->GetElectricalDataRef();
579 if (data.useMachineBase) {
580 double oldBase = generator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
581 k = m_powerSystemBase / oldBase;
584 double ra = data.armResistance * k;
585 auto smModelData = GetSyncMachineModelData(generator);
586 double xd = smModelData.xd;
587 double xq = smModelData.xq;
588 double xdq = 0.5 * (xd + xq);
589 return (std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0));
592bool Electromechanical::InitializeDynamicElements()
597 auto& data = bus->GetElectricalDataRef();
598 data.stabVoltageVector.clear();
599 data.stabVoltageVector.shrink_to_fit();
600 data.stabFreqVector.clear();
601 data.stabFreqVector.shrink_to_fit();
602 data.stabFreq = m_systemFreq;
603 data.oldAngle = std::arg(data.voltage);
604 data.filteredAngle = 0.0;
606 data.filteredVelocity = 0.0;
612 auto dataPU = load->GetPUElectricalData(m_powerSystemBase);
613 auto& data = load->GetElectricalDataRef();
615 double activePower = dataPU.activePower;
616 double reactivePower = dataPU.reactivePower;
621 data.voltage = bus->GetElectricalDataRef().voltage;
627 data.v0 = std::abs(data.voltage);
628 data.y0 = std::complex<double>(activePower, -reactivePower) / (data.v0 * data.v0);
630 if (data.loadType == CONST_IMPEDANCE) {
631 std::complex<double> s0 = std::complex<double>(activePower, -reactivePower) * (data.v0 * data.v0);
632 activePower = s0.real();
633 reactivePower = -s0.imag();
636 data.pz0 = (data.constImpedanceActive / 100.0) * activePower;
637 data.pi0 = (data.constCurrentActive / 100.0) * activePower;
638 data.pp0 = (data.constPowerActive / 100.0) * activePower;
640 data.qz0 = (data.constImpedanceReactive / 100.0) * reactivePower;
641 data.qi0 = (data.constCurrentReactive / 100.0) * reactivePower;
642 data.qp0 = (data.constPowerReactive / 100.0) * reactivePower;
644 data.voltageVector.clear();
645 data.voltageVector.shrink_to_fit();
646 data.electricalPowerVector.clear();
647 data.electricalPowerVector.shrink_to_fit();
650 data.electricalPower = std::complex<double>(activePower, reactivePower);
652 data.electricalPower = std::complex<double>(0.0, 0.0);
653 data.voltage = std::complex<double>(0.0, 0.0);
661 auto dataPU = syncGenerator->GetPUElectricalData(m_powerSystemBase);
662 auto& data = syncGenerator->GetElectricalDataRef();
664 if (data.useMachineBase) {
665 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
666 k = m_powerSystemBase / oldBase;
670 data.terminalVoltage = bus->GetElectricalDataRef().voltage;
672 data.terminalVoltage = std::complex<double>(1.0, 0.0);
674 std::complex<double> conjS(dataPU.activePower, -dataPU.reactivePower);
675 std::complex<double> vt = data.terminalVoltage;
676 std::complex<double> ia = conjS / std::conj(vt);
678 double xd = data.syncXd * k;
679 double xq = data.syncXq * k;
680 double ra = data.armResistance * k;
682 if (data.model == Machines::SM_MODEL_1) {
683 xq = data.transXd * k;
686 else if (data.syncXq == 0.0)
687 xq = data.syncXd * k;
692 double xp = data.potierReactance * k;
693 bool hasSaturation =
false;
694 if (data.satFactor != 0.0) {
695 satF = (data.satFactor - 1.2) / std::pow(1.2, 7);
696 if (xp == 0.0) xp = 0.8 * (data.transXd * k);
697 hasSaturation =
true;
701 std::complex<double> eq0 = vt + std::complex<double>(ra, xq) * ia;
702 double delta = std::arg(eq0);
704 double id0, iq0, vd0, vq0;
721 double epd = vd0 + ra * id0 + xp * iq0;
723 sq = 1.0 + satF * (xq / xd) * std::pow(epd, 6);
724 xqs = (xq - xp) / sq + xp;
725 eq0 = data.terminalVoltage + std::complex<double>(ra, xqs) * ia;
726 delta = std::arg(eq0);
727 if (std::abs(delta - oldDelta) < m_saturationTolerance) {
730 else if (numIt >= m_maxIterations) {
731 m_errorMsg = _(
"Error on initializate the saturation values of \"") + data.name + _(
"\".");
737 double epq = vq0 + ra * iq0 - xp * id0;
738 sd = 1.0 + satF * std::pow(epq, 6);
739 xds = (xd - xp) / sd + xp;
742 double ef0 = vq0 + ra * iq0 - xds * id0;
744 data.initialFieldVoltage = ef0 * sd;
745 data.fieldVoltage = data.initialFieldVoltage;
746 data.pm = std::real((data.terminalVoltage * std::conj(ia)) + (std::abs(ia) * std::abs(ia) * ra));
747 syncGenerator->
IsOnline() ? data.speed = 2.0 * M_PI * m_systemFreq : data.speed = 2.0 * M_PI * data.ocFrequency;
750 data.electricalPower = std::complex<double>(dataPU.activePower, dataPU.reactivePower);
751 if (!syncGenerator->
IsOnline()) data.electricalPower = std::complex<double>(0.0, 0.0);
760 data.oldPe = data.pe;
764 switch (data.model) {
765 case Machines::SM_MODEL_1: {
766 data.tranEq = std::abs(eq0);
772 case Machines::SM_MODEL_2: {
773 double tranXd = data.transXd * k;
775 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
780 case Machines::SM_MODEL_3: {
781 double tranXd = data.transXd * k;
782 double tranXq = data.transXq * k;
783 if (tranXq == 0.0) tranXq = tranXd;
785 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
786 data.tranEd = -(xq - tranXq) * (iq0 / sq);
791 case Machines::SM_MODEL_4: {
792 double tranXd = data.transXd * k;
793 double subXd = data.subXd * k;
794 double subXq = data.subXq * k;
795 if (subXd == 0.0) subXd = subXq;
796 if (subXq == 0.0) subXq = subXd;
798 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
800 data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
801 data.subEd = -(xq - subXq) * (iq0 / sq);
803 case Machines::SM_MODEL_5: {
804 double tranXd = data.transXd * k;
805 double tranXq = data.transXq * k;
806 double subXd = data.subXd * k;
807 double subXq = data.subXq * k;
808 if (subXd == 0.0) subXd = subXq;
809 if (subXq == 0.0) subXq = subXd;
811 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
812 data.tranEd = -(xq - tranXq) * (iq0 / sq);
813 data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
814 data.subEd = data.tranEd - (tranXq - subXq) * (iq0 / sq);
825 data.avrSolver = std::make_shared<ControlElementSolver>(data.avr, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
827 data.avrSolver->SetSwitchStatus(syncGenerator->
IsOnline());
828 data.avrSolver->SetCurrentTime(m_currentTime);
829 data.avrSolver->SetTerminalVoltage(std::abs(data.terminalVoltage));
830 data.avrSolver->SetInitialTerminalVoltage(std::abs(data.terminalVoltage));
831 data.avrSolver->SetActivePower(dataPU.activePower);
832 data.avrSolver->SetReactivePower(dataPU.reactivePower);
833 data.avrSolver->SetVelocity(data.speed);
834 data.avrSolver->SetInitialVelocity(data.speed);
835 data.avrSolver->InitializeValues(
false);
836 if (!data.avrSolver->IsOK()) {
837 m_errorMsg = _(
"Error on initializate the AVR of \"") + data.name + wxT(
"\".\n") +
838 data.avrSolver->GetErrorMessage();
843 if (data.useSpeedGovernor) {
848 data.speedGovSolver = std::make_shared<ControlElementSolver>(data.speedGov, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
849 data.speedGovSolver->SetSwitchStatus(syncGenerator->
IsOnline());
850 data.speedGovSolver->SetCurrentTime(m_currentTime);
851 data.speedGovSolver->SetActivePower(dataPU.activePower);
852 data.speedGovSolver->SetReactivePower(dataPU.reactivePower);
853 data.speedGovSolver->SetVelocity(data.speed);
854 data.speedGovSolver->SetInitialVelocity(data.speed);
855 data.speedGovSolver->SetInitialMecPower(data.pm);
856 data.speedGovSolver->InitializeValues(
false);
857 if (!data.speedGovSolver->IsOK()) {
858 m_errorMsg = _(
"Error on initializate the speed governor of \"") + data.name + wxT(
"\".\n") +
859 data.speedGovSolver->GetErrorMessage();
869 data.terminalVoltageVector.clear();
870 data.terminalVoltageVector.shrink_to_fit();
871 data.electricalPowerVector.clear();
872 data.electricalPowerVector.shrink_to_fit();
873 data.mechanicalPowerVector.clear();
874 data.mechanicalPowerVector.shrink_to_fit();
875 data.freqVector.clear();
876 data.freqVector.shrink_to_fit();
877 data.fieldVoltageVector.clear();
878 data.fieldVoltageVector.shrink_to_fit();
879 data.deltaVector.clear();
880 data.deltaVector.shrink_to_fit();
887 auto dataPU = indMotor->GetPUElectricalData(m_powerSystemBase);
888 auto& data = indMotor->GetElectricalDataRef();
891 if (connBus->GetElectricalDataRef().number < 0) indMotor->
SetOnline(
false);
893 double w0 = 2.0 * M_PI * m_systemFreq;
894 std::complex<double> i1 = std::complex<double>(dataPU.activePower, -data.q0) / std::conj(data.terminalVoltage);
895 double ir = std::real(i1);
896 double im = std::imag(i1);
897 std::complex<double> e = data.terminalVoltage - std::complex<double>(data.r1t, data.x1t) * i1;
898 double te = std::real(e * std::conj(i1)) / w0;
900 double wi = w0 * (1 - data.s0);
901 data.as = te * (data.aw + (data.bw * w0) / wi + (data.cw * w0 * w0) / (wi * wi));
902 data.bs = te * ((data.bw * w0) / wi + (2.0 * data.cw * w0 * w0) / (wi * wi));
903 data.cs = (te * data.cw * w0 * w0) / (wi * wi);
905 data.aCalc = data.as;
906 data.bCalc = data.bs;
907 data.cCalc = data.cs;
910 std::complex<double> tranE =
911 (std::complex<double>(0, data.x0 - data.xt) * i1) / std::complex<double>(1.0, w0 * data.s0 * data.t0);
913 data.tranEr = std::real(tranE);
914 data.tranEm = std::imag(tranE);
924 data.slip = 1.0 - 1e-7;
931 data.oldTe = data.te;
932 data.oldIr = data.ir;
933 data.oldIm = data.im;
936 data.slipVector.clear();
937 data.slipVector.shrink_to_fit();
938 data.electricalTorqueVector.clear();
939 data.electricalTorqueVector.shrink_to_fit();
940 data.mechanicalTorqueVector.clear();
941 data.mechanicalTorqueVector.shrink_to_fit();
942 data.velocityVector.clear();
943 data.velocityVector.shrink_to_fit();
944 data.currentVector.clear();
945 data.currentVector.shrink_to_fit();
946 data.terminalVoltageVector.clear();
947 data.terminalVoltageVector.shrink_to_fit();
948 data.activePowerVector.clear();
949 data.activePowerVector.shrink_to_fit();
950 data.reactivePowerVector.clear();
951 data.reactivePowerVector.shrink_to_fit();
955 CalculateReferenceSpeed();
959bool Electromechanical::CalculateInjectedCurrents()
962 for (
unsigned int i = 0; i < m_iBus.size(); ++i) m_iBus[i] = std::complex<double>(0.0, 0.0);
967 auto& data = syncGenerator->GetElectricalDataRef();
970 if (data.useMachineBase) {
971 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
972 k = m_powerSystemBase / oldBase;
975 double ra = data.armResistance * k;
976 double xp = data.potierReactance * k;
977 if (xp == 0.0) xp = 0.8 * data.transXd * k;
979 int n =
static_cast<Bus*
>(syncGenerator->
GetParentList()[0])->GetElectricalDataRef().number;
980 std::complex<double> e = std::complex<double>(0.0, 0.0);
981 std::complex<double> v = m_vBus[n];
982 std::complex<double> iInj = m_iBus[n];
984 auto smModelData = GetSyncMachineModelData(syncGenerator);
985 DQ0toABC(smModelData.ed, smModelData.eq, data.delta, e);
986 double xd = smModelData.xd;
987 double xq = smModelData.xq;
994 if (data.satFactor != 0.0) {
995 if (!CalculateSyncMachineSaturation(syncGenerator,
id, iq, sd, sq,
false, k))
return false;
998 double xdq, xds, xqs, xdqs;
999 xdq = 0.5 * (xd + xq);
1000 xds = (xd - xp) / sd + xp;
1001 xqs = (xq - xp) / sq + xp;
1002 xdqs = 0.5 * (xds + xqs);
1004 std::complex<double> y0 = std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0);
1005 std::complex<double> iUnadjusted = y0 * v;
1010 std::complex<double> iSaliency = std::complex<double>(0.0, -((0.5 * (xqs - xds)) / (ra * ra + xds * xqs))) *
1011 (std::conj(e) - std::conj(v));
1012 iSaliency = iSaliency * std::cos(2.0 * data.delta) +
1013 iSaliency * std::complex<double>(0.0, std::sin(2.0 * data.delta));
1016 std::complex<double> y0s = std::complex<double>(ra, -xdqs) / std::complex<double>(ra * ra + xds * xqs, 0.0);
1017 std::complex<double> iSaturation = y0s * (e - v);
1019 iInj = iUnadjusted + iSaliency + iSaturation;
1025 std::complex<double> iMachine = iInj - iUnadjusted;
1026 data.electricalPower = v * std::conj(iMachine);
1028 ABCtoDQ0(iMachine, data.delta,
id, iq);
1036 data.electricalPower = std::complex<double>(0.0, 0.0);
1044 auto& data = motor->GetElectricalDataRef();
1046 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalDataRef().number;
1047 std::complex<double> y0 = std::complex<double>(1.0, 0.0) / std::complex<double>(data.r1t, data.xt);
1048 std::complex<double> v = m_vBus[n];
1049 std::complex<double> e = std::complex<double>(data.tranEr, data.tranEm);
1050 std::complex<double> iInj = y0 * e;
1053 std::complex<double> iMachine = y0 * (v - e);
1055 data.ir = std::real(iMachine);
1056 data.im = std::imag(iMachine);
1068 auto& data = load->GetElectricalDataRef();
1075 data.voltage = m_vBus[bus->GetElectricalDataRef().number];
1076 double vAbs = std::abs(data.voltage);
1078 double pz, pi, pp, qz, qi, qp;
1079 pz = data.pz0 * std::pow(vAbs / data.v0, 2);
1080 pi = data.pi0 * (vAbs / data.v0);
1082 qz = data.qz0 * std::pow(vAbs / data.v0, 2);
1083 qi = data.qi0 * (vAbs / data.v0);
1087 if (vAbs < data.constCurrentUV) {
1088 pi = data.pi0 * (data.constCurrentUV / data.v0) * std::pow(vAbs / data.constCurrentUV, 2);
1089 qi = data.qi0 * (data.constCurrentUV / data.v0) * std::pow(vAbs / data.constCurrentUV, 2);
1091 if (vAbs < data.constPowerUV) {
1092 pp *= std::pow(vAbs / data.constPowerUV, 2);
1093 qp *= std::pow(vAbs / data.constPowerUV, 2);
1096 double activePower = pz + pi + pp;
1097 double reactivePower = qz + qi + qp;
1099 std::complex<double> newY = std::complex<double>(activePower, -reactivePower) / (vAbs * vAbs);
1100 m_iBus[bus->GetElectricalDataRef().number] += (data.y0 - newY) * data.voltage;
1102 data.electricalPower = std::complex<double>(activePower, reactivePower);
1105 data.voltage = std::complex<double>(0.0, 0.0);
1106 data.electricalPower = std::complex<double>(0.0, 0.0);
1114void Electromechanical::CalculateIntegrationConstants(
SyncGenerator* syncGenerator,
double id,
double iq,
double k)
1116 CalculateReferenceSpeed();
1117 auto& data = syncGenerator->GetElectricalDataRef();
1119 double syncXd, syncXq, transXd, transXq, subXd, subXq;
1120 syncXd = data.syncXd * k;
1121 syncXq = data.syncXq * k;
1122 transXd = data.transXd * k;
1123 transXq = data.transXq * k;
1124 subXd = data.subXd * k;
1125 subXq = data.subXq * k;
1127 if (syncXq == 0.0) syncXq = syncXd;
1128 if (transXq == 0.0) transXq = transXd;
1129 if (subXd == 0.0) subXd = subXq;
1130 if (subXq == 0.0) subXq = subXd;
1132 double transTd0, transTq0, subTd0, subTq0;
1133 transTd0 = data.transTd0;
1134 transTq0 = data.transTq0;
1135 subTd0 = data.subTd0;
1136 subTq0 = data.subTq0;
1138 if (subTd0 == 0.0) subTd0 = subTq0;
1139 if (subTq0 == 0.0) subTq0 = subTd0;
1142 data.icSpeed.m = m_timeStep / ((4.0f * data.inertia / m_refSpeed) / k + m_timeStep * data.damping * k);
1143 data.icSpeed.c = (1.0f - 2.0f * data.icSpeed.m * data.damping * k) * data.speed +
1144 data.icSpeed.m * (data.pm - data.pe + 2.0f * m_refSpeed * data.damping * k);
1147 data.icDelta.m = 0.5f * m_timeStep;
1148 data.icDelta.c = data.delta + data.icDelta.m * (data.speed - 2.0f * m_refSpeed);
1151 if (data.model == Machines::SM_MODEL_2 || data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 ||
1152 data.model == Machines::SM_MODEL_5) {
1153 data.icTranEq.m = m_timeStep / (2.0f * transTd0 + m_timeStep);
1154 data.icTranEq.c = (1.0 - data.icTranEq.m * (1.0 + data.sd)) * data.tranEq +
1155 data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id);
1159 if (data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
1160 data.icTranEd.m = m_timeStep / (2.0f * transTq0 + m_timeStep);
1162 (1.0 - data.icTranEd.m * (1.0 + data.sq)) * data.tranEd - data.icTranEd.m * (syncXq - transXq) * iq;
1166 if (data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
1167 data.icSubEq.m = m_timeStep / (2.0f * subTd0 + m_timeStep);
1168 data.icSubEq.c = (1.0 - data.icSubEq.m * (1.0 + data.sd)) * data.subEq +
1169 data.icSubEq.m * (data.sd * data.tranEq + (transXd - subXd) *
id);
1172 if (data.model == Machines::SM_MODEL_4) {
1173 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1175 (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd - data.icSubEd.m * (syncXq - subXq) * iq;
1177 if (data.model == Machines::SM_MODEL_5) {
1178 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1179 data.icSubEd.c = (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd +
1180 data.icSubEd.m * (data.sq * data.tranEd - (transXq - subXq) * iq);
1186void Electromechanical::CalculateIntegrationConstants(
IndMotor* indMotor,
double ir,
double im,
double k)
1188 double w0 = 2.0 * M_PI * m_systemFreq;
1190 auto& data = indMotor->GetElectricalDataRef();
1193 if (data.slip > 0.99999) {
1199 data.as = data.aCalc;
1200 data.bs = data.bCalc;
1201 data.cs = data.cCalc;
1206 data.icSlip.m = m_timeStep / ((4.0f * data.inertia / w0) / k + data.bs * m_timeStep);
1207 data.icSlip.c = data.slip * (1.0 - 2.0 * data.bs * data.icSlip.m) +
1208 data.icSlip.m * (2.0 * data.as + data.cs * data.slip * data.slip - data.te);
1212 data.icTranEr.m = m_timeStep / (2.0 * data.t0 + m_timeStep);
1213 data.icTranEr.c = data.tranEr * (1.0 - 2.0 * data.icTranEr.m) +
1214 data.icTranEr.m * (w0 * data.t0 * data.slip * data.tranEm - (data.x0 - data.xt) * im);
1216 data.icTranEm.m = m_timeStep / (2.0 * data.t0 + m_timeStep);
1217 data.icTranEm.c = data.tranEm * (1.0 - 2.0 * data.icTranEm.m) -
1218 data.icTranEm.m * (w0 * data.t0 * data.slip * data.tranEr - (data.x0 - data.xt) * ir);
1223bool Electromechanical::SolveMachines()
1229 const auto& data = syncGenerator->GetElectricalDataRef();
1232 double id, iq, pe, sd, sq;
1240 if (data.useMachineBase) {
1241 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1242 k = m_powerSystemBase / oldBase;
1246 CalculateIntegrationConstants(syncGenerator,
id, iq, k);
1248 if (!CalculateNonIntVariables(syncGenerator,
id, iq, sd, sq, pe, k))
return false;
1250 id = 2.0 *
id - data.oldId;
1251 iq = 2.0 * iq - data.oldIq;
1252 pe = 2.0 * pe - data.oldPe;
1253 sd = 2.0 * sd - data.oldSd;
1254 sq = 2.0 * sq - data.oldSq;
1256 CalculateIntVariables(syncGenerator,
id, iq, sd, sq, pe, k);
1259 CalculateIntegrationConstants(syncGenerator, 0.0f, 0.0f);
1265 const auto& data = indMotor->GetElectricalDataRef();
1274 if (data.useMachinePowerAsBase) {
1275 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1276 k = m_powerSystemBase / oldBase;
1280 CalculateIntegrationConstants(indMotor, ir, im, k);
1282 if (!CalculateNonIntVariables(indMotor, ir, im, te, k))
return false;
1284 ir = 2.0 * ir - data.oldIr;
1285 im = 2.0 * im - data.oldIm;
1286 te = 2.0 * te - data.oldTe;
1288 CalculateIntVariables(indMotor, ir, im, te, k);
1296 while (error > m_tolerance) {
1300 if (!CalculateInjectedCurrents())
return false;
1303 m_vBus =
LUEvaluate(m_yBusU, m_yBusL, m_iBus);
1309 const auto& data = syncGenerator->GetElectricalDataRef();
1311 double id = data.id;
1312 double iq = data.iq;
1313 double pe = data.pe;
1314 double sd = data.sd;
1315 double sq = data.sq;
1319 if (data.useMachineBase) {
1320 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1321 k = m_powerSystemBase / oldBase;
1325 if (!CalculateNonIntVariables(syncGenerator,
id, iq, sd, sq, pe, k))
return false;
1327 double genError = CalculateIntVariables(syncGenerator,
id, iq, sd, sq, pe, k);
1329 if (genError > error) error = genError;
1336 const auto& data = indMotor->GetElectricalDataRef();
1338 double ir = data.ir;
1339 double im = data.im;
1340 double te = data.te;
1344 if (data.useMachinePowerAsBase) {
1345 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1346 k = m_powerSystemBase / oldBase;
1350 if (!CalculateNonIntVariables(indMotor, ir, im, te, k))
return false;
1352 double motorError = CalculateIntVariables(indMotor, ir, im, te, k);
1354 if (motorError > error) error = motorError;
1359 if (iterations > m_maxIterations) {
1360 m_errorMsg = _(
"Impossible to solve the system machines.\nCheck the system parameters and/or "
1361 "decrease the time step.");
1365 m_iterationsNum = iterations;
1368 int ctrlRatio =
static_cast<int>(1 / m_ctrlTimeStepMultiplier);
1371 auto& data = syncGenerator->GetElectricalDataRef();
1372 if (data.useAVR && data.avrSolver) {
1373 data.avrSolver->SetSwitchStatus(syncGenerator->
IsOnline());
1374 data.avrSolver->SetCurrentTime(m_currentTime);
1375 data.avrSolver->SetTerminalVoltage(std::abs(data.terminalVoltage));
1376 data.avrSolver->SetDeltaActivePower((data.electricalPower.real() - data.avrSolver->GetActivePower()) /
1378 data.avrSolver->SetActivePower(data.electricalPower.real());
1379 data.avrSolver->SetReactivePower(data.electricalPower.imag());
1380 data.avrSolver->SetDeltaVelocity((data.speed - data.avrSolver->GetVelocity()) / m_timeStep);
1381 data.avrSolver->SetVelocity(data.speed);
1383 for (
int i = 0; i < ctrlRatio; ++i) data.avrSolver->SolveNextStep();
1385 data.fieldVoltage = data.initialFieldVoltage + data.avrSolver->GetFieldVoltage();
1388 if (data.useSpeedGovernor && data.speedGovSolver) {
1389 data.speedGovSolver->SetSwitchStatus(syncGenerator->
IsOnline());
1390 data.speedGovSolver->SetCurrentTime(m_currentTime);
1391 data.speedGovSolver->SetVelocity(data.speed);
1392 data.speedGovSolver->SetActivePower(data.electricalPower.real());
1393 data.speedGovSolver->SetReactivePower(data.electricalPower.imag());
1395 for (
int i = 0; i < ctrlRatio; ++i) data.speedGovSolver->SolveNextStep();
1397 data.pm = data.speedGovSolver->GetMechanicalPower();
1405void Electromechanical::SaveData()
1409 auto& data = syncGenerator->GetElectricalDataRef();
1410 if (data.plotSyncMachine) { syncGenerator->SavePlotData(); }
1414 auto& data = bus->GetElectricalDataRef();
1416 data.stabVoltageVector.emplace_back(data.number >= 0 ? m_vBus[data.number] : 0.0);
1417 data.stabFreqVector.emplace_back(data.number >= 0 ? data.stabFreq : 0.0);
1422 auto& data = load->GetElectricalDataRef();
1423 if (data.plotLoad) {
1424 data.voltageVector.emplace_back(data.voltage);
1425 data.electricalPowerVector.emplace_back(data.electricalPower);
1431 auto& data = motor->GetElectricalDataRef();
1432 if (data.plotIndMachine) {
1433 data.slipVector.emplace_back(data.slip * 100.0);
1434 data.electricalTorqueVector.emplace_back(data.te * 2.0 * M_PI * m_systemFreq);
1435 double tm = (data.as - data.bs * data.slip + data.cs * data.slip * data.slip) * 2.0 * M_PI * m_systemFreq;
1436 data.mechanicalTorqueVector.emplace_back(tm);
1437 double w = (1.0 - data.slip) * 2.0 * M_PI * m_systemFreq;
1438 data.velocityVector.emplace_back(w);
1439 std::complex<double> i1 = std::complex<double>(data.ir, data.im);
1440 data.currentVector.emplace_back(std::abs(i1));
1441 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalDataRef().number;
1442 std::complex<double> v = n >= 0 ? m_vBus[n] : 0.0;
1443 data.terminalVoltageVector.emplace_back(std::abs(v));
1444 std::complex<double> s = v * std::conj(i1);
1445 data.activePowerVector.emplace_back(std::real(s));
1446 data.reactivePowerVector.emplace_back(std::imag(s));
1450 m_iterationsNumVector.emplace_back(m_iterationsNum);
1453void Electromechanical::SetSyncMachinesModel()
1457 auto& data = syncGenerator->GetElectricalDataRef();
1463bool Electromechanical::CalculateNonIntVariables(
SyncGenerator* syncGenerator,
1471 auto& data = syncGenerator->GetElectricalDataRef();
1473 if (
GetParentBus(syncGenerator, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalDataRef().number]; }
1476 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1478 if (data.satFactor != 0.0) {
1479 if (!CalculateSyncMachineSaturation(syncGenerator,
id, iq, sd, sq,
true, k))
return false;
1487 pe =
id * vd + iq * vq + (
id *
id + iq * iq) * data.armResistance * k;
1490 pe =
id = iq = 0.0f;
1503bool Electromechanical::CalculateNonIntVariables(
IndMotor* indMotor,
double& ir,
double& im,
double& te,
double k)
1505 auto& data = indMotor->GetElectricalDataRef();
1507 double w0 = 2.0 * M_PI * m_systemFreq;
1508 te = (data.tranEr * ir + data.tranEm * im) / w0;
1524double Electromechanical::CalculateIntVariables(
SyncGenerator* syncGenerator,
1533 auto& data = syncGenerator->GetElectricalDataRef();
1536 double w = data.icSpeed.c + data.icSpeed.m * (data.pm - pe);
1537 error = std::max(error, std::abs(data.speed - w) / m_refSpeed);
1539 double delta = data.icDelta.c + data.icDelta.m * w;
1540 error = std::max(error, std::abs(data.delta - delta));
1546 switch (data.model) {
1547 case Machines::SM_MODEL_1: {
1550 case Machines::SM_MODEL_2: {
1551 double syncXd, transXd;
1552 syncXd = data.syncXd * k;
1553 transXd = data.transXd * k;
1555 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id)) /
1556 (1.0 + data.icTranEq.m * (sd - 1.0));
1557 error = std::max(error, std::abs(data.tranEq - tranEq));
1559 data.tranEq = tranEq;
1561 case Machines::SM_MODEL_3: {
1562 double syncXd, syncXq, transXd, transXq;
1563 syncXd = data.syncXd * k;
1564 syncXq = data.syncXq * k;
1565 transXd = data.transXd * k;
1566 transXq = data.transXq * k;
1567 if (syncXq == 0.0) syncXq = syncXd;
1568 if (transXq == 0.0) transXq = transXd;
1570 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id)) /
1571 (1.0 + data.icTranEq.m * (sd - 1.0));
1572 error = std::max(error, std::abs(data.tranEq - tranEq));
1575 (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
1576 error = std::max(error, std::abs(data.tranEd - tranEd));
1578 data.tranEq = tranEq;
1579 data.tranEd = tranEd;
1582 std::complex<double> e;
1583 DQ0toABC(data.tranEd, data.tranEq, data.delta, e);
1584 data.terminalVoltage = e;
1587 case Machines::SM_MODEL_4: {
1588 double syncXd, syncXq, transXd, subXd, subXq;
1589 syncXd = data.syncXd * k;
1590 syncXq = data.syncXq * k;
1591 transXd = data.transXd * k;
1592 subXd = data.subXd * k;
1593 subXq = data.subXq * k;
1594 if (syncXq == 0.0) syncXq = syncXd;
1595 if (subXd == 0.0) subXd = subXq;
1596 if (subXq == 0.0) subXq = subXd;
1598 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id)) /
1599 (1.0 + data.icTranEq.m * (sd - 1.0));
1600 error = std::max(error, std::abs(data.tranEq - tranEq));
1602 double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) *
id)) /
1603 (1.0 + data.icSubEq.m * (sd - 1.0));
1604 error = std::max(error, std::abs(data.subEq - subEq));
1607 (data.icSubEd.c - data.icSubEd.m * ((syncXq - subXq) * iq)) / (1.0 + data.icSubEd.m * (sq - 1.0));
1608 error = std::max(error, std::abs(data.subEd - subEd));
1610 data.tranEq = tranEq;
1614 case Machines::SM_MODEL_5: {
1615 double syncXd, syncXq, transXd, transXq, subXd, subXq;
1616 syncXd = data.syncXd * k;
1617 syncXq = data.syncXq * k;
1618 transXd = data.transXd * k;
1619 transXq = data.transXq * k;
1620 subXd = data.subXd * k;
1621 subXq = data.subXq * k;
1622 if (syncXq == 0.0) syncXq = syncXd;
1623 if (transXq == 0.0) transXq = transXd;
1624 if (subXd == 0.0) subXd = subXq;
1625 if (subXq == 0.0) subXq = subXd;
1627 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) *
id)) /
1628 (1.0 + data.icTranEq.m * (sd - 1.0));
1629 error = std::max(error, std::abs(data.tranEq - tranEq));
1632 (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
1633 error = std::max(error, std::abs(data.tranEd - tranEd));
1635 double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) *
id)) /
1636 (1.0 + data.icSubEq.m * (sd - 1.0));
1637 error = std::max(error, std::abs(data.subEq - subEq));
1639 double subEd = (data.icSubEd.c + data.icSubEd.m * (sq * tranEd - (transXq - subXq) * iq)) /
1640 (1.0 + data.icSubEd.m * (sq - 1.0));
1641 error = std::max(error, std::abs(data.subEd - subEd));
1643 data.tranEq = tranEq;
1644 data.tranEd = tranEd;
1654double Electromechanical::CalculateIntVariables(
IndMotor* indMotor,
double ir,
double im,
double te,
double k)
1657 auto& data = indMotor->GetElectricalDataRef();
1658 double w0 = 2.0 * M_PI * m_systemFreq;
1662 double slip = data.slip;
1663 double ds = (data.icSlip.c + data.icSlip.m * (data.cs * slip * slip - te) - slip) /
1664 (1.0 - 2.0 * data.icSlip.m * data.cs * slip * slip);
1666 while (std::abs(ds) > 1e-8) {
1668 ds = (data.icSlip.c + data.icSlip.m * (data.cs * slip * slip - te) - slip) /
1669 (1.0 - 2.0 * data.icSlip.m * data.cs * slip * slip);
1671 if (iteration > m_maxIterations)
break;
1675 error = std::max(error, std::abs(data.slip - slip));
1680 data.t0 = (data.x2t + data.xmt) / (2.0 * M_PI * m_systemFreq * data.r2t * (1.0 + data.kf * data.r2t));
1683 double tranEr = data.icTranEr.c + data.icTranEr.m * (w0 * data.t0 * slip * data.tranEm - (data.x0 - data.xt) * im);
1684 error = std::max(error, std::abs(data.tranEr - tranEr));
1686 double tranEm = data.icTranEm.c - data.icTranEm.m * (w0 * data.t0 * slip * data.tranEr - (data.x0 - data.xt) * ir);
1687 error = std::max(error, std::abs(data.tranEm - tranEm));
1689 data.tranEr = tranEr;
1690 data.tranEm = tranEm;
1696void Electromechanical::CalculateReferenceSpeed()
1704 auto& data = syncGenerator->GetElectricalDataRef();
1706 if (data.useMachineBase) {
1707 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1708 k = m_powerSystemBase / oldBase;
1710 sumH += data.inertia / k;
1711 sumHW += data.inertia * data.speed / k;
1714 m_refSpeed = sumHW / sumH;
1717 m_refSpeed = 2.0 * M_PI * m_systemFreq;
1721bool Electromechanical::CalculateSyncMachineSaturation(
SyncGenerator* syncMachine,
1726 bool updateCurrents,
1730 auto& data = syncMachine->GetElectricalDataRef();
1731 auto smDataModel = GetSyncMachineModelData(syncMachine);
1734 if (
GetParentBus(syncMachine, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalDataRef().number]; }
1745 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1746 double deltaVd = smDataModel.ed - vd;
1747 double deltaVq = smDataModel.eq - vq;
1749 double ra = data.armResistance * k;
1750 double xd = smDataModel.xd;
1751 double xq = smDataModel.xq;
1753 double syncXd = data.syncXd * k;
1754 double syncXq = data.syncXq * k;
1755 if (data.model == Machines::SM_MODEL_1) {
1756 syncXq = data.transXd * k;
1759 else if (data.syncXq == 0.0)
1760 syncXq = data.syncXd * k;
1762 double xp = data.potierReactance * k;
1763 if (xp == 0.0) xp = 0.8 * data.transXd * k;
1764 double satFacd = (data.satFactor - 1.2) / std::pow(1.2, 7);
1765 double satFacq = satFacd * (syncXq / syncXd);
1770 double oldSd = sdCalc;
1771 double oldSq = sqCalc;
1774 double xds = (xd - xp) / sdCalc + xp;
1775 double xqs = (xq - xp) / sqCalc + xp;
1777 double den = 1.0 / (ra * ra + xds * xqs);
1778 iqCalc = den * (ra * deltaVq + xds * deltaVd);
1779 idCalc = den * (-xqs * deltaVq + ra * deltaVd);
1781 double epq = vq + ra * iqCalc - xp * idCalc;
1782 double epd = vd + ra * idCalc + xp * iqCalc;
1789 double f1 = 1.0 - sdCalc + satFacd * std::pow(epq, 6);
1790 double f2 = 1.0 - sqCalc + satFacq * std::pow(epd, 6);
1792 (6.0 * satFacd * std::pow(epq, 5) * xp * (xd - xp) * deltaVq) / ((sdCalc - 1.0) * xp + xd) - 1.0;
1794 (6.0 * satFacq * std::pow(epd, 5) * xp * (xq - xp) * deltaVd) / ((sqCalc - 1.0) * xp + xq) - 1.0;
1796 sdCalc = sdCalc - f1 / dF1dSd;
1797 sqCalc = sqCalc - f2 / dF2dSq;
1799 double error = std::abs(sdCalc - oldSd) + std::abs(sqCalc - oldSq);
1800 if (error < m_saturationTolerance) exit =
true;
1803 if ((iterations >= m_maxIterations) && !exit) {
1805 _(
"It was not possible to solve the saturation of the synchronous machine \"") + data.name + wxT(
"\".");
1812 if (updateCurrents) {
1819void Electromechanical::CalculateBusesFrequency(
bool hasEvent)
1821 bool ignoreEventStep =
true;
1823 auto& data = bus->GetElectricalDataRef();
1824 if (!data.plotBus)
continue;
1826 if (m_simData.busFreqEstimation == BusFreqEstimation::WASHOUT_FILTER) {
1835 double tf = m_simData.tf;
1836 double tw = m_simData.tw;
1838 double theta0 = std::arg(data.number >= 0 ? data.voltage : 0.0);
1841 auto fdxt = [
this, tf, theta0](
double theta,
double xt) {
1842 return (1.0 / tf) * ((1.0 / (2.0 * M_PI * m_systemFreq)) * (theta - theta0) - xt);
1846 auto fddw = [
this, tw](
double dxt,
double dw) {
1847 return (1.0 / tw) * (dxt - dw);
1850 double theta = std::arg(data.number >= 0 ? m_vBus[data.number] : theta0);
1852 double xt = 0, dw = 0, dxt = 0, ddw = 0;
1854 while (error > 1e-6 && iterations <= 100) {
1855 double oldXT = xt, oldDW = dw;
1857 dxt = fdxt(theta, xt);
1858 xt = data.filteredAngle + 0.5 * m_timeStep * (data.dxt + dxt);
1859 error = std::abs(xt - oldXT);
1861 ddw = fddw(dxt, dw);
1862 dw = data.filteredVelocity + 0.5 * m_timeStep * (data.ddw + ddw);
1863 error = std::max(error, std::abs(dw - oldDW));
1874 data.filteredAngle = xt;
1875 data.filteredVelocity = dw;
1879 double busVelocity = m_refSpeed + dw * (2.0 * M_PI * m_systemFreq);
1880 data.stabFreq = busVelocity / (2.0 * M_PI);
1884 else if (m_simData.busFreqEstimation == BusFreqEstimation::ANGLE_DERIVATION) {
1885 double newAngle = std::arg(data.number >= 0 ? m_vBus[data.number] : 0.0);
1889 double dw = (newAngle - data.oldAngle) / m_timeStep;
1892 if (m_simData.ignoreBusFreqEventStep && hasEvent) dw = 0.0;
1895 data.stabFreq = (m_refSpeed + dw) / (2.0 * M_PI);
1898 data.oldAngle = newAngle;
1900 bus->SetElectricalData(data);
1908 const auto& data = syncMachine->GetElectricalDataRef();
1910 if (data.useMachineBase) {
1911 double oldBase = syncMachine->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1912 k = m_powerSystemBase / oldBase;
1915 switch (data.model) {
1916 case Machines::SM_MODEL_1: {
1917 smModelData.
ed = data.tranEd;
1918 smModelData.
eq = data.tranEq;
1919 smModelData.
xq = data.transXd * k;
1920 smModelData.
xd = smModelData.
xq;
1922 case Machines::SM_MODEL_2: {
1923 smModelData.
ed = data.tranEd;
1924 smModelData.
eq = data.tranEq;
1925 smModelData.
xd = data.transXd * k;
1926 smModelData.
xq = data.transXq * k;
1927 if (smModelData.
xq == 0.0) {
1928 smModelData.
xq = data.syncXq * k;
1929 if (smModelData.
xq == 0.0) { smModelData.
xq = data.syncXd * k; }
1932 case Machines::SM_MODEL_3: {
1933 smModelData.
ed = data.tranEd;
1934 smModelData.
eq = data.tranEq;
1935 smModelData.
xd = data.transXd * k;
1936 smModelData.
xq = data.transXq * k;
1937 if (smModelData.
xq == 0.0) smModelData.
xq = smModelData.
xd;
1939 case Machines::SM_MODEL_4:
1940 case Machines::SM_MODEL_5: {
1941 smModelData.
ed = data.subEd;
1942 smModelData.
eq = data.subEq;
1943 smModelData.
xd = data.subXd * k;
1944 smModelData.
xq = data.subXq * k;
1945 if (smModelData.
xd == 0.0) smModelData.
xd = smModelData.
xq;
1946 if (smModelData.
xq == 0.0) smModelData.
xq = smModelData.
xd;
1952void Electromechanical::PreallocateVectors()
1954 int numPoints =
static_cast<unsigned int>(m_simTime / m_plotTime) + 100;
1956 m_timeVector.reserve(numPoints);
1959 auto& data = syncGenerator->GetElectricalDataRef();
1960 if (data.plotSyncMachine) {
1961 data.terminalVoltageVector.reserve(numPoints);
1962 data.electricalPowerVector.reserve(numPoints);
1963 data.mechanicalPowerVector.reserve(numPoints);
1964 data.freqVector.reserve(numPoints);
1965 data.fieldVoltageVector.reserve(numPoints);
1966 data.deltaVector.reserve(numPoints);
1972 auto& data = bus->GetElectricalDataRef();
1974 data.stabVoltageVector.reserve(numPoints);
1975 data.stabFreqVector.reserve(numPoints);
1981 auto& data = load->GetElectricalDataRef();
1982 if (data.plotLoad) {
1983 data.voltageVector.reserve(numPoints);
1984 data.electricalPowerVector.reserve(numPoints);
1990 auto& data = motor->GetElectricalDataRef();
1991 if (data.plotIndMachine) {
1992 data.slipVector.reserve(numPoints);
1993 data.electricalTorqueVector.reserve(numPoints);
1994 data.mechanicalTorqueVector.reserve(numPoints);
1995 data.velocityVector.reserve(numPoints);
1996 data.currentVector.reserve(numPoints);
1997 data.terminalVoltageVector.reserve(numPoints);
1998 data.activePowerVector.reserve(numPoints);
1999 data.reactivePowerVector.reserve(numPoints);
2004 m_iterationsNumVector.reserve(numPoints);
2007std::complex<double> Electromechanical::GetIndMachineAdmittance(
IndMotor* motor)
2009 const auto& data = motor->GetElectricalDataRef();
2010 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
2011 std::complex<double> v =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalDataRef().voltage;
2012 std::complex<double> y0 = (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
2014 std::complex<double> yq = std::complex<double>(0.0, data.q0 - dataPU.reactivePower) / (std::abs(v) * std::abs(v));
2018bool Electromechanical::InsertIndMachinesOnYBus()
2022 if (!CalculateIndMachinesTransientValues(motor))
return false;
2024 int n =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalDataRef().number;
2025 m_yBus[n][n] += GetIndMachineAdmittance(motor);
2031bool Electromechanical::CalculateIndMachinesTransientValues(
IndMotor* motor)
2033 auto& data = motor->GetElectricalDataRef();
2034 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
2036 if (data.useMachinePowerAsBase) {
2037 double oldBase = motor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
2038 k = m_powerSystemBase / oldBase;
2041 data.terminalVoltage =
static_cast<Bus*
>(motor->
GetParentList()[0])->GetElectricalDataRef().voltage;
2044 double r1t = data.r1 * k;
2045 double r2t = data.r2 * k;
2046 double x1t = data.x1 * k;
2047 double x2t = data.x2 * k;
2048 double xmt = data.xm * k;
2055 double xt = x1t + (x2t * xmt) / (x2t + xmt);
2056 double x0 = x1t + xmt;
2060 double p = dataPU.activePower;
2061 double v = std::abs(data.terminalVoltage);
2066 double r1 = data.r1t;
2067 double r2 = data.r2t;
2068 if (data.useKf) r2 *= (1.0 + data.kf * r2t);
2069 double x1 = data.x1t;
2070 double x2 = data.x2t;
2071 double xm = data.xmt;
2072 double k1 = x2 + xm;
2073 double k2 = -x1 * k1 - x2 * xm;
2074 double k3 = xm + x1;
2075 double k4 = r1 * k1;
2076 double a = p * (r1 * r1 + k3 * k3) - v * v * r1;
2077 double b = 2.0 * p * (r1 * k2 + k3 * k4) - v * v * (k2 + k1 * k3);
2078 double c = p * (k2 * k2 + k4 * k4) - v * v * k1 * k4;
2079 double d = (b * b - 4.0 * a * c);
2081 m_errorMsg = _(
"Error on initializate the slip of \"") + data.name + _(
"\".");
2084 double r2_s = (-b + std::sqrt(d)) / (2.0 * a);
2085 data.s0 = r2 / r2_s;
2087 double qa = k1 * (r2_s * r1 - x1 * k1 - x2 * xm);
2088 double qb = r2_s * (r2_s * (xm + x1) + r1 * k1);
2089 double qc = r2_s * r1 - x1 * k1 - x2 * xm;
2090 double qd = r2_s * (xm + x1) + r1 * k1;
2091 data.q0 = (-v * v * (qa - qb)) / (qc * qc + qd * qd);
2093 data.t0 = (x2t + xmt) / (2.0 * M_PI * m_systemFreq * r2);
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.