summaryrefslogtreecommitdiffstats
path: root/Project/Electromechanical.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Project/Electromechanical.cpp')
-rw-r--r--Project/Electromechanical.cpp561
1 files changed, 383 insertions, 178 deletions
diff --git a/Project/Electromechanical.cpp b/Project/Electromechanical.cpp
index 57a275a..4b8a711 100644
--- a/Project/Electromechanical.cpp
+++ b/Project/Electromechanical.cpp
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2017 Thales Lima Oliveira <thales@ufu.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
#include "Electromechanical.h"
#include "ControlElementSolver.h"
@@ -7,7 +24,8 @@ Electromechanical::Electromechanical(wxWindow* parent, std::vector<Element*> ele
GetElementsFromList(elementList);
SetEventTimeList();
- m_powerSystemBase = GetPowerValue(data.basePower, data.basePowerUnit);
+ Bus dummyBus;
+ m_powerSystemBase = dummyBus.GetValueFromUnit(data.basePower, data.basePowerUnit);
m_systemFreq = data.stabilityFrequency;
m_simTime = data.stabilitySimulationTime;
m_timeStep = data.timeStep;
@@ -397,42 +415,14 @@ std::complex<double> Electromechanical::GetSyncMachineAdmittance(SyncGenerator*
auto data = generator->GetElectricalData();
double k = 1.0; // Power base change factor.
if(data.useMachineBase) {
- double oldBase = GetPowerValue(data.nominalPower, data.nominalPowerUnit);
+ double oldBase = generator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
k = m_powerSystemBase / oldBase;
}
- double xd = 0.0;
- double xq = 0.0;
double ra = data.armResistance * k;
-
- switch(data.model) {
- case Machines::SM_MODEL_1: {
- xq = data.transXd * k;
- xd = xq;
- } break;
- case Machines::SM_MODEL_2: {
- xd = data.transXd * k;
- xq = data.transXq * k;
- if(xq == 0.0) {
- xq = data.syncXq * k;
- if(xq == 0.0) {
- xq = data.syncXd * k;
- }
- }
- } break;
- case Machines::SM_MODEL_3: {
- xd = data.transXd * k;
- xq = data.transXq * k;
- if(xq == 0.0) xq = xd;
- } break;
- case Machines::SM_MODEL_4:
- case Machines::SM_MODEL_5: {
- xd = data.subXd * k;
- xq = data.subXq * k;
- if(xd == 0.0) xd = xq;
- if(xq == 0.0) xq = xd;
- } break;
- }
+ auto smModelData = GetSyncMachineModelData(generator);
+ double xd = smModelData.xd;
+ double xq = smModelData.xq;
double xdq = 0.5 * (xd + xq);
return (std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0));
}
@@ -454,14 +444,14 @@ bool Electromechanical::InitializeDynamicElements()
if(syncGenerator->IsOnline()) {
double k = 1.0; // Power base change factor.
if(data.useMachineBase) {
- double oldBase = GetPowerValue(data.nominalPower, data.nominalPowerUnit);
+ double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
k = m_powerSystemBase / oldBase;
}
data.terminalVoltage = static_cast<Bus*>(syncGenerator->GetParentList()[0])->GetElectricalData().voltage;
std::complex<double> conjS(dataPU.activePower, -dataPU.reactivePower);
- std::complex<double> conjV = std::conj(data.terminalVoltage);
- std::complex<double> ia = conjS / conjV;
+ std::complex<double> vt = data.terminalVoltage;
+ std::complex<double> ia = conjS / std::conj(vt);
double xd = data.syncXd * k;
double xq = data.syncXq * k;
@@ -473,28 +463,86 @@ bool Electromechanical::InitializeDynamicElements()
} else if(data.syncXq == 0.0)
xq = data.syncXd * k;
+ double sd = 1.0;
+ double sq = 1.0;
+ double satF = 1.0;
+ double xp = data.potierReactance * k;
+ bool hasSaturation = false;
+ if(data.satFactor != 0.0) { // Have saturation.
+ satF = (data.satFactor - 1.2) / std::pow(1.2, 7);
+ if(xp == 0.0) xp = 0.8 * (data.transXd * k);
+ hasSaturation = true;
+ }
+
// Initialize state variables
- std::complex<double> eq0 = data.terminalVoltage + std::complex<double>(ra, xq) * ia;
- data.delta = std::arg(eq0);
+ std::complex<double> eq0 = vt + std::complex<double>(ra, xq) * ia;
+ double delta = std::arg(eq0);
+
+ double id0, iq0, vd0, vq0;
+ ABCtoDQ0(ia, delta, id0, iq0);
+ ABCtoDQ0(vt, delta, vd0, vq0);
+
+ // Initialize saturation
+ double xqs = xq;
+ double xds = xd;
+ if(hasSaturation) {
+ double oldDelta = 0;
+ bool exit = false;
+ int numIt = 0;
+ while(!exit) {
+ oldDelta = delta;
+ ABCtoDQ0(ia, delta, id0, iq0);
+ ABCtoDQ0(vt, delta, vd0, vq0);
+
+ // Direct-axis Potier voltage.
+ double epd = vd0 + ra * id0 + xp * iq0;
+
+ sq = 1.0 + satF * (xq / xd) * std::pow(epd, 6);
+ xqs = (xq - xp) / sq + xp;
+ eq0 = data.terminalVoltage + std::complex<double>(ra, xqs) * ia;
+ delta = std::arg(eq0);
+ if(std::abs(delta - oldDelta) < m_saturationTolerance) {
+ exit = true;
+ } else if(numIt >= m_maxIterations) {
+ m_errorMsg = _("Error on initializate the saturation values of \"") + data.name + _("\".");
+ return false;
+ }
+ numIt++;
+ }
+ // Quadrature-axis Potier voltage.
+ double epq = vq0 + ra * iq0 - xp * id0;
+ sd = 1.0 + satF * std::pow(epq, 6);
+ xds = (xd - xp) / sd + xp;
+ /*CalculateSyncMachineSaturation(syncGenerator, id0, iq0, sq, sd, true, k);
+ xqs = (xq - xp) / sq + xp;
+ xds = (xd - xp) / sd + xp;
+ eq0 = data.terminalVoltage + std::complex<double>(ra, xqs) * ia;
+ delta = std::arg(eq0);*/
+ }
- double fi0 = std::arg(ia);
- double id0, iq0;
- // ABCtoDQ0(ia, data.delta - fi0, id0, iq0);
- iq0 = std::abs(ia) * std::cos(data.delta - fi0);
- id0 = -std::abs(ia) * std::sin(data.delta - fi0);
+ double ef0 = vq0 + ra * iq0 - xds * id0;
- data.initialFieldVoltage = std::abs(eq0) - (xd - xq) * id0;
+ data.initialFieldVoltage = ef0 * sd;
data.fieldVoltage = data.initialFieldVoltage;
data.pm = std::real((data.terminalVoltage * std::conj(ia)) + (std::abs(ia) * std::abs(ia) * ra));
data.speed = 2.0 * M_PI * m_systemFreq;
-
+ data.delta = delta;
data.pe = data.pm;
data.electricalPower = std::complex<double>(dataPU.activePower, dataPU.reactivePower);
+ data.sd = sd;
+ data.sq = sq;
+ data.id = id0;
+ data.iq = iq0;
// Variables to extrapolate.
data.oldIq = iq0;
data.oldId = id0;
data.oldPe = data.pe;
+ data.oldSd = sd;
+ data.oldSq = sq;
+
+ m_sdC = sd;
+ m_sqC = sq;
switch(data.model) {
case Machines::SM_MODEL_1: {
@@ -510,7 +558,7 @@ bool Electromechanical::InitializeDynamicElements()
case Machines::SM_MODEL_2: {
double tranXd = data.transXd * k;
- data.tranEq = data.initialFieldVoltage + (xd - tranXd) * id0;
+ data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
data.tranEd = 0.0;
data.subEd = 0.0;
data.subEq = 0.0;
@@ -520,8 +568,8 @@ bool Electromechanical::InitializeDynamicElements()
double tranXq = data.transXq * k;
if(tranXq == 0.0) tranXq = tranXd;
- data.tranEq = data.initialFieldVoltage + (xd - tranXd) * id0;
- data.tranEd = -(xq - tranXq) * iq0;
+ data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
+ data.tranEd = -(xq - tranXq) * (iq0 / sq);
data.subEd = 0.0;
data.subEq = 0.0;
@@ -533,10 +581,10 @@ bool Electromechanical::InitializeDynamicElements()
if(subXd == 0.0) subXd = subXq;
if(subXq == 0.0) subXq = subXd;
- data.tranEq = data.initialFieldVoltage + (xd - tranXd) * id0;
+ data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
data.tranEd = 0.0;
- data.subEq = data.tranEq + (tranXd - subXd) * id0;
- data.subEd = -(xq - subXq) * iq0;
+ data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
+ data.subEd = -(xq - subXq) * (iq0 / sq);
} break;
case Machines::SM_MODEL_5: {
double tranXd = data.transXd * k;
@@ -546,10 +594,10 @@ bool Electromechanical::InitializeDynamicElements()
if(subXd == 0.0) subXd = subXq;
if(subXq == 0.0) subXq = subXd;
- data.tranEq = data.initialFieldVoltage + (xd - tranXd) * id0;
- data.tranEd = -(xq - tranXq) * iq0;
- data.subEq = data.tranEq + (tranXd - subXd) * id0;
- data.subEd = data.tranEd - (tranXq - subXq) * iq0;
+ data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
+ data.tranEd = -(xq - tranXq) * (iq0 / sq);
+ data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
+ data.subEd = data.tranEd - (tranXq - subXq) * (iq0 / sq);
} break;
default:
break;
@@ -593,7 +641,7 @@ bool Electromechanical::InitializeDynamicElements()
return true;
}
-void Electromechanical::CalculateMachinesCurrents()
+bool Electromechanical::CalculateMachinesCurrents()
{
// Reset injected currents vector
for(unsigned int i = 0; i < m_iBus.size(); ++i) m_iBus[i] = std::complex<double>(0.0, 0.0);
@@ -604,73 +652,77 @@ void Electromechanical::CalculateMachinesCurrents()
if(syncGenerator->IsOnline()) {
double k = 1.0; // Power base change factor.
if(data.useMachineBase) {
- double oldBase = GetPowerValue(data.nominalPower, data.nominalPowerUnit);
+ double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
k = m_powerSystemBase / oldBase;
}
- double xd = 0.0;
- double xq = 0.0;
double ra = data.armResistance * k;
+ double xp = data.potierReactance * k;
+ if(xp == 0.0) xp = 0.8 * data.transXd * k;
+
int n = static_cast<Bus*>(syncGenerator->GetParentList()[0])->GetElectricalData().number;
std::complex<double> e = std::complex<double>(0.0, 0.0);
std::complex<double> v = m_vBus[n];
std::complex<double> iInj = m_iBus[n];
- double xdq = 0.0;
- switch(data.model) {
- case Machines::SM_MODEL_1: {
- DQ0toABC(data.tranEd, data.tranEq, data.delta, e);
- xq = data.transXd * k;
- xd = xq;
- } break;
- case Machines::SM_MODEL_2: {
- DQ0toABC(data.tranEd, data.tranEq, data.delta, e);
- xd = data.transXd * k;
- xq = data.transXq * k;
- if(xq == 0.0) {
- xq = data.syncXq * k;
- if(xq == 0.0) {
- xq = data.syncXd * k;
- }
- }
- } break;
- case Machines::SM_MODEL_3: {
- DQ0toABC(data.tranEd, data.tranEq, data.delta, e);
- xd = data.transXd * k;
- xq = data.transXq * k;
- if(xq == 0.0) xq = xd;
- } break;
- case Machines::SM_MODEL_4:
- case Machines::SM_MODEL_5: {
- DQ0toABC(data.subEd, data.subEq, data.delta, e);
- xd = data.subXd * k;
- xq = data.subXq * k;
- if(xd == 0.0) xd = xq;
- if(xq == 0.0) xq = xd;
- } break;
+ auto smModelData = GetSyncMachineModelData(syncGenerator);
+ DQ0toABC(smModelData.ed, smModelData.eq, data.delta, e);
+ double xd = smModelData.xd;
+ double xq = smModelData.xq;
+
+ double sd = data.sd;
+ double sq = data.sq;
+ double id, iq;
+
+ // Calculate the saturation effect
+ if(data.satFactor != 0.0) {
+ if(!CalculateSyncMachineSaturation(syncGenerator, id, iq, sd, sq, false, k)) return false;
}
+
+ double xdq, xds, xqs, xdqs;
xdq = 0.5 * (xd + xq);
+ xds = (xd - xp) / sd + xp;
+ xqs = (xq - xp) / sq + xp;
+ xdqs = 0.5 * (xds + xqs);
std::complex<double> y0 = std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0);
- std::complex<double> iUnadj = y0 * e;
+ // std::complex<double> iUnadjusted = y0 * e;
+ std::complex<double> iUnadjusted = y0 * v;
- std::complex<double> iAdj =
- std::complex<double>(0.0, -((0.5 * (xq - xd)) / (ra * ra + xd * xq))) * (std::conj(e) - std::conj(v));
- iAdj = iAdj * std::cos(2.0 * data.delta) + iAdj * std::complex<double>(0.0, std::sin(2.0 * data.delta));
+ // [Ref] Arrillaga, J.; Arnold, C. P.. "Computer Modelling of Electrical Power Systems". Pg. 225-226
+ // [Ref] Dommell, H. W.; Sato, N.. "Fast transient stability solutions". IEEE Transactions on Power
+ // Apparatus and Systems, PAS-91 (4), 1643-1650
+ std::complex<double> iSaliency = std::complex<double>(0.0, -((0.5 * (xqs - xds)) / (ra * ra + xds * xqs))) *
+ (std::conj(e) - std::conj(v));
+ iSaliency = iSaliency * std::cos(2.0 * data.delta) +
+ iSaliency * std::complex<double>(0.0, std::sin(2.0 * data.delta));
- iInj = iUnadj + iAdj;
+ // [Ref] Arrillaga, J.; Arnold, C. P.; Computer Modelling of Electrical Power Systems. Pg. 258-259
+ std::complex<double> y0s = std::complex<double>(ra, -xdqs) / std::complex<double>(ra * ra + xds * xqs, 0.0);
+ std::complex<double> iSaturation = y0s * (e - v);
- m_iBus[n] += iInj;
+ iInj = iUnadjusted + iSaliency + iSaturation;
- std::complex<double> iMachine = iInj - y0 * v;
+ m_iBus[n] += iInj;
+ // Remove the current flowing through y0 (i.e. iUnadjusted in this case, y0 is inserted in admittance
+ // matrix) to calculate the electrical power.
+ std::complex<double> iMachine = iInj - iUnadjusted;
data.electricalPower = v * std::conj(iMachine);
+
+ ABCtoDQ0(iMachine, data.delta, id, iq);
+
+ data.id = id;
+ data.iq = iq;
+ data.sd = sd;
+ data.sq = sq;
} else {
data.electricalPower = std::complex<double>(0.0, 0.0);
}
syncGenerator->SetElectricalData(data);
}
+ return true;
}
void Electromechanical::CalculateIntegrationConstants(SyncGenerator* syncGenerator, double id, double iq, double k)
@@ -713,31 +765,41 @@ void Electromechanical::CalculateIntegrationConstants(SyncGenerator* syncGenerat
if(data.model == Machines::SM_MODEL_2 || data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 ||
data.model == Machines::SM_MODEL_5) {
data.icTranEq.m = m_timeStep / (2.0f * transTd0 + m_timeStep);
- data.icTranEq.c = (1.0f - 2.0 * data.icTranEq.m) * data.tranEq +
+ // data.icTranEq.c = (1.0f - 2.0 * data.icTranEq.m) * data.tranEq +
+ // data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id);
+ data.icTranEq.c = (1.0 - data.icTranEq.m * (1.0 + data.sd)) * data.tranEq +
data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id);
}
// Ed'
if(data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
data.icTranEd.m = m_timeStep / (2.0f * transTq0 + m_timeStep);
- data.icTranEd.c = (1.0f - 2.0f * data.icTranEd.m) * data.tranEd - data.icTranEd.m * (syncXq - transXq) * iq;
+ // data.icTranEd.c = (1.0f - 2.0f * data.icTranEd.m) * data.tranEd - data.icTranEd.m * (syncXq - transXq) * iq;
+ data.icTranEd.c =
+ (1.0 - data.icTranEd.m * (1.0 + data.sq)) * data.tranEd - data.icTranEd.m * (syncXq - transXq) * iq;
}
// Eq''
if(data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
data.icSubEq.m = m_timeStep / (2.0f * subTd0 + m_timeStep);
- data.icSubEq.c =
- (1.0f - 2.0f * data.icSubEq.m) * data.subEq + data.icSubEq.m * (data.tranEq + (transXd - subXd) * id);
+ // data.icSubEq.c =
+ // (1.0f - 2.0f * data.icSubEq.m) * data.subEq + data.icSubEq.m * (data.tranEq + (transXd - subXd) * id);
+ data.icSubEq.c = (1.0 - data.icSubEq.m * (1.0 + data.sd)) * data.subEq +
+ data.icSubEq.m * (data.sd * data.tranEq + (transXd - subXd) * id);
}
// Ed''
if(data.model == Machines::SM_MODEL_4) {
data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
- data.icSubEd.c = (1.0f - 2.0f * data.icSubEd.m) * data.subEd - data.icSubEd.m * (syncXq - subXq) * iq;
+ // data.icSubEd.c = (1.0f - 2.0f * data.icSubEd.m) * data.subEd - data.icSubEd.m * (syncXq - subXq) * iq;
+ data.icSubEd.c =
+ (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd - data.icSubEd.m * (syncXq - subXq) * iq;
}
if(data.model == Machines::SM_MODEL_5) {
data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
- data.icSubEd.c =
- (1.0f - 2.0f * data.icSubEd.m) * data.subEd + data.icSubEd.m * (data.tranEd - (transXq - subXq) * iq);
+ // data.icSubEd.c =
+ // (1.0f - 2.0f * data.icSubEd.m) * data.subEd + data.icSubEd.m * (data.tranEd - (transXq - subXq) * iq);
+ data.icSubEd.c = (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd +
+ data.icSubEd.m * (data.sq * data.tranEd - (transXq - subXq) * iq);
}
syncGenerator->SetElectricalData(data);
@@ -745,37 +807,39 @@ void Electromechanical::CalculateIntegrationConstants(SyncGenerator* syncGenerat
bool Electromechanical::SolveSynchronousMachines()
{
- // CalculateMachinesCurrents();
for(auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
SyncGenerator* syncGenerator = *it;
auto data = syncGenerator->GetElectricalData();
if(syncGenerator->IsOnline()) {
- int n = static_cast<Bus*>(syncGenerator->GetParentList()[0])->GetElectricalData().number;
- double id, iq, pe;
-
+ double id, iq, pe, sd, sq;
pe = data.pe;
+ id = data.id;
+ iq = data.iq;
+ sd = data.sd;
+ sq = data.sq;
double k = 1.0; // Power base change factor.
if(data.useMachineBase) {
- double oldBase = GetPowerValue(data.nominalPower, data.nominalPowerUnit);
+ double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
k = m_powerSystemBase / oldBase;
}
- std::complex<double> iMachine = std::conj(data.electricalPower) / std::conj(m_vBus[n]);
-
- ABCtoDQ0(iMachine, data.delta, id, iq);
-
// Calculate integration constants.
CalculateIntegrationConstants(syncGenerator, id, iq, k);
- CalculateSyncMachineNonIntVariables(syncGenerator, id, iq, pe, k);
+ if(!CalculateSyncMachineNonIntVariables(syncGenerator, id, iq, sd, sq, pe, k)) return false;
// Extrapolate nonintegrable variables.
id = 2.0 * id - data.oldId;
iq = 2.0 * iq - data.oldIq;
pe = 2.0 * pe - data.oldPe;
+ sd = 2.0 * sd - data.oldSd;
+ sq = 2.0 * sq - data.oldSq;
+
+ m_sdC = sd;
+ m_sqC = sq;
- CalculateSyncMachineIntVariables(syncGenerator, id, iq, pe, k);
+ CalculateSyncMachineIntVariables(syncGenerator, id, iq, sd, sq, pe, k);
} else {
CalculateIntegrationConstants(syncGenerator, 0.0f, 0.0f);
}
@@ -789,7 +853,7 @@ bool Electromechanical::SolveSynchronousMachines()
error = 0.0;
// Calculate the injected currents.
- CalculateMachinesCurrents();
+ if(!CalculateMachinesCurrents()) return false;
// Calculate the buses voltages.
m_vBus = LUEvaluate(m_yBusU, m_yBusL, m_iBus);
@@ -800,16 +864,23 @@ bool Electromechanical::SolveSynchronousMachines()
auto data = syncGenerator->GetElectricalData();
- double id, iq, pe;
- double k = 1.0; // Power base change factor.
+ double id = data.id;
+ double iq = data.iq;
+ double pe = data.pe;
+ double sd = data.sd;
+ double sq = data.sq;
+
+ // Power base change factor.
+ double k = 1.0;
if(data.useMachineBase) {
- double oldBase = GetPowerValue(data.nominalPower, data.nominalPowerUnit);
+ double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
k = m_powerSystemBase / oldBase;
}
- CalculateSyncMachineNonIntVariables(syncGenerator, id, iq, pe, k);
+ // Calculate id, iq, dq, sd
+ if(!CalculateSyncMachineNonIntVariables(syncGenerator, id, iq, sd, sq, pe, k)) return false;
- double genError = CalculateSyncMachineIntVariables(syncGenerator, id, iq, pe, k);
+ double genError = CalculateSyncMachineIntVariables(syncGenerator, id, iq, sd, sq, pe, k);
if(genError > error) error = genError;
}
@@ -844,46 +915,6 @@ bool Electromechanical::SolveSynchronousMachines()
return true;
}
-double Electromechanical::GetPowerValue(double value, ElectricalUnit unit)
-{
- switch(unit) {
- case UNIT_PU: {
- return value;
- } break;
- case UNIT_VA: {
- return value;
- } break;
- case UNIT_kVA: {
- return value * 1e3;
- } break;
- case UNIT_MVA: {
- return value * 1e6;
- } break;
- case UNIT_W: {
- return value;
- } break;
- case UNIT_kW: {
- return value * 1e3;
- } break;
- case UNIT_MW: {
- return value * 1e6;
- } break;
- case UNIT_VAr: {
- return value;
- } break;
- case UNIT_kVAr: {
- return value * 1e3;
- } break;
- case UNIT_MVAr: {
- return value * 1e6;
- } break;
- default: {
- return 0.0;
- } break;
- }
- return 0.0;
-}
-
void Electromechanical::SaveData()
{
for(auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
@@ -910,6 +941,8 @@ void Electromechanical::SaveData()
m_wErrorVector.push_back(m_wError);
m_numItVector.push_back(m_numIt);
+ m_sdCVector.push_back(m_sdC);
+ m_sqCVector.push_back(m_sqC);
}
void Electromechanical::SetSyncMachinesModel()
@@ -922,9 +955,11 @@ void Electromechanical::SetSyncMachinesModel()
}
}
-void Electromechanical::CalculateSyncMachineNonIntVariables(SyncGenerator* syncGenerator,
+bool Electromechanical::CalculateSyncMachineNonIntVariables(SyncGenerator* syncGenerator,
double& id,
double& iq,
+ double& sd,
+ double& sq,
double& pe,
double k)
{
@@ -938,23 +973,35 @@ void Electromechanical::CalculateSyncMachineNonIntVariables(SyncGenerator* syncG
double vd, vq;
ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
- if(syncGenerator->IsOnline()) {
- std::complex<double> iMachine = std::conj(data.electricalPower) / std::conj(m_vBus[n]);
- ABCtoDQ0(iMachine, data.delta, id, iq);
+ if(data.satFactor != 0.0) {
+ if(!CalculateSyncMachineSaturation(syncGenerator, id, iq, sd, sq, true, k)) return false;
+ data.sd = sd;
+ data.sq = sq;
+ data.oldSd = sd;
+ data.oldSq = sq;
+ }
+ if(syncGenerator->IsOnline()) {
pe = id * vd + iq * vq + (id * id + iq * iq) * data.armResistance * k;
} else {
pe = id = iq = 0.0f;
}
data.pe = pe;
+ data.id = id;
+ data.iq = iq;
+ data.oldPe = pe;
data.oldId = id;
data.oldIq = iq;
syncGenerator->SetElectricalData(data);
+
+ return true;
}
double Electromechanical::CalculateSyncMachineIntVariables(SyncGenerator* syncGenerator,
double id,
double iq,
+ double sd,
+ double sq,
double pe,
double k)
{
@@ -979,8 +1026,12 @@ double Electromechanical::CalculateSyncMachineIntVariables(SyncGenerator* syncGe
// There is no differential equations.
} break;
case Machines::SM_MODEL_2: {
- double tranEq =
- data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (data.syncXd * k - data.transXd * k) * id);
+ double syncXd, transXd;
+ syncXd = data.syncXd * k;
+ transXd = data.transXd * k;
+
+ double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
+ (1.0 + data.icTranEq.m * (sd - 1.0));
error = std::max(error, std::abs(data.tranEq - tranEq));
data.tranEq = tranEq;
@@ -994,10 +1045,12 @@ double Electromechanical::CalculateSyncMachineIntVariables(SyncGenerator* syncGe
if(syncXq == 0.0) syncXq = syncXd;
if(transXq == 0.0) transXq = transXd;
- double tranEq = data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id);
+ double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
+ (1.0 + data.icTranEq.m * (sd - 1.0));
error = std::max(error, std::abs(data.tranEq - tranEq));
- double tranEd = data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq;
+ double tranEd =
+ (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
error = std::max(error, std::abs(data.tranEd - tranEd));
data.tranEq = tranEq;
@@ -1020,13 +1073,16 @@ double Electromechanical::CalculateSyncMachineIntVariables(SyncGenerator* syncGe
if(subXd == 0.0) subXd = subXq;
if(subXq == 0.0) subXq = subXd;
- double tranEq = data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id);
+ double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
+ (1.0 + data.icTranEq.m * (sd - 1.0));
error = std::max(error, std::abs(data.tranEq - tranEq));
- double subEq = data.icSubEq.c + data.icSubEq.m * (tranEq + (transXd - subXd) * id);
+ double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) * id)) /
+ (1.0 + data.icSubEq.m * (sd - 1.0));
error = std::max(error, std::abs(data.subEq - subEq));
- double subEd = data.icSubEd.c - data.icSubEd.m * (syncXq - subXq) * iq;
+ double subEd =
+ (data.icSubEd.c - data.icSubEd.m * ((syncXq - subXq) * iq)) / (1.0 + data.icSubEd.m * (sq - 1.0));
error = std::max(error, std::abs(data.subEd - subEd));
data.tranEq = tranEq;
@@ -1046,16 +1102,20 @@ double Electromechanical::CalculateSyncMachineIntVariables(SyncGenerator* syncGe
if(subXd == 0.0) subXd = subXq;
if(subXq == 0.0) subXq = subXd;
- double tranEq = data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id);
+ double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
+ (1.0 + data.icTranEq.m * (sd - 1.0));
error = std::max(error, std::abs(data.tranEq - tranEq));
- double tranEd = data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq;
+ double tranEd =
+ (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
error = std::max(error, std::abs(data.tranEd - tranEd));
- double subEq = data.icSubEq.c + data.icSubEq.m * (tranEq + (transXd - subXd) * id);
+ double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) * id)) /
+ (1.0 + data.icSubEq.m * (sd - 1.0));
error = std::max(error, std::abs(data.subEq - subEq));
- double subEd = data.icSubEd.c + data.icSubEd.m * (tranEd - (transXq - subXq) * iq);
+ double subEd = (data.icSubEd.c + data.icSubEd.m * (sq * tranEd - (transXq - subXq) * iq)) /
+ (1.0 + data.icSubEd.m * (sq - 1.0));
error = std::max(error, std::abs(data.subEd - subEd));
data.tranEq = tranEq;
@@ -1080,7 +1140,7 @@ void Electromechanical::CalculateReferenceSpeed()
auto data = syncGenerator->GetElectricalData();
double k = 1.0; // Power base change factor.
if(data.useMachineBase) {
- double oldBase = GetPowerValue(data.nominalPower, data.nominalPowerUnit);
+ double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
k = m_powerSystemBase / oldBase;
}
sumH += data.inertia / k;
@@ -1092,3 +1152,148 @@ void Electromechanical::CalculateReferenceSpeed()
m_refSpeed = 2.0 * M_PI * m_systemFreq;
}
}
+
+bool Electromechanical::CalculateSyncMachineSaturation(SyncGenerator* syncMachine,
+ double& id,
+ double& iq,
+ double& sd,
+ double& sq,
+ bool updateCurrents,
+ double k)
+{
+ // [Ref] Arrillaga, J.; Arnold, C. P.. "Computer Modelling of Electrical Power Systems". Pg. 254-260
+ auto data = syncMachine->GetElectricalData();
+ auto smDataModel = GetSyncMachineModelData(syncMachine);
+
+ int n = static_cast<Bus*>(syncMachine->GetParentList()[0])->GetElectricalData().number;
+ if(syncMachine->IsOnline()) {
+ data.terminalVoltage = m_vBus[n];
+ }
+ double idCalc = id;
+ double iqCalc = iq;
+ double sdCalc = sd;
+ double sqCalc = sq;
+
+ double vd, vq;
+ ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
+ double deltaVd = smDataModel.ed - vd;
+ double deltaVq = smDataModel.eq - vq;
+
+ double ra = data.armResistance * k;
+ double xd = smDataModel.xd;
+ double xq = smDataModel.xq;
+
+ double syncXd = data.syncXd * k;
+ double syncXq = data.syncXq * k;
+ if(data.model == Machines::SM_MODEL_1) {
+ syncXq = data.transXd * k;
+ syncXd = syncXq;
+ } else if(data.syncXq == 0.0)
+ syncXq = data.syncXd * k;
+
+ double xp = data.potierReactance * k;
+ if(xp == 0.0) xp = 0.8 * data.transXd * k;
+ double satFacd = (data.satFactor - 1.2) / std::pow(1.2, 7);
+ double satFacq = satFacd * (syncXq / syncXd);
+
+ bool exit = false;
+ int iterations = 0;
+ while(!exit) {
+ double oldSd = sdCalc;
+ double oldSq = sqCalc;
+
+ // Saturated reactances.
+ double xds = (xd - xp) / sdCalc + xp;
+ double xqs = (xq - xp) / sqCalc + xp;
+ // dq currents.
+ double den = 1.0 / (ra * ra + xds * xqs);
+ iqCalc = den * (ra * deltaVq + xds * deltaVd);
+ idCalc = den * (-xqs * deltaVq + ra * deltaVd);
+ // Potier voltages
+ double epq = vq + ra * iqCalc - xp * idCalc;
+ double epd = vd + ra * idCalc + xp * iqCalc;
+ // Saturation factors.
+ // Gauss
+ /*sdCalc = 1.0 + satFacd * std::pow(epq, 6);
+ sqCalc = 1.0 + satFacq * std::pow(epd, 6);*/
+
+ // Newton-raphson
+ double f1 = 1.0 - sdCalc + satFacd * std::pow(epq, 6);
+ double f2 = 1.0 - sqCalc + satFacq * std::pow(epd, 6);
+ double dF1dSd =
+ (6.0 * satFacd * std::pow(epq, 5) * xp * (xd - xp) * deltaVq) / ((sdCalc - 1.0) * xp + xd) - 1.0;
+ double dF2dSq =
+ (6.0 * satFacq * std::pow(epd, 5) * xp * (xq - xp) * deltaVd) / ((sqCalc - 1.0) * xp + xq) - 1.0;
+
+ sdCalc = sdCalc - f1 / dF1dSd;
+ sqCalc = sqCalc - f2 / dF2dSq;
+
+ double error = std::abs(sdCalc - oldSd) + std::abs(sqCalc - oldSq);
+ if(error < m_saturationTolerance) exit = true;
+
+ iterations++;
+ if((iterations >= m_maxIterations) & !exit) {
+ m_errorMsg =
+ _("It was not possible to solve the saturation of the synchronous machine \"") + data.name + wxT("\".");
+ return false;
+ }
+ }
+
+ sd = sdCalc;
+ sq = sqCalc;
+ if(updateCurrents) {
+ id = idCalc;
+ iq = iqCalc;
+ }
+ return true;
+}
+
+SyncMachineModelData Electromechanical::GetSyncMachineModelData(SyncGenerator* syncMachine)
+{
+ SyncMachineModelData smModelData;
+
+ auto data = syncMachine->GetElectricalData();
+ double k = 1.0; // Power base change factor.
+ if(data.useMachineBase) {
+ double oldBase = syncMachine->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
+ k = m_powerSystemBase / oldBase;
+ }
+
+ switch(data.model) {
+ case Machines::SM_MODEL_1: {
+ smModelData.ed = data.tranEd;
+ smModelData.eq = data.tranEq;
+ smModelData.xq = data.transXd * k;
+ smModelData.xd = smModelData.xq;
+ } break;
+ case Machines::SM_MODEL_2: {
+ smModelData.ed = data.tranEd;
+ smModelData.eq = data.tranEq;
+ smModelData.xd = data.transXd * k;
+ smModelData.xq = data.transXq * k;
+ if(smModelData.xq == 0.0) {
+ smModelData.xq = data.syncXq * k;
+ if(smModelData.xq == 0.0) {
+ smModelData.xq = data.syncXd * k;
+ }
+ }
+ } break;
+ case Machines::SM_MODEL_3: {
+ smModelData.ed = data.tranEd;
+ smModelData.eq = data.tranEq;
+ smModelData.xd = data.transXd * k;
+ smModelData.xq = data.transXq * k;
+ if(smModelData.xq == 0.0) smModelData.xq = smModelData.xd;
+ } break;
+ case Machines::SM_MODEL_4:
+ case Machines::SM_MODEL_5: {
+ smModelData.ed = data.subEd;
+ smModelData.eq = data.subEq;
+ smModelData.xd = data.subXd * k;
+ smModelData.xq = data.subXq * k;
+ if(smModelData.xd == 0.0) smModelData.xd = smModelData.xq;
+ if(smModelData.xq == 0.0) smModelData.xq = smModelData.xd;
+ } break;
+ }
+ return smModelData;
+}