summaryrefslogtreecommitdiffstats
path: root/Project/Electromechanical.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Project/Electromechanical.cpp')
-rw-r--r--Project/Electromechanical.cpp317
1 files changed, 314 insertions, 3 deletions
diff --git a/Project/Electromechanical.cpp b/Project/Electromechanical.cpp
index 85e10b4..191a1da 100644
--- a/Project/Electromechanical.cpp
+++ b/Project/Electromechanical.cpp
@@ -12,7 +12,7 @@ void Electromechanical::SetEventTimeList()
// Fault
for(auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
Bus* bus = *it;
- auto data = bus->GetEletricalData();
+ auto data = bus->GetElectricalData();
if(data.stabHasFault) {
m_eventTimeList.push_back(data.stabFaultTime);
m_eventOccurrenceList.push_back(false);
@@ -35,7 +35,7 @@ bool Electromechanical::HasEvent(double currentTime)
{
for(unsigned int i = 0; i < m_eventTimeList.size(); ++i) {
if(!m_eventOccurrenceList[i]) {
- if((m_eventTimeList[i] - m_timeStep) < currentTime && m_eventTimeList[i] >= currentTime) {
+ if(EventTrigger(m_eventTimeList[i], currentTime)) {
m_eventOccurrenceList[i] = true;
return true;
}
@@ -44,16 +44,327 @@ bool Electromechanical::HasEvent(double currentTime)
return false;
}
-void Electromechanical::SetEvent(double currentTime) {}
+void Electromechanical::SetEvent(double currentTime)
+{
+ // Fault
+ for(auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
+ Bus* bus = *it;
+ auto data = bus->GetElectricalData();
+ if(data.stabHasFault) {
+ int n = data.number;
+
+ // Insert fault
+ if(EventTrigger(data.stabFaultTime, currentTime)) {
+ double r, x;
+ r = data.stabFaultResistance;
+ x = data.stabFaultReactance;
+ if(x < 1e-5) x = 1e-5;
+ m_yBus[n][n] += std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
+ }
+
+ // Remove fault
+ else if(EventTrigger(data.stabFaultTime + data.stabFaultLength, currentTime)) {
+ double r, x;
+ r = data.stabFaultResistance;
+ x = data.stabFaultReactance;
+ if(x < 1e-5) x = 1e-5;
+ m_yBus[n][n] -= std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
+ }
+ }
+ }
+
+ // SyncGenerator switching
+ for(auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
+ SyncGenerator* generator = *it;
+ auto swData = generator->GetSwitchingData();
+ for(unsigned int i = 0; i < swData.swType.size(); ++i) {
+ if(EventTrigger(swData.swTime[i], currentTime)) {
+ // Remove machine (only connected machines)
+ if(swData.swType[i] == SW_REMOVE && generator->IsOnline()) {
+ generator->SetOnline(false);
+ auto data = generator->GetPUElectricalData(m_powerSystemBase);
+ int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalData().number;
+ m_yBus[n][n] -= GetSyncMachineAdmittance(generator);
+ }
+
+ // Insert machine (only disconnected machines)
+ if(swData.swType[i] == SW_INSERT && !generator->IsOnline() && generator->GetParentList().size() == 1) {
+ generator->SetOnline(true);
+ auto data = generator->GetPUElectricalData(m_powerSystemBase);
+ int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalData().number;
+ m_yBus[n][n] += GetSyncMachineAdmittance(generator);
+ }
+ }
+ }
+ }
+
+ // Load switching
+ for(auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
+ Load* load = *it;
+ auto swData = load->GetSwitchingData();
+ for(unsigned int i = 0; i < swData.swType.size(); ++i) {
+ if(EventTrigger(swData.swTime[i], currentTime)) {
+ // Remove load (only connected loads)
+ if(swData.swType[i] == SW_REMOVE && load->IsOnline()) {
+ load->SetOnline(false);
+ auto data = load->GetPUElectricalData(m_powerSystemBase);
+ Bus* parentBus = static_cast<Bus*>(load->GetParentList()[0]);
+ int n = parentBus->GetElectricalData().number;
+ std::complex<double> v = parentBus->GetElectricalData().voltage;
+ m_yBus[n][n] -= std::complex<double>(data.activePower, -data.reactivePower) / (v * v);
+ }
+
+ // Insert load (only disconnected load)
+ if(swData.swType[i] == SW_INSERT && !load->IsOnline() && load->GetParentList().size() == 1) {
+ load->SetOnline(true);
+ auto data = load->GetPUElectricalData(m_powerSystemBase);
+ Bus* parentBus = static_cast<Bus*>(load->GetParentList()[0]);
+ int n = parentBus->GetElectricalData().number;
+ std::complex<double> v = parentBus->GetElectricalData().voltage;
+ m_yBus[n][n] += std::complex<double>(data.activePower, -data.reactivePower) / (v * v);
+ }
+ }
+ }
+ }
+
+ // Line switching
+ for(auto it = m_lineList.begin(), itEnd = m_lineList.end(); it != itEnd; ++it) {
+ Line* line = *it;
+ auto swData = line->GetSwitchingData();
+ for(unsigned int i = 0; i < swData.swType.size(); ++i) {
+ if(EventTrigger(swData.swTime[i], currentTime)) {
+ // Remove line (only connected lines)
+ if(swData.swType[i] == SW_REMOVE && line->IsOnline()) {
+ line->SetOnline(false);
+ auto data = line->GetElectricalData();
+
+ int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData().number;
+ int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData().number;
+
+ m_yBus[n1][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n2][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
+
+ m_yBus[n1][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n2][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
+
+ m_yBus[n1][n1] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
+ m_yBus[n2][n2] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
+ }
+
+ // Insert line (only disconnected lines)
+ if(swData.swType[i] == SW_INSERT && !line->IsOnline() && line->GetParentList().size() == 2) {
+ if(line->GetParentList()[0] && line->GetParentList()[1]) {
+ line->SetOnline(true);
+ auto data = line->GetElectricalData();
+
+ int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData().number;
+ int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData().number;
+
+ m_yBus[n1][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n2][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
+
+ m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
+
+ m_yBus[n1][n1] += std::complex<double>(0.0, data.capSusceptance / 2.0);
+ m_yBus[n2][n2] += std::complex<double>(0.0, data.capSusceptance / 2.0);
+ }
+ }
+ }
+ }
+ }
+
+ // Transformer switching
+ for(auto it = m_transformerList.begin(), itEnd = m_transformerList.end(); it != itEnd; ++it) {
+ Transformer* transformer = *it;
+ auto swData = transformer->GetSwitchingData();
+ for(unsigned int i = 0; i < swData.swType.size(); ++i) {
+ if(EventTrigger(swData.swTime[i], currentTime)) {
+ // Remove transformer (only connected transformers)
+ if(swData.swType[i] == SW_REMOVE && transformer->IsOnline()) {
+ transformer->SetOnline(false);
+ auto data = transformer->GetElectricalData();
+
+ int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalData().number;
+ int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalData().number;
+
+ if(data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
+ m_yBus[n1][n2] -= -1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n2][n1] -= -1.0 / std::complex<double>(data.resistance, data.indReactance);
+
+ m_yBus[n1][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n2][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ } else {
+ // Complex turns ratio
+ double radPhaseShift = wxDegToRad(data.phaseShift);
+ std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
+ -data.turnsRatio * std::sin(radPhaseShift));
+
+ // Transformer admitance
+ std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n1][n1] -= y / std::pow(std::abs(a), 2.0);
+ m_yBus[n1][n2] -= -(y / std::conj(a));
+ m_yBus[n2][n1] -= -(y / a);
+ m_yBus[n2][n2] -= y;
+ }
+ }
+
+ // Insert transformer (only disconnected transformers)
+ if(swData.swType[i] == SW_INSERT && !transformer->IsOnline() &&
+ transformer->GetParentList().size() == 2) {
+ if(transformer->GetParentList()[0] && transformer->GetParentList()[1]) {
+ transformer->SetOnline(true);
+ auto data = transformer->GetElectricalData();
+
+ int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalData().number;
+ int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalData().number;
+
+ if(data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
+ m_yBus[n1][n2] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n2][n1] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
+
+ m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ } else {
+ // Complex turns ratio
+ double radPhaseShift = wxDegToRad(data.phaseShift);
+ std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
+ -data.turnsRatio * std::sin(radPhaseShift));
+
+ // Transformer admitance
+ std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
+ m_yBus[n1][n1] += y / std::pow(std::abs(a), 2.0);
+ m_yBus[n1][n2] += -(y / std::conj(a));
+ m_yBus[n2][n1] += -(y / a);
+ m_yBus[n2][n2] += y;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Capacitor switching
+ for(auto it = m_capacitorList.begin(), itEnd = m_capacitorList.end(); it != itEnd; ++it) {
+ Capacitor* capacitor = *it;
+ auto swData = capacitor->GetSwitchingData();
+ for(unsigned int i = 0; i < swData.swType.size(); ++i) {
+ if(EventTrigger(swData.swTime[i], currentTime)) {
+ // Remove capacitor (only connected capacitors)
+ if(swData.swType[i] == SW_REMOVE && capacitor->IsOnline()) {
+ capacitor->SetOnline(false);
+ auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
+ int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalData().number;
+ m_yBus[n][n] += std::complex<double>(0.0, data.reactivePower);
+ }
+
+ // Insert capacitor (only disconnected capacitors)
+ if(swData.swType[i] == SW_INSERT && !capacitor->IsOnline() && capacitor->GetParentList().size() == 1) {
+ if(capacitor->GetParentList()[0]) {
+ capacitor->SetOnline(true);
+ auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
+ int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalData().number;
+ m_yBus[n][n] -= std::complex<double>(0.0, data.reactivePower);
+ }
+ }
+ }
+ }
+ }
+
+ // Capacitor switching
+ for(auto it = m_inductorList.begin(), itEnd = m_inductorList.end(); it != itEnd; ++it) {
+ Inductor* inductor = *it;
+ auto swData = inductor->GetSwitchingData();
+ for(unsigned int i = 0; i < swData.swType.size(); ++i) {
+ if(EventTrigger(swData.swTime[i], currentTime)) {
+ // Remove inductor (only connected inductors)
+ if(swData.swType[i] == SW_REMOVE && inductor->IsOnline()) {
+ inductor->SetOnline(false);
+ auto data = inductor->GetPUElectricalData(m_powerSystemBase);
+ int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalData().number;
+ m_yBus[n][n] += std::complex<double>(0.0, -data.reactivePower);
+ }
+
+ // Insert inductor (only disconnected inductors)
+ if(swData.swType[i] == SW_INSERT && !inductor->IsOnline() && inductor->GetParentList().size() == 1) {
+ if(inductor->GetParentList()[0]) {
+ inductor->SetOnline(true);
+ auto data = inductor->GetPUElectricalData(m_powerSystemBase);
+ int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalData().number;
+ m_yBus[n][n] -= std::complex<double>(0.0, -data.reactivePower);
+ }
+ }
+ }
+ }
+ }
+}
+
bool Electromechanical::RunStabilityCalculation()
{
+ // Calculate the admittance matrix with the synchronous machines.
+ if(!GetYBus(m_yBus, m_powerSystemBase, POSITIVE_SEQ, false, true)) {
+ m_errorMsg = _("It was not possible to build the admittance matrix.");
+ return false;
+ }
+ InsertSyncMachinesOnYBus();
+
// test
double simTime = 10.0;
double currentTime = 0.0;
while(currentTime <= simTime) {
if(HasEvent(currentTime)) {
+ SetEvent(currentTime);
}
currentTime += m_timeStep;
}
return true;
}
+
+void Electromechanical::InsertSyncMachinesOnYBus()
+{
+ for(auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
+ SyncGenerator* generator = *it;
+ if(generator->IsOnline()) {
+ auto data = generator->GetElectricalData();
+ int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalData().number;
+ m_yBus[n][n] += GetSyncMachineAdmittance(generator);
+ }
+ }
+}
+
+bool Electromechanical::EventTrigger(double eventTime, double currentTime)
+{
+ return (((eventTime - m_timeStep) < currentTime) && (eventTime >= currentTime));
+}
+
+std::complex<double> Electromechanical::GetSyncMachineAdmittance(SyncGenerator* generator)
+{
+ auto data = generator->GetPUElectricalData(m_powerSystemBase);
+ double k = 1.0; // Power base change factor.
+ if(data.useMachineBase) {
+ double oldBase = data.nominalPower * std::pow(1000.0f, data.nominalPowerUnit);
+ k = m_powerSystemBase / oldBase;
+ }
+
+ double xd = 0.0;
+ double xq = 0.0;
+ double ra = data.armResistance * k;
+
+ switch(GetMachineModel(generator)) {
+ case SM_MODEL_1:
+ case SM_MODEL_2:
+ case SM_MODEL_3: {
+ xd = data.transXd * k;
+ xq = data.transXq * k;
+ break;
+ }
+ case SM_MODEL_4:
+ case SM_MODEL_5: {
+ xd = data.subXd * k;
+ xq = data.subXq * k;
+ break;
+ }
+ }
+ double xdq = 0.5 * (xd + xq);
+ return std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0);
+}