Power System Platform  2026w11a-beta
Loading...
Searching...
No Matches
Electromechanical.cpp
1/*
2 * Copyright (C) 2017 Thales Lima Oliveira <thales@ufu.br>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "../elements/controlElement/ControlElementSolver.h"
19#include "Electromechanical.h"
20
21#ifdef _DEBUG
22#include <chrono>
23
24double totalSaveData = 0.0;
25double totalSolveMachines = 0.0;
26double totalCalcFreq = 0.0;
27
28size_t saveDataCalls = 0;
29size_t solveMachinesCalls = 0;
30size_t calcFreqCalls = 0;
31#endif //_DEBUG
32
33Electromechanical::Electromechanical(wxWindow* parent, std::vector<Element*> elementList, SimulationData data)
34{
35 m_parent = parent;
36 GetElementsFromList(elementList);
37 SetEventTimeList();
38
39 Bus dummyBus;
40 m_simData = 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;
47
48 m_ctrlTimeStepMultiplier = 1.0 / static_cast<double>(data.controlTimeStepRatio);
49
50 m_plotTime = data.plotTime;
51 m_useCOI = data.useCOI;
52 // If the user use all load as ZIP, updates the portions of each model, otherwise use constant impedance only.
53 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
54 Load* load = *it;
55 auto& loadData = load->GetElectricalDataRef();
56 if (!loadData.useCompLoad) { // If no individual load composition defined.
57 if (data.useCompLoads) { // Use general composition, if defined.
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;
64 }
65 else { // Otherwise, use constant impedance.
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;
72 }
73 }
74
75 loadData.constCurrentUV = data.underVoltageConstCurrent / 100.0;
76 loadData.constPowerUV = data.underVoltageConstPower / 100.0;
77 //load->SetElectricalData(loadData);
78 }
79}
80
81Electromechanical::~Electromechanical() { }
82
83bool Electromechanical::RunStabilityCalculation()
84{
85 wxProgressDialog pbd(_("Running simulation"), _("Initializing..."), 100, m_parent,
86 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_SMOOTH);
87
88 SetSyncMachinesModel();
89
90 // Calculate the admittance matrix with the synchronous machines.
91 if (!GetYBus(m_yBus, m_powerSystemBase, POSITIVE_SEQ, false, true, true)) {
92 m_errorMsg = _("It was not possible to build the admittance matrix.");
93 return false;
94 }
95 InsertSyncMachinesOnYBus();
96 if (!InsertIndMachinesOnYBus()) return false;
97 GetLUDecomposition(m_yBus, m_yBusL, m_yBusU);
98
99 // Get buses voltages.
100 m_vBus.clear();
101 m_vBus.resize(m_yBus.size());
102 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
103 Bus* bus = *it;
104 const auto& data = bus->GetElectricalDataRef();
105 if (data.number >= 0) m_vBus[data.number] = data.voltage;
106 }
107 m_vBus.shrink_to_fit();
108
109 // Calculate injected currents
110 m_iBus = ComplexMatrixTimesVector(m_yBus, m_vBus);
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);
113 }
114
115 if (!InitializeDynamicElements()) return false;
116 PreallocateVectors(); // Reserve the vectors' memory with a estimated size, this can optimize the simulation.
117
118#ifdef _DEBUG
119 for (auto* syncGenerator : m_syncGeneratorList) {
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";
142 }
143#endif
144
145 double pbdTime = m_plotTime;
146 m_currentTime = 0.0;
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)) {
152 hasEvent = true;
153 SetEvent(m_currentTime);
154 GetLUDecomposition(m_yBus, m_yBusL, m_yBusU);
155 }
156
157
158
159 if (currentPlotTime >= m_plotTime || m_currentTime == 0.0) {
160 m_timeVector.emplace_back(m_currentTime);
161
162 #ifdef _DEBUG
163 auto t0 = std::chrono::high_resolution_clock::now();
164 #endif //_DEBUG
165
166 SaveData();
167
168 #ifdef _DEBUG
169 auto t1 = std::chrono::high_resolution_clock::now();
170 totalSaveData += std::chrono::duration<double, std::milli>(t1 - t0).count();
171 saveDataCalls++;
172 #endif //_DEBUG
173
174 currentPlotTime = 0.0;
175 }
176
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);
180 pbd.Update(100);
181 return false;
182 }
183 currentPbdTime = 0.0;
184 }
185
186 #ifdef _DEBUG
187 auto t0 = std::chrono::high_resolution_clock::now();
188 #endif //_DEBUG
189
190 if (!SolveMachines()) return false;
191
192 #ifdef _DEBUG
193 auto t1 = std::chrono::high_resolution_clock::now();
194 totalSolveMachines += std::chrono::duration<double, std::milli>(t1 - t0).count();
195 solveMachinesCalls++;
196
197 t0 = std::chrono::high_resolution_clock::now();
198 #endif //_DEBUG
199
200 CalculateBusesFrequency(hasEvent);
201
202 #ifdef _DEBUG
203 t1 = std::chrono::high_resolution_clock::now();
204 totalCalcFreq += std::chrono::duration<double, std::milli>(t1 - t0).count();
205 calcFreqCalls++;
206 #endif //_DEBUG
207
208 m_currentTime += m_timeStep;
209 currentPlotTime += m_timeStep;
210 currentPbdTime += m_timeStep;
211 }
212 #ifdef _DEBUG
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);
216 #endif //_DEBUG
217
218 return true;
219}
220
221void Electromechanical::SetEventTimeList()
222{
223 // Fault
224 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
225 Bus* bus = *it;
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);
232 }
233 }
234 // Switching
235 for (auto it = m_powerElementList.begin(), itEnd = m_powerElementList.end(); it != itEnd; ++it) {
236 PowerElement* element = *it;
237 SwitchingData swData = element->GetSwitchingData();
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);
241 }
242 }
243}
244
245bool Electromechanical::HasEvent(double currentTime)
246{
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;
251 return true;
252 }
253 }
254 }
255 return false;
256}
257
258void Electromechanical::SetEvent(double currentTime)
259{
260 // Fault
261 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
262 Bus* bus = *it;
263 const auto& data = bus->GetElectricalDataRef();
264 int n = data.number;
265 if (data.stabHasFault && n >= 0) {
266
267 // Insert fault
268 if (EventTrigger(data.stabFaultTime, currentTime)) {
269 double r, x;
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);
274 }
275
276 // Remove fault
277 else if (EventTrigger(data.stabFaultTime + data.stabFaultLength, currentTime)) {
278 double r, x;
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);
283 }
284 }
285 }
286
287 // SyncGenerator switching
288 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
289 SyncGenerator* generator = *it;
290 auto swData = generator->GetSwitchingData();
291 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
292 if (EventTrigger(swData.swTime[i], currentTime)) {
293 // Remove machine (only connected machines)
294 if (swData.swType[i] == SwitchingType::SW_REMOVE && generator->IsOnline()) {
295 generator->SetOnline(false);
296 int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalDataRef().number;
297 if (n >= 0) m_yBus[n][n] -= GetSyncMachineAdmittance(generator);
298 }
299
300 // Insert machine (only disconnected machines)
301 if (swData.swType[i] == SwitchingType::SW_INSERT && !generator->IsOnline() && generator->GetParentList().size() == 1) {
302 if (generator->SetOnline(true)) {
303 int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalDataRef().number;
304 if (n >= 0) m_yBus[n][n] += GetSyncMachineAdmittance(generator);
305 }
306 }
307 }
308 }
309 }
310
311 // Induction motor switching
312 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
313 IndMotor* motor = *it;
314 auto swData = motor->GetSwitchingData();
315 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
316 if (EventTrigger(swData.swTime[i], currentTime)) {
317 // Remove machine (only connected machines)
318 if (swData.swType[i] == SwitchingType::SW_REMOVE && motor->IsOnline() && motor->GetParentList().size() == 1) {
319 const auto& data = motor->GetElectricalDataRef();
320 motor->SetOnline(false);
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));
323 }
324
325 // Insert machine (only disconnected machines)
326 if (swData.swType[i] == SwitchingType::SW_INSERT && !motor->IsOnline() && motor->GetParentList().size() == 1) {
327 const auto& data = motor->GetElectricalDataRef();
328 if (motor->SetOnline(true)) {
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));
331 }
332 }
333 }
334 }
335 }
336
337 // Load switching
338 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
339 Load* load = *it;
340 auto swData = load->GetSwitchingData();
341 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
342 if (EventTrigger(swData.swTime[i], currentTime)) {
343 // Remove load (only connected loads)
344 if (swData.swType[i] == SwitchingType::SW_REMOVE && load->IsOnline() && load->GetParentList().size() == 1) {
345 load->SetOnline(false);
346 auto data = load->GetPUElectricalData(m_powerSystemBase);
347 Bus* parentBus = static_cast<Bus*>(load->GetParentList()[0]);
348 int n = parentBus->GetElectricalDataRef().number;
349 std::complex<double> v = parentBus->GetElectricalDataRef().voltage;
350 if (n >= 0) {
351 m_yBus[n][n] -=
352 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
353 }
354 }
355
356 // Insert load (only disconnected load)
357 if (swData.swType[i] == SwitchingType::SW_INSERT && !load->IsOnline() && load->GetParentList().size() == 1) {
358 if (load->SetOnline(true)) {
359 auto data = load->GetPUElectricalData(m_powerSystemBase);
360 Bus* parentBus = static_cast<Bus*>(load->GetParentList()[0]);
361 int n = parentBus->GetElectricalDataRef().number;
362 std::complex<double> v = parentBus->GetElectricalDataRef().voltage;
363 if (n >= 0) {
364 m_yBus[n][n] +=
365 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
366 }
367 }
368 }
369 }
370 }
371 }
372
373 // Line switching
374 for (auto it = m_lineList.begin(), itEnd = m_lineList.end(); it != itEnd; ++it) {
375 Line* line = *it;
376 auto swData = line->GetSwitchingData();
377 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
378 if (EventTrigger(swData.swTime[i], currentTime)) {
379 // Remove line (only connected lines)
380 if (swData.swType[i] == SwitchingType::SW_REMOVE && line->IsOnline()) {
381 line->SetOnline(false);
382 const auto& data = line->GetElectricalDataRef();
383
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);
389
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);
392
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);
395 }
396 }
397
398 // Insert line (only disconnected lines)
399 if (swData.swType[i] == SwitchingType::SW_INSERT && !line->IsOnline() && line->GetParentList().size() == 2) {
400 if (line->SetOnline(true)) {
401 auto& data = line->GetElectricalDataRef();
402
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);
408
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);
411
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);
414 }
415 }
416 }
417 }
418 }
419 }
420
421 // Transformer switching
422 for (auto it = m_transformerList.begin(), itEnd = m_transformerList.end(); it != itEnd; ++it) {
423 Transformer* transformer = *it;
424 auto swData = transformer->GetSwitchingData();
425 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
426 if (EventTrigger(swData.swTime[i], currentTime)) {
427 // Remove transformer (only connected transformers)
428 if (swData.swType[i] == SwitchingType::SW_REMOVE && transformer->IsOnline()) {
429 transformer->SetOnline(false);
430 const auto& data = transformer->GetElectricalDataRef();
431
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);
438
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);
441 }
442 else {
443 // Complex turns ratio
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));
447
448 // Transformer admitance
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);
453 m_yBus[n2][n2] -= y;
454 }
455 }
456 }
457
458 // Insert transformer (only disconnected transformers)
459 if (swData.swType[i] == SwitchingType::SW_INSERT && !transformer->IsOnline() &&
460 transformer->GetParentList().size() == 2) {
461 if (transformer->SetOnline(true)) {
462 const auto& data = transformer->GetElectricalDataRef();
463
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);
470
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);
473 }
474 else {
475 // Complex turns ratio
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));
479
480 // Transformer admitance
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);
485 m_yBus[n2][n2] += y;
486 }
487 }
488 }
489 }
490 }
491 }
492 }
493
494 // Capacitor switching
495 for (auto it = m_capacitorList.begin(), itEnd = m_capacitorList.end(); it != itEnd; ++it) {
496 Capacitor* capacitor = *it;
497 auto swData = capacitor->GetSwitchingData();
498 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
499 if (EventTrigger(swData.swTime[i], currentTime)) {
500 // Remove capacitor (only connected capacitors)
501 if (swData.swType[i] == SwitchingType::SW_REMOVE && capacitor->IsOnline()) {
502 capacitor->SetOnline(false);
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);
506 }
507
508 // Insert capacitor (only disconnected capacitors)
509 if (swData.swType[i] == SwitchingType::SW_INSERT && !capacitor->IsOnline() && capacitor->GetParentList().size() == 1) {
510 if (capacitor->SetOnline(true)) {
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);
514 }
515 }
516 }
517 }
518 }
519
520 // Inductor switching
521 for (auto it = m_inductorList.begin(), itEnd = m_inductorList.end(); it != itEnd; ++it) {
522 Inductor* inductor = *it;
523 auto swData = inductor->GetSwitchingData();
524 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
525 if (EventTrigger(swData.swTime[i], currentTime)) {
526 // Remove inductor (only connected inductors)
527 if (swData.swType[i] == SwitchingType::SW_REMOVE && inductor->IsOnline()) {
528 inductor->SetOnline(false);
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);
532 }
533
534 // Insert inductor (only disconnected inductors)
535 if (swData.swType[i] == SwitchingType::SW_INSERT && !inductor->IsOnline() && inductor->GetParentList().size() == 1) {
536 if (inductor->SetOnline(true)) {
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);
540 }
541 }
542 }
543 }
544 }
545}
546
547void Electromechanical::InsertSyncMachinesOnYBus()
548{
549 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
550 SyncGenerator* syncGenerator = *it;
551 if (syncGenerator->IsOnline()) {
552 Bus* connBus = static_cast<Bus*>(syncGenerator->GetParentList()[0]);
553 if (connBus->GetElectricalDataRef().number < 0) {
554 syncGenerator->SetOnline(false);
555 //auto data = syncGenerator->GetElectricalData();
556 //data.activePower = 0.0;
557 //data.reactivePower = 0.0;
558 //syncGenerator->SetElectricalData(data);
559 }
560
561 if (syncGenerator->IsOnline()) {
562 const auto& data = syncGenerator->GetElectricalDataRef();
563 int n = connBus->GetElectricalDataRef().number;
564 m_yBus[n][n] += GetSyncMachineAdmittance(syncGenerator);
565 }
566 }
567 }
568}
569
570bool Electromechanical::EventTrigger(double eventTime, double currentTime)
571{
572 return (((eventTime - m_timeStep) < currentTime) && (eventTime >= currentTime));
573}
574
575std::complex<double> Electromechanical::GetSyncMachineAdmittance(SyncGenerator* generator)
576{
577 const auto& data = generator->GetElectricalDataRef();
578 double k = 1.0; // Power base change factor.
579 if (data.useMachineBase) {
580 double oldBase = generator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
581 k = m_powerSystemBase / oldBase;
582 }
583
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));
590}
591
592bool Electromechanical::InitializeDynamicElements()
593{
594 // Buses
595 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
596 Bus* bus = *it;
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;
605 data.dxt = 0.0;
606 data.filteredVelocity = 0.0;
607 data.ddw = 0.0;
608 //bus->SetElectricalData(data);
609 }
610 // Loads
611 for (auto* load : m_loadList) {
612 auto dataPU = load->GetPUElectricalData(m_powerSystemBase);
613 auto& data = load->GetElectricalDataRef();
614
615 double activePower = dataPU.activePower;
616 double reactivePower = dataPU.reactivePower;
617
618 Bus* bus = nullptr;
619 if (GetParentBus(load, bus))
620 {
621 data.voltage = bus->GetElectricalDataRef().voltage;
622 }
623 else {
624 load->SetOnline(false);
625 }
626
627 data.v0 = std::abs(data.voltage);
628 data.y0 = std::complex<double>(activePower, -reactivePower) / (data.v0 * data.v0);
629
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();
634 }
635
636 data.pz0 = (data.constImpedanceActive / 100.0) * activePower;
637 data.pi0 = (data.constCurrentActive / 100.0) * activePower;
638 data.pp0 = (data.constPowerActive / 100.0) * activePower;
639
640 data.qz0 = (data.constImpedanceReactive / 100.0) * reactivePower;
641 data.qi0 = (data.constCurrentReactive / 100.0) * reactivePower;
642 data.qp0 = (data.constPowerReactive / 100.0) * reactivePower;
643
644 data.voltageVector.clear();
645 data.voltageVector.shrink_to_fit();
646 data.electricalPowerVector.clear();
647 data.electricalPowerVector.shrink_to_fit();
648
649 if (load->IsOnline())
650 data.electricalPower = std::complex<double>(activePower, reactivePower);
651 else {
652 data.electricalPower = std::complex<double>(0.0, 0.0);
653 data.voltage = std::complex<double>(0.0, 0.0);
654 }
655
656 //load->SetElectricalData(data);
657 }
658 // Synchronous generators
659 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
660 SyncGenerator* syncGenerator = *it;
661 auto dataPU = syncGenerator->GetPUElectricalData(m_powerSystemBase);
662 auto& data = syncGenerator->GetElectricalDataRef();
663 double k = 1.0; // Power base change factor.
664 if (data.useMachineBase) {
665 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
666 k = m_powerSystemBase / oldBase;
667 }
668 Bus* bus = nullptr;
669 if (GetParentBus(syncGenerator, bus))
670 data.terminalVoltage = bus->GetElectricalDataRef().voltage;
671 else
672 data.terminalVoltage = std::complex<double>(1.0, 0.0);
673
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);
677
678 double xd = data.syncXd * k;
679 double xq = data.syncXq * k;
680 double ra = data.armResistance * k;
681
682 if (data.model == Machines::SM_MODEL_1) {
683 xq = data.transXd * k;
684 xd = xq;
685 }
686 else if (data.syncXq == 0.0)
687 xq = data.syncXd * k;
688
689 double sd = 1.0;
690 double sq = 1.0;
691 double satF = 1.0;
692 double xp = data.potierReactance * k;
693 bool hasSaturation = false;
694 if (data.satFactor != 0.0) { // Have saturation.
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;
698 }
699
700 // Initialize state variables
701 std::complex<double> eq0 = vt + std::complex<double>(ra, xq) * ia;
702 double delta = std::arg(eq0);
703
704 double id0, iq0, vd0, vq0;
705 ABCtoDQ0(ia, delta, id0, iq0);
706 ABCtoDQ0(vt, delta, vd0, vq0);
707
708 // Initialize saturation
709 double xqs = xq;
710 double xds = xd;
711 if (hasSaturation) {
712 double oldDelta = 0;
713 bool exit = false;
714 int numIt = 0;
715 while (!exit) {
716 oldDelta = delta;
717 ABCtoDQ0(ia, delta, id0, iq0);
718 ABCtoDQ0(vt, delta, vd0, vq0);
719
720 // Direct-axis Potier voltage.
721 double epd = vd0 + ra * id0 + xp * iq0;
722
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) {
728 exit = true;
729 }
730 else if (numIt >= m_maxIterations) {
731 m_errorMsg = _("Error on initializate the saturation values of \"") + data.name + _("\".");
732 return false;
733 }
734 numIt++;
735 }
736 // Quadrature-axis Potier voltage.
737 double epq = vq0 + ra * iq0 - xp * id0;
738 sd = 1.0 + satF * std::pow(epq, 6);
739 xds = (xd - xp) / sd + xp;
740 }
741
742 double ef0 = vq0 + ra * iq0 - xds * id0;
743
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;
748 data.delta = delta;
749 data.pe = data.pm;
750 data.electricalPower = std::complex<double>(dataPU.activePower, dataPU.reactivePower);
751 if (!syncGenerator->IsOnline()) data.electricalPower = std::complex<double>(0.0, 0.0);
752 data.sd = sd;
753 data.sq = sq;
754 data.id = id0;
755 data.iq = iq0;
756
757 // Variables to extrapolate.
758 data.oldIq = iq0;
759 data.oldId = id0;
760 data.oldPe = data.pe;
761 data.oldSd = sd;
762 data.oldSq = sq;
763
764 switch (data.model) {
765 case Machines::SM_MODEL_1: {
766 data.tranEq = std::abs(eq0);
767
768 data.tranEd = 0.0;
769 data.subEq = 0.0;
770 data.subEd = 0.0;
771 } break;
772 case Machines::SM_MODEL_2: {
773 double tranXd = data.transXd * k;
774
775 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
776 data.tranEd = 0.0;
777 data.subEd = 0.0;
778 data.subEq = 0.0;
779 } break;
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;
784
785 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
786 data.tranEd = -(xq - tranXq) * (iq0 / sq);
787
788 data.subEd = 0.0;
789 data.subEq = 0.0;
790 } break;
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;
797
798 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
799 data.tranEd = 0.0;
800 data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
801 data.subEd = -(xq - subXq) * (iq0 / sq);
802 } break;
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;
810
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);
815 } break;
816 default:
817 break;
818 }
819
820 // Initialize controllers
821 if (data.useAVR) {
822 //if (data.avrSolver) delete data.avrSolver;
823 //data.avrSolver =
824 // new ControlElementSolver(data.avr, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
825 data.avrSolver = std::make_shared<ControlElementSolver>(data.avr, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
826
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();
839 //syncGenerator->SetElectricalData(data);
840 return false;
841 }
842 }
843 if (data.useSpeedGovernor) {
844 //if (data.speedGovSolver) delete data.speedGovSolver;
845 //data.speedGovSolver =
846 // new ControlElementSolver(data.speedGov, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
847
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();
860 //syncGenerator->SetElectricalData(data);
861 return false;
862 }
863 }
864 //}
865 //} else {
866 // Initialize open circuit machine.
867 //}
868 // Reset plot data
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();
881
882 //syncGenerator->SetElectricalData(data);
883 }
884 // Induction motors
885 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
886 IndMotor* indMotor = *it;
887 auto dataPU = indMotor->GetPUElectricalData(m_powerSystemBase);
888 auto& data = indMotor->GetElectricalDataRef();
889
890 Bus* connBus = static_cast<Bus*>(indMotor->GetParentList()[0]);
891 if (connBus->GetElectricalDataRef().number < 0) indMotor->SetOnline(false);
892
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;
899
900 double wi = w0 * (1 - data.s0); // Initial velocity
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);
904
905 data.aCalc = data.as;
906 data.bCalc = data.bs;
907 data.cCalc = data.cs;
908
909 if (indMotor->IsOnline()) {
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);
912
913 data.tranEr = std::real(tranE);
914 data.tranEm = std::imag(tranE);
915
916 data.slip = data.s0;
917 data.ir = ir;
918 data.im = im;
919 data.te = te;
920 }
921 else { // Offline
922 data.tranEr = 0.0;
923 data.tranEm = 0.0;
924 data.slip = 1.0 - 1e-7;
925 data.ir = 0.0;
926 data.im = 0.0;
927 data.te = 0.0;
928 }
929
930 // Variables to extrapolate.
931 data.oldTe = data.te;
932 data.oldIr = data.ir;
933 data.oldIm = data.im;
934
935 // Reset plot data
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();
952
953 //indMotor->SetElectricalData(data);
954 }
955 CalculateReferenceSpeed();
956 return true;
957}
958
959bool Electromechanical::CalculateInjectedCurrents()
960{
961 // Reset injected currents vector
962 for (unsigned int i = 0; i < m_iBus.size(); ++i) m_iBus[i] = std::complex<double>(0.0, 0.0);
963
964 // Synchronous machines
965 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
966 SyncGenerator* syncGenerator = *it;
967 auto& data = syncGenerator->GetElectricalDataRef();
968 if (syncGenerator->IsOnline()) {
969 double k = 1.0; // Power base change factor.
970 if (data.useMachineBase) {
971 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
972 k = m_powerSystemBase / oldBase;
973 }
974
975 double ra = data.armResistance * k;
976 double xp = data.potierReactance * k;
977 if (xp == 0.0) xp = 0.8 * data.transXd * k;
978
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];
983
984 auto smModelData = GetSyncMachineModelData(syncGenerator);
985 DQ0toABC(smModelData.ed, smModelData.eq, data.delta, e);
986 double xd = smModelData.xd;
987 double xq = smModelData.xq;
988
989 double sd = data.sd;
990 double sq = data.sq;
991 double id, iq;
992
993 // Calculate the saturation effect
994 if (data.satFactor != 0.0) {
995 if (!CalculateSyncMachineSaturation(syncGenerator, id, iq, sd, sq, false, k)) return false;
996 }
997
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);
1003
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;
1006
1007 // [Ref] Arrillaga, J.; Arnold, C. P.. "Computer Modelling of Electrical Power Systems". Pg. 225-226
1008 // [Ref] Dommell, H. W.; Sato, N.. "Fast transient stability solutions". IEEE Transactions on Power
1009 // Apparatus and Systems, PAS-91 (4), 1643-1650
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));
1014
1015 // [Ref] Arrillaga, J.; Arnold, C. P.; Computer Modelling of Electrical Power Systems. Pg. 258-259
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);
1018
1019 iInj = iUnadjusted + iSaliency + iSaturation;
1020
1021 m_iBus[n] += iInj;
1022
1023 // Remove the current flowing through y0 (i.e. iUnadjusted in this case, y0 is inserted in admittance
1024 // matrix) to calculate the electrical power.
1025 std::complex<double> iMachine = iInj - iUnadjusted;
1026 data.electricalPower = v * std::conj(iMachine);
1027
1028 ABCtoDQ0(iMachine, data.delta, id, iq);
1029
1030 data.id = id;
1031 data.iq = iq;
1032 data.sd = sd;
1033 data.sq = sq;
1034 }
1035 else {
1036 data.electricalPower = std::complex<double>(0.0, 0.0);
1037 }
1038
1039 //syncGenerator->SetElectricalData(data);
1040 }
1041 // Induction motors
1042 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1043 IndMotor* motor = *it;
1044 auto& data = motor->GetElectricalDataRef();
1045 if (motor->IsOnline()) {
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;
1051 m_iBus[n] += iInj;
1052
1053 std::complex<double> iMachine = y0 * (v - e);
1054
1055 data.ir = std::real(iMachine);
1056 data.im = std::imag(iMachine);
1057 }
1058 else {
1059 data.ir = 0.0;
1060 data.im = 0.0;
1061 }
1062 //motor->SetElectricalData(data);
1063 }
1064
1065 // Loads
1066 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
1067 Load* load = *it;
1068 auto& data = load->GetElectricalDataRef();
1069 Bus* bus = nullptr;
1070 if (!GetParentBus(load, bus)) {
1071 load->SetOnline(false);
1072 }
1073 else if (load->IsOnline()) {
1074
1075 data.voltage = m_vBus[bus->GetElectricalDataRef().number];
1076 double vAbs = std::abs(data.voltage);
1077
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);
1081 pp = data.pp0;
1082 qz = data.qz0 * std::pow(vAbs / data.v0, 2);
1083 qi = data.qi0 * (vAbs / data.v0);
1084 qp = data.qp0;
1085
1086 // If voltage value is low, set the ZIP load to constant impedance.
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);
1090 }
1091 if (vAbs < data.constPowerUV) {
1092 pp *= std::pow(vAbs / data.constPowerUV, 2);
1093 qp *= std::pow(vAbs / data.constPowerUV, 2);
1094 }
1095
1096 double activePower = pz + pi + pp;
1097 double reactivePower = qz + qi + qp;
1098
1099 std::complex<double> newY = std::complex<double>(activePower, -reactivePower) / (vAbs * vAbs);
1100 m_iBus[bus->GetElectricalDataRef().number] += (data.y0 - newY) * data.voltage;
1101
1102 data.electricalPower = std::complex<double>(activePower, reactivePower);
1103 }
1104 else {
1105 data.voltage = std::complex<double>(0.0, 0.0);
1106 data.electricalPower = std::complex<double>(0.0, 0.0);
1107 }
1108
1109 //load->SetElectricalData(data);
1110 }
1111 return true;
1112}
1113
1114void Electromechanical::CalculateIntegrationConstants(SyncGenerator* syncGenerator, double id, double iq, double k)
1115{
1116 CalculateReferenceSpeed();
1117 auto& data = syncGenerator->GetElectricalDataRef();
1118
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;
1126
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;
1131
1132 double transTd0, transTq0, subTd0, subTq0;
1133 transTd0 = data.transTd0;
1134 transTq0 = data.transTq0;
1135 subTd0 = data.subTd0;
1136 subTq0 = data.subTq0;
1137
1138 if (subTd0 == 0.0) subTd0 = subTq0;
1139 if (subTq0 == 0.0) subTq0 = subTd0;
1140
1141 // Speed
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);
1145
1146 // Delta
1147 data.icDelta.m = 0.5f * m_timeStep;
1148 data.icDelta.c = data.delta + data.icDelta.m * (data.speed - 2.0f * m_refSpeed);
1149
1150 // Eq'
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);
1156 }
1157
1158 // Ed'
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);
1161 data.icTranEd.c =
1162 (1.0 - data.icTranEd.m * (1.0 + data.sq)) * data.tranEd - data.icTranEd.m * (syncXq - transXq) * iq;
1163 }
1164
1165 // Eq''
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);
1170 }
1171 // Ed''
1172 if (data.model == Machines::SM_MODEL_4) {
1173 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1174 data.icSubEd.c =
1175 (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd - data.icSubEd.m * (syncXq - subXq) * iq;
1176 }
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);
1181 }
1182
1183 //syncGenerator->SetElectricalData(data);
1184}
1185
1186void Electromechanical::CalculateIntegrationConstants(IndMotor* indMotor, double ir, double im, double k)
1187{
1188 double w0 = 2.0 * M_PI * m_systemFreq;
1189
1190 auto& data = indMotor->GetElectricalDataRef();
1191
1192 // If the velocity is too low set mechanical torque to zero (a, b and c coeficients)
1193 if (data.slip > 0.99999) {
1194 data.as = 0.0;
1195 data.bs = 0.0;
1196 data.cs = 0.0;
1197 }
1198 else {
1199 data.as = data.aCalc;
1200 data.bs = data.bCalc;
1201 data.cs = data.cCalc;
1202 }
1203
1204 // Mechanical equations
1205 // s
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);
1209
1210 // Electrical equations
1211 // Er
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);
1215 // Em
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);
1219
1220 //indMotor->SetElectricalData(data);
1221}
1222
1223bool Electromechanical::SolveMachines()
1224{
1225 // First interation values and extrapolations
1226 // Synchronous machines
1227 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1228 SyncGenerator* syncGenerator = *it;
1229 const auto& data = syncGenerator->GetElectricalDataRef();
1230
1231 if (syncGenerator->IsOnline()) {
1232 double id, iq, pe, sd, sq;
1233 pe = data.pe;
1234 id = data.id;
1235 iq = data.iq;
1236 sd = data.sd;
1237 sq = data.sq;
1238
1239 double k = 1.0; // Power base change factor.
1240 if (data.useMachineBase) {
1241 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1242 k = m_powerSystemBase / oldBase;
1243 }
1244
1245 // Calculate integration constants.
1246 CalculateIntegrationConstants(syncGenerator, id, iq, k);
1247
1248 if (!CalculateNonIntVariables(syncGenerator, id, iq, sd, sq, pe, k)) return false;
1249 // Extrapolate nonintegrable variables.
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;
1255
1256 CalculateIntVariables(syncGenerator, id, iq, sd, sq, pe, k);
1257 }
1258 else {
1259 CalculateIntegrationConstants(syncGenerator, 0.0f, 0.0f);
1260 }
1261 }
1262 // Induction machines
1263 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1264 IndMotor* indMotor = *it;
1265 const auto& data = indMotor->GetElectricalDataRef();
1266
1267 //if(indMotor->IsOnline()) {
1268 double ir, im, te;
1269 te = data.te;
1270 ir = data.ir;
1271 im = data.im;
1272
1273 double k = 1.0; // Power base change factor.
1274 if (data.useMachinePowerAsBase) {
1275 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1276 k = m_powerSystemBase / oldBase;
1277 }
1278
1279 // Calculate integration constants.
1280 CalculateIntegrationConstants(indMotor, ir, im, k);
1281
1282 if (!CalculateNonIntVariables(indMotor, ir, im, te, k)) return false;
1283 // Extrapolate nonintegrable variables.
1284 ir = 2.0 * ir - data.oldIr;
1285 im = 2.0 * im - data.oldIm;
1286 te = 2.0 * te - data.oldTe;
1287
1288 CalculateIntVariables(indMotor, ir, im, te, k);
1289 //} else {
1290 //CalculateIntegrationConstants(indMotor, 0.0f, 0.0f);
1291 //}
1292 }
1293
1294 double error = 1.0;
1295 int iterations = 0;
1296 while (error > m_tolerance) {
1297 error = 0.0;
1298
1299 // Calculate the injected currents.
1300 if (!CalculateInjectedCurrents()) return false;
1301
1302 // Calculate the buses voltages.
1303 m_vBus = LUEvaluate(m_yBusU, m_yBusL, m_iBus);
1304
1305 // Solve synchronous machine equations.
1306 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1307 SyncGenerator* syncGenerator = *it;
1308
1309 const auto& data = syncGenerator->GetElectricalDataRef();
1310
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;
1316
1317 // Power base change factor.
1318 double k = 1.0;
1319 if (data.useMachineBase) {
1320 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1321 k = m_powerSystemBase / oldBase;
1322 }
1323
1324 // Calculate id, iq, dq, sd
1325 if (!CalculateNonIntVariables(syncGenerator, id, iq, sd, sq, pe, k)) return false;
1326
1327 double genError = CalculateIntVariables(syncGenerator, id, iq, sd, sq, pe, k);
1328
1329 if (genError > error) error = genError;
1330 }
1331
1332 // Solve induction machine equation
1333 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1334 IndMotor* indMotor = *it;
1335
1336 const auto& data = indMotor->GetElectricalDataRef();
1337
1338 double ir = data.ir;
1339 double im = data.im;
1340 double te = data.te;
1341
1342 // Power base change factor.
1343 double k = 1.0;
1344 if (data.useMachinePowerAsBase) {
1345 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1346 k = m_powerSystemBase / oldBase;
1347 }
1348
1349 // Calculate te
1350 if (!CalculateNonIntVariables(indMotor, ir, im, te, k)) return false;
1351
1352 double motorError = CalculateIntVariables(indMotor, ir, im, te, k);
1353
1354 if (motorError > error) error = motorError;
1355 }
1356
1357 ++iterations;
1358
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.");
1362 return false;
1363 }
1364 }
1365 m_iterationsNum = iterations;
1366
1367 // Solve controllers.
1368 int ctrlRatio = static_cast<int>(1 / m_ctrlTimeStepMultiplier);
1369 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1370 SyncGenerator* syncGenerator = *it;
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()) /
1377 m_timeStep);
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);
1382
1383 for (int i = 0; i < ctrlRatio; ++i) data.avrSolver->SolveNextStep();
1384
1385 data.fieldVoltage = data.initialFieldVoltage + data.avrSolver->GetFieldVoltage();
1386 }
1387
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());
1394
1395 for (int i = 0; i < ctrlRatio; ++i) data.speedGovSolver->SolveNextStep();
1396
1397 data.pm = data.speedGovSolver->GetMechanicalPower();
1398 }
1399 //syncGenerator->SetElectricalData(data);
1400 }
1401
1402 return true;
1403}
1404
1405void Electromechanical::SaveData()
1406{
1407
1408 for(auto& syncGenerator : m_syncGeneratorList) {
1409 auto& data = syncGenerator->GetElectricalDataRef();
1410 if (data.plotSyncMachine) { syncGenerator->SavePlotData(); }
1411 }
1412
1413 for(auto& bus : m_busList) {
1414 auto& data = bus->GetElectricalDataRef();
1415 if (data.plotBus) {
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);
1418 }
1419 }
1420
1421 for(auto& load : m_loadList) {
1422 auto& data = load->GetElectricalDataRef();
1423 if (data.plotLoad) {
1424 data.voltageVector.emplace_back(data.voltage);
1425 data.electricalPowerVector.emplace_back(data.electricalPower);
1426 //load->SetElectricalData(data);
1427 }
1428 }
1429 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1430 IndMotor* motor = *it;
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));
1447 //motor->SetElectricalData(data);
1448 }
1449 }
1450 m_iterationsNumVector.emplace_back(m_iterationsNum);
1451}
1452
1453void Electromechanical::SetSyncMachinesModel()
1454{
1455 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1456 SyncGenerator* syncGenerator = *it;
1457 auto& data = syncGenerator->GetElectricalDataRef();
1458 data.model = GetMachineModel(syncGenerator);
1459 //syncGenerator->SetElectricalData(data);
1460 }
1461}
1462
1463bool Electromechanical::CalculateNonIntVariables(SyncGenerator* syncGenerator,
1464 double& id,
1465 double& iq,
1466 double& sd,
1467 double& sq,
1468 double& pe,
1469 double k)
1470{
1471 auto& data = syncGenerator->GetElectricalDataRef();
1472 Bus* bus = nullptr;
1473 if (GetParentBus(syncGenerator, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalDataRef().number]; }
1474
1475 double vd, vq;
1476 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1477
1478 if (data.satFactor != 0.0) {
1479 if (!CalculateSyncMachineSaturation(syncGenerator, id, iq, sd, sq, true, k)) return false;
1480 data.sd = sd;
1481 data.sq = sq;
1482 data.oldSd = sd;
1483 data.oldSq = sq;
1484 }
1485
1486 if (syncGenerator->IsOnline()) {
1487 pe = id * vd + iq * vq + (id * id + iq * iq) * data.armResistance * k;
1488 }
1489 else {
1490 pe = id = iq = 0.0f;
1491 }
1492 data.pe = pe;
1493 data.id = id;
1494 data.iq = iq;
1495 data.oldPe = pe;
1496 data.oldId = id;
1497 data.oldIq = iq;
1498 //syncGenerator->SetElectricalData(data);
1499
1500 return true;
1501}
1502
1503bool Electromechanical::CalculateNonIntVariables(IndMotor* indMotor, double& ir, double& im, double& te, double k)
1504{
1505 auto& data = indMotor->GetElectricalDataRef();
1506 if (indMotor->IsOnline()) {
1507 double w0 = 2.0 * M_PI * m_systemFreq;
1508 te = (data.tranEr * ir + data.tranEm * im) / w0;
1509 }
1510 else {
1511 te = ir = im = 0.0;
1512 }
1513 data.te = te;
1514 data.ir = ir;
1515 data.im = im;
1516 data.oldTe = te;
1517 data.oldIr = ir;
1518 data.oldIm = im;
1519 //indMotor->SetElectricalData(data);
1520
1521 return true;
1522}
1523
1524double Electromechanical::CalculateIntVariables(SyncGenerator* syncGenerator,
1525 double id,
1526 double iq,
1527 double sd,
1528 double sq,
1529 double pe,
1530 double k)
1531{
1532 double error = 0.0;
1533 auto& data = syncGenerator->GetElectricalDataRef();
1534
1535 // Mechanical differential equations.
1536 double w = data.icSpeed.c + data.icSpeed.m * (data.pm - pe);
1537 error = std::max(error, std::abs(data.speed - w) / m_refSpeed);
1538
1539 double delta = data.icDelta.c + data.icDelta.m * w;
1540 error = std::max(error, std::abs(data.delta - delta));
1541
1542 data.speed = w;
1543 data.delta = delta;
1544
1545 // Electrical differential equations
1546 switch (data.model) {
1547 case Machines::SM_MODEL_1: {
1548 // There is no differential equations.
1549 } break;
1550 case Machines::SM_MODEL_2: {
1551 double syncXd, transXd;
1552 syncXd = data.syncXd * k;
1553 transXd = data.transXd * k;
1554
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));
1558
1559 data.tranEq = tranEq;
1560 } break;
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;
1569
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));
1573
1574 double tranEd =
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));
1577
1578 data.tranEq = tranEq;
1579 data.tranEd = tranEd;
1580
1581 if (!syncGenerator->IsOnline()) {
1582 std::complex<double> e;
1583 DQ0toABC(data.tranEd, data.tranEq, data.delta, e);
1584 data.terminalVoltage = e;
1585 }
1586 } break;
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;
1597
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));
1601
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));
1605
1606 double subEd =
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));
1609
1610 data.tranEq = tranEq;
1611 data.subEq = subEq;
1612 data.subEd = subEd;
1613 } break;
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;
1626
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));
1630
1631 double tranEd =
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));
1634
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));
1638
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));
1642
1643 data.tranEq = tranEq;
1644 data.tranEd = tranEd;
1645 data.subEq = subEq;
1646 data.subEd = subEd;
1647 } break;
1648 }
1649
1650 //syncGenerator->SetElectricalData(data);
1651 return error;
1652}
1653
1654double Electromechanical::CalculateIntVariables(IndMotor* indMotor, double ir, double im, double te, double k)
1655{
1656 double error = 0.0;
1657 auto& data = indMotor->GetElectricalDataRef();
1658 double w0 = 2.0 * M_PI * m_systemFreq;
1659
1660 // Mechanical differential equations
1661 // Using Newton method to solve the non-linear slip equation: s = Cs + Ms * (C * s^2 - Te):
1662 double slip = data.slip; // Initial value. CAN BE THE PROBLEM ON MOTOR START!
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);
1665 int iteration = 0;
1666 while (std::abs(ds) > 1e-8) {
1667 slip += ds;
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);
1670 iteration++;
1671 if (iteration > m_maxIterations) break;
1672 }
1673
1674 //if(!indMotor->IsOnline()) slip = 1.0 - 1e-7;
1675 error = std::max(error, std::abs(data.slip - slip));
1676 data.slip = slip;
1677
1678 // Change T'0 with the cage factor
1679 if (data.useKf)
1680 data.t0 = (data.x2t + data.xmt) / (2.0 * M_PI * m_systemFreq * data.r2t * (1.0 + data.kf * data.r2t));
1681
1682 // Electrical differential equations
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));
1685
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));
1688
1689 data.tranEr = tranEr;
1690 data.tranEm = tranEm;
1691
1692 //indMotor->SetElectricalData(data);
1693 return error;
1694}
1695
1696void Electromechanical::CalculateReferenceSpeed()
1697{
1698 if (m_useCOI) {
1699 double sumHW = 0.0;
1700 double sumH = 0.0;
1701 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1702 SyncGenerator* syncGenerator = *it;
1703 if (syncGenerator->IsOnline()) {
1704 auto& data = syncGenerator->GetElectricalDataRef();
1705 double k = 1.0; // Power base change factor.
1706 if (data.useMachineBase) {
1707 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1708 k = m_powerSystemBase / oldBase;
1709 }
1710 sumH += data.inertia / k;
1711 sumHW += data.inertia * data.speed / k;
1712 }
1713 }
1714 m_refSpeed = sumHW / sumH;
1715 }
1716 else {
1717 m_refSpeed = 2.0 * M_PI * m_systemFreq;
1718 }
1719}
1720
1721bool Electromechanical::CalculateSyncMachineSaturation(SyncGenerator* syncMachine,
1722 double& id,
1723 double& iq,
1724 double& sd,
1725 double& sq,
1726 bool updateCurrents,
1727 double k)
1728{
1729 // [Ref] Arrillaga, J.; Arnold, C. P.. "Computer Modelling of Electrical Power Systems". Pg. 254-260
1730 auto& data = syncMachine->GetElectricalDataRef();
1731 auto smDataModel = GetSyncMachineModelData(syncMachine);
1732
1733 Bus* bus = nullptr;
1734 if (GetParentBus(syncMachine, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalDataRef().number]; }
1735 //else {
1736 //data.terminalVoltage = 0.0;
1737 //return true;
1738 //}
1739 double idCalc = id;
1740 double iqCalc = iq;
1741 double sdCalc = sd;
1742 double sqCalc = sq;
1743
1744 double vd, vq;
1745 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1746 double deltaVd = smDataModel.ed - vd;
1747 double deltaVq = smDataModel.eq - vq;
1748
1749 double ra = data.armResistance * k;
1750 double xd = smDataModel.xd;
1751 double xq = smDataModel.xq;
1752
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;
1757 syncXd = syncXq;
1758 }
1759 else if (data.syncXq == 0.0)
1760 syncXq = data.syncXd * k;
1761
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);
1766
1767 bool exit = false;
1768 int iterations = 0;
1769 while (!exit) {
1770 double oldSd = sdCalc;
1771 double oldSq = sqCalc;
1772
1773 // Saturated reactances.
1774 double xds = (xd - xp) / sdCalc + xp;
1775 double xqs = (xq - xp) / sqCalc + xp;
1776 // dq currents.
1777 double den = 1.0 / (ra * ra + xds * xqs);
1778 iqCalc = den * (ra * deltaVq + xds * deltaVd);
1779 idCalc = den * (-xqs * deltaVq + ra * deltaVd);
1780 // Potier voltages
1781 double epq = vq + ra * iqCalc - xp * idCalc;
1782 double epd = vd + ra * idCalc + xp * iqCalc;
1783 // Saturation factors.
1784 // Gauss
1785 /*sdCalc = 1.0 + satFacd * std::pow(epq, 6);
1786 sqCalc = 1.0 + satFacq * std::pow(epd, 6);*/
1787
1788 // Newton-raphson
1789 double f1 = 1.0 - sdCalc + satFacd * std::pow(epq, 6);
1790 double f2 = 1.0 - sqCalc + satFacq * std::pow(epd, 6);
1791 double dF1dSd =
1792 (6.0 * satFacd * std::pow(epq, 5) * xp * (xd - xp) * deltaVq) / ((sdCalc - 1.0) * xp + xd) - 1.0;
1793 double dF2dSq =
1794 (6.0 * satFacq * std::pow(epd, 5) * xp * (xq - xp) * deltaVd) / ((sqCalc - 1.0) * xp + xq) - 1.0;
1795
1796 sdCalc = sdCalc - f1 / dF1dSd;
1797 sqCalc = sqCalc - f2 / dF2dSq;
1798
1799 double error = std::abs(sdCalc - oldSd) + std::abs(sqCalc - oldSq);
1800 if (error < m_saturationTolerance) exit = true;
1801
1802 iterations++;
1803 if ((iterations >= m_maxIterations) && !exit) {
1804 m_errorMsg =
1805 _("It was not possible to solve the saturation of the synchronous machine \"") + data.name + wxT("\".");
1806 return false;
1807 }
1808 }
1809
1810 sd = sdCalc;
1811 sq = sqCalc;
1812 if (updateCurrents) {
1813 id = idCalc;
1814 iq = iqCalc;
1815 }
1816 return true;
1817}
1818
1819void Electromechanical::CalculateBusesFrequency(bool hasEvent)
1820{
1821 bool ignoreEventStep = true;
1822 for (auto& bus : m_busList) {
1823 auto& data = bus->GetElectricalDataRef();
1824 if (!data.plotBus) continue;
1825
1826 if (m_simData.busFreqEstimation == BusFreqEstimation::WASHOUT_FILTER) {
1827 //tex:
1828 // Low-pass filter:
1829 // $$\frac{d}{dt}x_{\theta} = x_{\theta}' = \frac{\omega^{-1}(\theta_k - \theta_0) - x_{\theta}}{T_f}$$
1830 // Washout filter:
1831 // $$\frac{d}{dt}\Delta\omega_{k} = \Delta\omega_{k}' = \frac{x_{\theta}' - \Delta\omega_{k}}{T_w}$$
1832 // Trapezoidal rule:
1833 // $$ y_{k} = y_{k-1} + 0.5 h \cdot (y_{k-1}'+y_{k}') $$
1834
1835 double tf = m_simData.tf;
1836 double tw = m_simData.tw;
1837 double error = 1.0;
1838 double theta0 = std::arg(data.number >= 0 ? data.voltage : 0.0);
1839
1840 // Low-pass filter
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);
1843 };
1844
1845 // Washout filter
1846 auto fddw = [this, tw](double dxt, double dw) {
1847 return (1.0 / tw) * (dxt - dw);
1848 };
1849
1850 double theta = std::arg(data.number >= 0 ? m_vBus[data.number] : theta0);
1851
1852 double xt = 0, dw = 0, dxt = 0, ddw = 0;
1853 int iterations = 0;
1854 while (error > 1e-6 && iterations <= 100) {
1855 double oldXT = xt, oldDW = dw;
1856
1857 dxt = fdxt(theta, xt);
1858 xt = data.filteredAngle + 0.5 * m_timeStep * (data.dxt + dxt); // Trapezoidal rule
1859 error = std::abs(xt - oldXT);
1860
1861 ddw = fddw(dxt, dw);
1862 dw = data.filteredVelocity + 0.5 * m_timeStep * (data.ddw + ddw); // Trapezoidal rule
1863 error = std::max(error, std::abs(dw - oldDW));
1864 iterations++;
1865 }
1866
1867 //if (ignoreEventStep && hasEvent) {
1868 // xt = data.filteredAngle;
1869 // dw = 0.0;
1870 // dxt = 0.0;
1871 // ddw = 0.0;
1872 //}
1873
1874 data.filteredAngle = xt;
1875 data.filteredVelocity = dw;
1876 data.dxt = dxt;
1877 data.ddw = ddw;
1878
1879 double busVelocity = m_refSpeed + dw * (2.0 * M_PI * m_systemFreq);
1880 data.stabFreq = busVelocity / (2.0 * M_PI);
1881
1882 //bus->SetElectricalData(data);
1883 }
1884 else if (m_simData.busFreqEstimation == BusFreqEstimation::ANGLE_DERIVATION) {
1885 double newAngle = std::arg(data.number >= 0 ? m_vBus[data.number] : 0.0);
1886
1887
1888 //tex: $$\Delta \omega^k=\frac{\theta^k - \theta^{k-1}}{h}$$
1889 double dw = (newAngle - data.oldAngle) / m_timeStep;
1890 //double dw = (1.0 / (2.0 * M_PI * m_systemFreq)) * ((newAngle - data.oldAngle) / m_timeStep); // p.u.
1891
1892 if (m_simData.ignoreBusFreqEventStep && hasEvent) dw = 0.0;
1893
1894 //tex: $$f^k= \frac{\omega_{CoI}+\Delta \omega^k}{2\pi}$$
1895 data.stabFreq = (m_refSpeed + dw) / (2.0 * M_PI);
1896 //data.stabFreq = ((2.0 * M_PI * m_systemFreq) + dw) / (2.0 * M_PI);
1897
1898 data.oldAngle = newAngle;
1899 }
1900 bus->SetElectricalData(data);
1901 }
1902}
1903
1904SyncMachineModelData Electromechanical::GetSyncMachineModelData(SyncGenerator* syncMachine)
1905{
1906 SyncMachineModelData smModelData;
1907
1908 const auto& data = syncMachine->GetElectricalDataRef();
1909 double k = 1.0; // Power base change factor.
1910 if (data.useMachineBase) {
1911 double oldBase = syncMachine->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1912 k = m_powerSystemBase / oldBase;
1913 }
1914
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;
1921 } break;
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; }
1930 }
1931 } break;
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;
1938 } break;
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;
1947 } break;
1948 }
1949 return smModelData;
1950}
1951
1952void Electromechanical::PreallocateVectors()
1953{
1954 int numPoints = static_cast<unsigned int>(m_simTime / m_plotTime) + 100;
1955
1956 m_timeVector.reserve(numPoints);
1957 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1958 SyncGenerator* syncGenerator = *it;
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);
1967 //syncGenerator->SetElectricalData(data);
1968 }
1969 }
1970 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
1971 Bus* bus = *it;
1972 auto& data = bus->GetElectricalDataRef();
1973 if (data.plotBus) {
1974 data.stabVoltageVector.reserve(numPoints);
1975 data.stabFreqVector.reserve(numPoints);
1976 //bus->SetElectricalData(data);
1977 }
1978 }
1979 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
1980 Load* load = *it;
1981 auto& data = load->GetElectricalDataRef();
1982 if (data.plotLoad) {
1983 data.voltageVector.reserve(numPoints);
1984 data.electricalPowerVector.reserve(numPoints);
1985 //load->SetElectricalData(data);
1986 }
1987 }
1988 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1989 IndMotor* motor = *it;
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);
2000 //motor->SetElectricalData(data);
2001 }
2002 }
2003
2004 m_iterationsNumVector.reserve(numPoints);
2005}
2006
2007std::complex<double> Electromechanical::GetIndMachineAdmittance(IndMotor* motor)
2008{
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));
2013 // The difference between calculated and defined reactive power
2014 std::complex<double> yq = std::complex<double>(0.0, data.q0 - dataPU.reactivePower) / (std::abs(v) * std::abs(v));
2015 return y0 + yq;
2016}
2017
2018bool Electromechanical::InsertIndMachinesOnYBus()
2019{
2020 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
2021 IndMotor* motor = *it;
2022 if (!CalculateIndMachinesTransientValues(motor)) return false;
2023 if (motor->IsOnline()) {
2024 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().number;
2025 m_yBus[n][n] += GetIndMachineAdmittance(motor);
2026 }
2027 }
2028 return true;
2029}
2030
2031bool Electromechanical::CalculateIndMachinesTransientValues(IndMotor* motor)
2032{
2033 auto& data = motor->GetElectricalDataRef();
2034 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
2035 double k = 1.0; // Power base change factor.
2036 if (data.useMachinePowerAsBase) {
2037 double oldBase = motor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
2038 k = m_powerSystemBase / oldBase;
2039 }
2040
2041 data.terminalVoltage = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().voltage;
2042
2043 // Calculate the induction machine transient constants at the machine base
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;
2049 data.r1t = r1t;
2050 data.r2t = r2t;
2051 data.x1t = x1t;
2052 data.x2t = x2t;
2053 data.xmt = xmt;
2054
2055 double xt = x1t + (x2t * xmt) / (x2t + xmt);
2056 double x0 = x1t + xmt;
2057 data.xt = xt;
2058 data.x0 = x0;
2059
2060 double p = dataPU.activePower;
2061 double v = std::abs(data.terminalVoltage);
2062
2063 //[Ref.] Induction Motor Static Models for Power Flow and Voltage stability Studies
2064 // If the motor is offline, calculate the nominal slip to user-defined power input and 1.0 p.u. voltage
2065 if (!motor->IsOnline()) v = 1.0;
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);
2080 if (d < 0.0) {
2081 m_errorMsg = _("Error on initializate the slip of \"") + data.name + _("\".");
2082 return false;
2083 }
2084 double r2_s = (-b + std::sqrt(d)) / (2.0 * a);
2085 data.s0 = r2 / r2_s;
2086
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);
2092
2093 data.t0 = (x2t + xmt) / (2.0 * M_PI * m_systemFreq * r2);
2094
2095 //motor->SetElectricalData(data);
2096 return true;
2097}
@ POSITIVE_SEQ
Node for power elements. All others power elements are connected through this.
Definition Bus.h:86
Shunt capactior power element.
Definition Capacitor.h:39
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.
Definition Element.h:559
bool IsOnline() const
Checks if the element is online or offline.
Definition Element.h:226
bool SetOnline(bool online=true)
Set if the element is online or offline.
Definition Element.cpp:378
Induction motor power element.
Definition IndMotor.h:119
Inductor shunt power element.
Definition Inductor.h:39
Power line element.
Definition Line.h:64
Loas shunt power element.
Definition Load.h:74
Abstract class of power elements.
virtual SwitchingData GetSwitchingData()
Returns the switching data of the element.
Synchronous generator power element.
Two-winding transformer power element.
Definition Transformer.h:84
Switching data of power elements.
std::vector< double > swTime
std::vector< SwitchingType > swType
Synchronous machine data for different models.