Power System Platform  2026w10a-beta
Loading...
Searching...
No Matches
ElectricCalculation.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 "ElectricCalculation.h"
19#ifdef USING_WX_3_0_X
20#include "../utils/DegreesAndRadians.h"
21#endif
22
23#include <wx/clipbrd.h>
24
27void ElectricCalculation::GetElementsFromList(std::vector<Element*> elementList)
28{
29 m_powerElementList.clear();
30 m_busList.clear();
31 m_capacitorList.clear();
32 m_indMotorList.clear();
33 m_inductorList.clear();
34 m_lineList.clear();
35 m_loadList.clear();
36 m_syncGeneratorList.clear();
37 m_syncMotorList.clear();
38 m_transformerList.clear();
39 m_harmCurrentList.clear();
40 m_emtElementList.clear();
41 // TODO: Bad design?
42 for (auto it = elementList.begin(); it != elementList.end(); it++) {
43 Element* element = *it;
44 m_powerElementList.push_back(static_cast<PowerElement*>(element));
45
46 if (Bus* bus = dynamic_cast<Bus*>(element))
47 m_busList.push_back(bus);
48 else if (Capacitor* capacitor = dynamic_cast<Capacitor*>(element))
49 m_capacitorList.push_back(capacitor);
50 else if (IndMotor* indMotor = dynamic_cast<IndMotor*>(element))
51 m_indMotorList.push_back(indMotor);
52 else if (Inductor* inductor = dynamic_cast<Inductor*>(element))
53 m_inductorList.push_back(inductor);
54 else if (Line* line = dynamic_cast<Line*>(element))
55 m_lineList.push_back(line);
56 else if (Load* load = dynamic_cast<Load*>(element))
57 m_loadList.push_back(load);
58 else if (SyncGenerator* syncGenerator = dynamic_cast<SyncGenerator*>(element))
59 m_syncGeneratorList.push_back(syncGenerator);
60 else if (SyncMotor* syncMotor = dynamic_cast<SyncMotor*>(element))
61 m_syncMotorList.push_back(syncMotor);
62 else if (Transformer* transformer = dynamic_cast<Transformer*>(element))
63 m_transformerList.push_back(transformer);
64 else if (HarmCurrent* harmCurrent = dynamic_cast<HarmCurrent*>(element))
65 m_harmCurrentList.push_back(harmCurrent);
66 else if (EMTElement* emtElement = dynamic_cast<EMTElement*>(element))
67 m_emtElementList.push_back(emtElement);
68 }
69
70 // Set buses numbers
71 int busNumber = 0;
72 for (auto itb = m_busList.begin(); itb != m_busList.end(); itb++) {
73 Bus* bus = *itb;
74 BusElectricalData data = bus->GetElectricalData();
75 data.number = busNumber;
76 bus->SetElectricalData(data);
77 busNumber++;
78 }
79}
80
81bool ElectricCalculation::GetYBus(std::vector<std::vector<std::complex<double> > >& yBus,
82 double systemPowerBase,
83 YBusSequence sequence,
84 bool includeSyncMachines,
85 bool allLoadsAsImpedances,
86 bool usePowerFlowVoltagesOnImpedances)
87{
88 if (m_busList.size() == 0) return false;
89
90 // Clear and fill with zeros the Ybus
91 yBus.clear();
92 for (int i = 0; i < (int)m_busList.size(); i++) {
93 std::vector<std::complex<double> > line;
94 for (int j = 0; j < (int)m_busList.size(); j++) { line.push_back(std::complex<double>(0.0, 0.0)); }
95 yBus.push_back(line);
96 }
97
98 // Set buses numbers and reset connection status
99 int busNumber = 0;
100 for (auto itb = m_busList.begin(); itb != m_busList.end(); itb++) {
101 Bus* bus = *itb;
102 BusElectricalData data = bus->GetElectricalData();
103 data.number = busNumber;
104 data.isConnected = true;
105 //data.voltage = std::complex<double>(1.0, 0.0);
106 bus->SetElectricalData(data);
107 busNumber++;
108 }
109
110 // Load
111 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
112 Load* load = *it;
113 if (load->IsOnline()) {
114 int n = static_cast<Bus*>(load->GetParentList()[0])->GetElectricalData().number;
115 LoadElectricalData data = load->GetPUElectricalData(systemPowerBase);
116 if (data.loadType == CONST_IMPEDANCE || allLoadsAsImpedances) {
117 std::complex<double> yLoad = std::complex<double>(data.activePower, -data.reactivePower);
118 if (allLoadsAsImpedances && usePowerFlowVoltagesOnImpedances) {
119 std::complex<double> v = static_cast<Bus*>(load->GetParentList()[0])->GetElectricalData().voltage;
120 yLoad /= (std::abs(v) * std::abs(v));
121 }
122 yBus[n][n] += yLoad;
123 }
124 }
125 }
126
127 // Capacitor
128 for (auto it = m_capacitorList.begin(), itEnd = m_capacitorList.end(); it != itEnd; ++it) {
129 Capacitor* capacitor = *it;
130 if (capacitor->IsOnline()) {
131 int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalData().number;
132 CapacitorElectricalData data = capacitor->GetPUElectricalData(systemPowerBase);
133 yBus[n][n] += std::complex<double>(0.0, data.reactivePower);
134 }
135 }
136
137 // Inductor
138 for (auto it = m_inductorList.begin(), itEnd = m_inductorList.end(); it != itEnd; ++it) {
139 Inductor* inductor = *it;
140 if (inductor->IsOnline()) {
141 int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalData().number;
142 InductorElectricalData data = inductor->GetPUElectricalData(systemPowerBase);
143 yBus[n][n] += std::complex<double>(0.0, -data.reactivePower);
144 }
145 }
146
147 //EMT Elements
148 for (EMTElement* emtElement : m_emtElementList) {
149 if (emtElement->IsOnline()) {
150 auto data = emtElement->GetEMTElementData();
151 int n = static_cast<Bus*>(emtElement->GetParentList()[0])->GetElectricalData().number;
152 yBus[n][n] += data.y0;
153 //wxMessageBox(wxString::Format("%f +j %f", data.y0.real(), data.y0.imag()));
154 }
155 }
156
157 // Power line
158 for (auto it = m_lineList.begin(), itEnd = m_lineList.end(); it != itEnd; ++it) {
159 Line* line = *it;
160 if (line->IsOnline()) {
161 LineElectricalData data = line->GetPUElectricalData(systemPowerBase);
162
163 int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData().number;
164 int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData().number;
165
166 switch (sequence) {
167 case POSITIVE_SEQ:
168 case NEGATIVE_SEQ: {
169 yBus[n1][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
170 yBus[n2][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
171
172 yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
173 yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
174
175 yBus[n1][n1] += std::complex<double>(0.0, data.capSusceptance / 2.0);
176 yBus[n2][n2] += std::complex<double>(0.0, data.capSusceptance / 2.0);
177 } break;
178 case ZERO_SEQ: {
179 yBus[n1][n2] -= 1.0 / std::complex<double>(data.zeroResistance, data.zeroIndReactance);
180 yBus[n2][n1] -= 1.0 / std::complex<double>(data.zeroResistance, data.zeroIndReactance);
181
182 yBus[n1][n1] += 1.0 / std::complex<double>(data.zeroResistance, data.zeroIndReactance);
183 yBus[n2][n2] += 1.0 / std::complex<double>(data.zeroResistance, data.zeroIndReactance);
184
185 yBus[n1][n1] += std::complex<double>(0.0, data.zeroCapSusceptance / 2.0);
186 yBus[n2][n2] += std::complex<double>(0.0, data.zeroCapSusceptance / 2.0);
187 }
188 }
189 }
190 }
191
192 // Transformer
193 for (auto it = m_transformerList.begin(), itEnd = m_transformerList.end(); it != itEnd; ++it) {
194 Transformer* transformer = *it;
195
196 if (transformer->IsOnline()) {
197 TransformerElectricalData data = transformer->GetPUElectricalData(systemPowerBase);
198
199 int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalData().number;
200 int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalData().number;
201
202 // If the transformer have nominal turns ratio (1.0) and no phase shifting, it will be modelled as series
203 // impedance.
204 if (data.turnsRatio == 1.0 && data.phaseShift == 0.0 && sequence != ZERO_SEQ) {
205 yBus[n1][n2] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
206 yBus[n2][n1] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
207
208 yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
209 yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
210 }
211 // If the transformer have no-nominal turn ratio and/or phase shifting, it will be modelled in a different
212 // way (see references).
213 //[Ref. 1: Elementos de analise de sistemas de potencia - Stevenson - pg. 232]
214 //[Ref. 2: http://www.ee.washington.edu/research/real/Library/Reports/Tap_Adjustments_in_AC_Load_Flows.pdf]
215 //[Ref. 3: http://www.columbia.edu/~dano/courses/power/notes/power/andersson1.pdf]
216 else if (sequence != ZERO_SEQ) {
217 // Complex turns ratio
218 double radPhaseShift = wxDegToRad(data.phaseShift);
219 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
220 -data.turnsRatio * std::sin(radPhaseShift));
221
222 // Transformer admitance
223 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
224
225 if (sequence == POSITIVE_SEQ) {
226 yBus[n1][n1] += y / std::pow(std::abs(a), 2.0);
227 yBus[n1][n2] += -(y / std::conj(a));
228 yBus[n2][n1] += -(y / a);
229 yBus[n2][n2] += y;
230 }
231 else if (sequence == NEGATIVE_SEQ) {
232 yBus[n1][n1] += y / std::pow(std::abs(a), 2.0);
233 yBus[n1][n2] += -(y / a);
234 yBus[n2][n1] += -(y / std::conj(a));
235 yBus[n2][n2] += y;
236 }
237 }
238 else if (sequence == ZERO_SEQ) {
239 switch (data.connection) {
240 case GWYE_GWYE: {
241 std::complex<double> y =
242 1.0 /
243 std::complex<double>(
244 data.zeroResistance + 3.0 * (data.primaryGrndResistance + data.secondaryGrndResistance),
245 data.zeroIndReactance +
246 3.0 * (data.primaryGrndReactance + data.secondaryGrndReactance));
247 std::complex<double> a = std::complex<double>(data.turnsRatio, 0.0);
248
249 yBus[n1][n1] += y / (a * a);
250 yBus[n1][n2] += -(y / a);
251 yBus[n2][n1] += -(y / a);
252 yBus[n2][n2] += y;
253 } break;
254 case DELTA_GWYE: {
255 std::complex<double> y =
256 1.0 / std::complex<double>(data.zeroResistance + 3.0 * (data.secondaryGrndResistance),
257 data.zeroIndReactance + 3.0 * (data.secondaryGrndReactance));
258 yBus[n2][n2] += y;
259 yBus[n1][n1] += 1e-4;
260 yBus[n1][n2] += 1e-4;
261 yBus[n2][n1] += 1e-4;
262 break;
263 }
264 case GWYE_DELTA: {
265 std::complex<double> y =
266 1.0 / std::complex<double>(data.zeroResistance + 3.0 * (data.primaryGrndResistance),
267 data.zeroIndReactance + 3.0 * (data.primaryGrndReactance));
268 yBus[n1][n1] += y;
269 yBus[n2][n2] += 1e-4;
270 yBus[n1][n2] += 1e-4;
271 yBus[n2][n1] += 1e-4;
272 break;
273 }
274 default:
275 break;
276 }
277 }
278 }
279 }
280
281 if (includeSyncMachines) {
282 // Synchronous generator
283 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
284 SyncGenerator* syncGenerator = *it;
285 if (syncGenerator->IsOnline()) {
286 int n = static_cast<Bus*>(syncGenerator->GetParentList()[0])->GetElectricalData().number;
287 SyncGeneratorElectricalData data = syncGenerator->GetPUElectricalData(systemPowerBase);
288 switch (sequence) {
289 case POSITIVE_SEQ: {
290 yBus[n][n] += 1.0 / std::complex<double>(data.positiveResistance, data.positiveReactance);
291 } break;
292 case NEGATIVE_SEQ: {
293 yBus[n][n] += 1.0 / std::complex<double>(data.negativeResistance, data.negativeReactance);
294 } break;
295 case ZERO_SEQ: {
296 if (data.groundNeutral) {
297 yBus[n][n] += 1.0 / std::complex<double>(data.zeroResistance + 3.0 * data.groundResistance,
298 data.zeroReactance + 3.0 * data.groundReactance);
299 }
300 } break;
301 }
302 }
303 }
304 // Synchronous motor
305 for (auto it = m_syncMotorList.begin(), itEnd = m_syncMotorList.end(); it != itEnd; ++it) {
306 SyncMotor* syncMotor = *it;
307 if (syncMotor->IsOnline()) {
308 int n = static_cast<Bus*>(syncMotor->GetParentList()[0])->GetElectricalData().number;
309 SyncMotorElectricalData data = syncMotor->GetPUElectricalData(systemPowerBase);
310 switch (sequence) {
311 case POSITIVE_SEQ: {
312 yBus[n][n] += 1.0 / std::complex<double>(data.positiveResistance, data.positiveReactance);
313 } break;
314 case NEGATIVE_SEQ: {
315 yBus[n][n] += 1.0 / std::complex<double>(data.negativeResistance, data.negativeReactance);
316 } break;
317 case ZERO_SEQ: {
318 if (data.groundNeutral) {
319 yBus[n][n] += 1.0 / std::complex<double>(data.zeroResistance + 3.0 * data.groundResistance,
320 data.zeroReactance + 3.0 * data.groundReactance);
321 }
322 } break;
323 }
324 }
325 }
326 }
327
328 // Identify buses to slack bus (power flow)
329 //if (sequence != ZERO_SEQ) {
330 std::vector<bool> connectedToSlack(m_busList.size(), false);
331 busNumber = 0;
332 int slackBus = 0;
333 std::vector<int> checkBusVector;
334 for (auto* bus : m_busList)
335 {
336 if (bus->GetElectricalData().slackBus)
337 {
338 connectedToSlack[busNumber] = true;
339 slackBus = busNumber;
340 break;
341 }
342 busNumber++;
343 }
344 GetNextConnection(slackBus, yBus, connectedToSlack);
345
346 bool hasIsolatedBus = false;
347 for (auto isConnectedToSlack : connectedToSlack)
348 {
349 if (!isConnectedToSlack) hasIsolatedBus = true;
350 }
351
352 if (hasIsolatedBus) {
353 // Update isolated and connected buses status and numbers
354 busNumber = 0;
355 for (unsigned int i = 0; i < m_busList.size(); ++i)
356 {
357 auto data = m_busList[i]->GetElectricalData();
358 data.isConnected = connectedToSlack[i];
359 if (connectedToSlack[i])
360 {
361 data.number = busNumber;
362 busNumber++;
363 }
364 else {
365 data.number = -1;
366 }
367 m_busList[i]->SetElectricalData(data);
368 }
369
370 // Create new Ybus without isolated buses (remove rows and columns)
371 std::vector< std::vector< std::complex<double> > > newYBus;
372 for (unsigned int i = 0; i < yBus.size(); ++i) {
373 if (connectedToSlack[i]) {
374 std::vector< std::complex<double> > newLine;
375 for (unsigned int j = 0; j < yBus.size(); ++j) {
376 if (connectedToSlack[j]) {
377 newLine.push_back(yBus[i][j]);
378 }
379 }
380 newYBus.push_back(newLine);
381 }
382 }
383
384 yBus.clear();
385 yBus = newYBus;
386 }
387 //}
388
389 return true;
390}
391
392void ElectricCalculation::GetNextConnection(const unsigned int& checkBusNumber,
393 const std::vector< std::vector< std::complex<double> > >& yBus,
394 std::vector<bool>& connToSlack)
395{
396 for (unsigned int i = 0; i < yBus.size(); i++) {
397
398 if (i != checkBusNumber && !connToSlack[i]) {
399 if (std::abs(yBus[i][checkBusNumber]) > 1e-6)
400 {
401 connToSlack[i] = true;
402 GetNextConnection(i, yBus, connToSlack);
403 }
404 }
405 }
406}
407
408void ElectricCalculation::DistributeReactivePower(std::vector<ReactiveMachine>& machines, double qTotal)
409{
410 int freeMachines = machines.size();
411 double qRemaining = qTotal;
412
413 bool changed = true;
414
415 while (changed && freeMachines > 0) {
416 changed = false;
417
418 double qShare = qRemaining / freeMachines;
419
420 for (auto& m : machines) {
421 if (m.fixed)
422 continue;
423
424 if (m.hasMax && qShare > m.qMax) {
425 m.q = m.qMax;
426 m.fixed = true;
427 qRemaining -= m.qMax;
428 freeMachines--;
429 changed = true;
430 }
431 else if (m.hasMin && qShare < m.qMin) {
432 m.q = m.qMin;
433 m.fixed = true;
434 qRemaining -= m.qMin;
435 freeMachines--;
436 changed = true;
437 }
438 else {
439 m.q = qShare;
440 }
441 }
442 }
443
444 if (freeMachines > 0) {
445 double qShare = qRemaining / freeMachines;
446 for (auto& m : machines) {
447 if (!m.fixed)
448 m.q = qShare;
449 }
450 }
451}
452
453void ElectricCalculation::UpdateElementsPowerFlow(std::vector<std::complex<double> > voltage,
454 std::vector<std::complex<double> > power,
455 std::vector<BusType> busType,
456 std::vector<ReactiveLimits> reactiveLimit,
457 double systemPowerBase)
458{
459 double zeroLimit = 1e-4;
460 for (unsigned int i = 0; i < reactiveLimit.size(); ++i) {
461 if (reactiveLimit[i].maxLimit > -zeroLimit && reactiveLimit[i].maxLimit < zeroLimit)
462 reactiveLimit[i].maxLimit = zeroLimit;
463 if (reactiveLimit[i].minLimit > -zeroLimit && reactiveLimit[i].minLimit < zeroLimit)
464 reactiveLimit[i].minLimit = zeroLimit;
465 }
466 for (unsigned int i = 0; i < power.size(); ++i) {
467 if (std::real(power[i]) > -zeroLimit && std::real(power[i]) < zeroLimit)
468 power[i] = std::complex<double>(0.0, std::imag(power[i]));
469 if (std::imag(power[i]) > -zeroLimit && std::imag(power[i]) < zeroLimit)
470 power[i] = std::complex<double>(std::real(power[i]), 0.0);
471 }
472 // Buses
473 for (int i = 0; i < (int)m_busList.size(); i++) {
474 Bus* bus = m_busList[i];
475 BusElectricalData data = bus->GetElectricalData();
476 if (data.isConnected) {
477 data.voltage = voltage[data.number];
478 data.power = power[data.number];
479 data.busType = busType[data.number];
480 }
481 else {
482 data.voltage = std::complex<double>(0.0, 0.0);
483 data.power = std::complex<double>(0.0, 0.0);
484 }
485 bus->SetElectricalData(data);
486 }
487
488 // Power line
489 for (int i = 0; i < (int)m_lineList.size(); i++) {
490 Line* line = m_lineList[i];
491 if (line->IsOnline()) {
492 auto dataBus1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData();
493 auto dataBus2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData();
494
495 if (dataBus1.isConnected && dataBus2.isConnected) {
496 int n1 = dataBus1.number;
497 int n2 = dataBus2.number;
498
499 LineElectricalData data = line->GetElectricalData();
500 LineElectricalData dataPU = line->GetPUElectricalData(systemPowerBase);
501 std::complex<double> v1 = voltage[n1];
502 std::complex<double> v2 = voltage[n2];
503
504 data.current[0] = (v1 - v2) / std::complex<double>(dataPU.resistance, dataPU.indReactance) +
505 v1 * std::complex<double>(0.0, dataPU.capSusceptance / 2.0);
506 data.current[1] = (v2 - v1) / std::complex<double>(dataPU.resistance, dataPU.indReactance) +
507 v2 * std::complex<double>(0.0, dataPU.capSusceptance / 2.0);
508
509 data.powerFlow[0] = v1 * std::conj(data.current[0]);
510 data.powerFlow[1] = v2 * std::conj(data.current[1]);
511
512 if (data.powerFlow[0].real() > data.powerFlow[1].real())
514 else
516
517 line->SetElectricalData(data);
518 }
519 }
520 }
521
522 // Transformer
523 for (int i = 0; i < (int)m_transformerList.size(); i++) {
524 Transformer* transformer = m_transformerList[i];
525 if (transformer->IsOnline()) {
526 auto dataBus1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalData();
527 auto dataBus2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalData();
528
529 if (dataBus1.isConnected && dataBus2.isConnected) {
530 TransformerElectricalData data = transformer->GetElectricalData();
531 TransformerElectricalData dataPU = transformer->GetPUElectricalData(systemPowerBase);
532
533 int n1 = dataBus1.number;
534 int n2 = dataBus2.number;
535
536 std::complex<double> v1 = voltage[n1]; // Primary voltage
537 std::complex<double> v2 = voltage[n2]; // Secondary voltage
538
539 // Transformer admitance
540 std::complex<double> y = 1.0 / std::complex<double>(dataPU.resistance, dataPU.indReactance);
541
542 if (dataPU.turnsRatio == 1.0 && dataPU.phaseShift == 0.0) {
543 data.current[0] = (v1 - v2) * y;
544 data.current[1] = (v2 - v1) * y;
545 }
546 else {
547 double radPS = wxDegToRad(dataPU.phaseShift);
548 std::complex<double> a =
549 std::complex<double>(dataPU.turnsRatio * std::cos(radPS), -dataPU.turnsRatio * std::sin(radPS));
550
551 data.current[0] = v1 * (y / std::pow(std::abs(a), 2)) - v2 * (y / std::conj(a));
552 data.current[1] = -v1 * (y / a) + v2 * y;
553 }
554
555 data.powerFlow[0] = v1 * std::conj(data.current[0]);
556 data.powerFlow[1] = v2 * std::conj(data.current[1]);
557
558 if (data.powerFlow[0].real() > data.powerFlow[1].real())
560 else
562
563 transformer->SetElectricaData(data);
564 }
565 }
566 }
567
568 // Ind motor
569 for (unsigned int i = 0; i < m_indMotorList.size(); i++) {
570 IndMotor* motor = m_indMotorList[i];
571 auto data = motor->GetElectricalData();
572 if (motor->IsOnline() && data.calcQInPowerFlow) {
573 double reactivePower = data.qValue * systemPowerBase;
574
575 switch (data.reactivePowerUnit) {
577 reactivePower /= systemPowerBase;
578 } break;
580 reactivePower /= 1e3;
581 } break;
583 reactivePower /= 1e6;
584 } break;
585 default:
586 break;
587 }
588
589 data.reactivePower = reactivePower;
590
591 motor->SetElectricalData(data);
592 }
593 }
594
595 //EMT Elements
596 for (EMTElement* emtElement : m_emtElementList) {
597
598 if (!emtElement->IsOnline()) {
599 auto data = emtElement->GetEMTElementData();
600 data.power = std::complex<double>(0.0, 0.0);
601 data.currHarmonics[1] = std::complex<double>(0.0, 0.0);
602 emtElement->SetEMTElementData(data);
603 }
604 else {
605 emtElement->UpdateData();
606 auto data = emtElement->GetEMTElementData();
607 double baseCurrent = systemPowerBase / (sqrt(3.0) * data.baseVoltage);
608 std::complex<double> current = data.currHarmonics[1] / baseCurrent; // Fundamental frequency current
609 std::complex<double> power = data.puVoltage * std::conj(current);
610 data.power = power;
611 emtElement->SetEMTElementData(data);
612 }
613 }
614
615 // Synchronous machines
616 for (auto* bus : m_busList) {
617 BusElectricalData data = bus->GetElectricalData();
618 int i = data.number;
619
620 if (data.isConnected) {
621 // Get the synchronous machines connected and calculate the load power on the bus.
622 std::vector<SyncGenerator*> syncGeneratorsOnBus;
623 std::vector<SyncMotor*> syncMotorsOnBus;
624 std::complex<double> loadPower(0.0, 0.0);
625
626 for (auto itsg = m_syncGeneratorList.begin(); itsg != m_syncGeneratorList.end(); itsg++) {
627 SyncGenerator* syncGenerator = *itsg;
628 if (bus == syncGenerator->GetParentList()[0] && syncGenerator->IsOnline())
629 syncGeneratorsOnBus.push_back(syncGenerator);
630 }
631 for (auto itsm = m_syncMotorList.begin(); itsm != m_syncMotorList.end(); itsm++) {
632 SyncMotor* syncMotor = *itsm;
633 if (bus == syncMotor->GetParentList()[0] && syncMotor->IsOnline()) {
634 syncMotorsOnBus.push_back(syncMotor);
635 SyncMotorElectricalData childData = syncMotor->GetPUElectricalData(systemPowerBase);
636 loadPower += std::complex<double>(childData.activePower, 0.0);
637 }
638 }
639 for (auto itlo = m_loadList.begin(); itlo != m_loadList.end(); itlo++) {
640 Load* load = *itlo;
641 if (bus == load->GetParentList()[0] && load->IsOnline()) {
642 LoadElectricalData childData = load->GetPUElectricalData(systemPowerBase);
643 if (childData.loadType == CONST_POWER)
644 loadPower += std::complex<double>(childData.activePower, childData.reactivePower);
645
646 if (childData.activePower >= 0.0)
648 else
650 }
651 }
652 for (auto itim = m_indMotorList.begin(); itim != m_indMotorList.end(); itim++) {
653 IndMotor* indMotor = *itim;
654 if (bus == indMotor->GetParentList()[0] && indMotor->IsOnline()) {
655 IndMotorElectricalData childData = indMotor->GetPUElectricalData(systemPowerBase);
656 loadPower += std::complex<double>(childData.activePower, childData.reactivePower);
657
658 if (childData.activePower >= 0.0)
660 else
662 }
663 }
664
665 // Setup the reactive machines vector for the distribution of reactive power
666 std::vector<ReactiveMachine> machines;
667 for (auto* gen : syncGeneratorsOnBus) {
668
669 auto data = gen->GetPUElectricalData(systemPowerBase);
670
672 m.qMax = data.maxReactive;
673 m.qMin = data.minReactive;
674
675 m.hasMax = data.haveMaxReactive;
676 m.hasMin = data.haveMinReactive;
677
678 m.machine = gen;
679 m.isGenerator = true;
680
681 machines.push_back(m);
682 }
683 for (auto* mot : syncMotorsOnBus) {
684
685 auto data = mot->GetPUElectricalData(systemPowerBase);
686
688 m.qMax = data.maxReactive;
689 m.qMin = data.minReactive;
690
691 m.hasMax = data.haveMaxReactive;
692 m.hasMin = data.haveMinReactive;
693
694 m.machine = mot;
695 m.isGenerator = false;
696
697 machines.push_back(m);
698 }
699
700 // Distribute the reactive power among the synchronous machines and set their reactive power
701 double qTotal = power[i].imag() + loadPower.imag();
702 DistributeReactivePower(machines, qTotal);
703
704 // Set the reactive power on the machines
705 for (auto& m : machines) {
706
707 if (m.isGenerator) {
708
709 SyncGenerator* gen = static_cast<SyncGenerator*>(m.machine);
710 auto data = gen->GetElectricalData();
711
712 double reactivePower = m.q * systemPowerBase;
713
714 switch (data.reactivePowerUnit) {
716 reactivePower /= systemPowerBase;
717 break;
719 reactivePower /= 1e3;
720 break;
722 reactivePower /= 1e6;
723 break;
724 default:
725 break;
726 }
727
728 data.reactivePower = reactivePower;
729 gen->SetElectricalData(data);
730 }
731 else {
732
733 SyncMotor* mot = static_cast<SyncMotor*>(m.machine);
734 auto data = mot->GetElectricalData();
735
736 double reactivePower = m.q * systemPowerBase;
737
738 switch (data.reactivePowerUnit) {
740 reactivePower /= systemPowerBase;
741 break;
743 reactivePower /= 1e3;
744 break;
746 reactivePower /= 1e6;
747 break;
748 default:
749 break;
750 }
751
752 data.reactivePower = reactivePower;
753 mot->SetElectricalData(data);
754 }
755 }
756 }
757 }
758}
759
760bool ElectricCalculation::InvertMatrix(std::vector<std::vector<std::complex<double> > > matrix,
761 std::vector<std::vector<std::complex<double> > >& inverse)
762{
763 int order = static_cast<int>(matrix.size());
764
765 inverse.clear();
766 // Fill the inverse matrix with identity.
767 for (int i = 0; i < order; ++i) {
768 std::vector<std::complex<double> > line;
769 for (int j = 0; j < order; ++j) {
770 line.push_back(i == j ? std::complex<double>(1.0, 0.0) : std::complex<double>(0.0, 0.0));
771 }
772 inverse.push_back(line);
773 }
774
775 // Check if a main diagonal value of the matrix is zero, if one is zero, try a linear combination to remove it.
776 for (int i = 0; i < order; ++i) {
777 for (int j = 0; j < order; ++j) {
778 if (i == j && matrix[i][j] == std::complex<double>(0.0, 0.0)) {
779 int row = 0;
780 while (row < order) {
781 if (matrix[row][j] != std::complex<double>(0.0, 0.0)) {
782 for (int k = 0; k < order; ++k) {
783 matrix[i][k] += matrix[row][k];
784 inverse[i][k] += inverse[row][k];
785 }
786 break;
787 }
788 row++;
789 }
790 // If all line values are zero, the matrix is singular and the solution is impossible.
791 if (row == order) return false;
792 }
793 }
794 }
795
796 // Linear combinations are made in both matrices, the goal is the input matrix become the identity. The final result
797 // have two matrices: the identity and the inverse of the input.
798 for (int i = 0; i < order; ++i) {
799 for (int j = 0; j < order; ++j) {
800 if (i != j) {
801 if (matrix[i][i] == std::complex<double>(0.0, 0.0)) return false;
802
803 std::complex<double> factor = matrix[j][i] / matrix[i][i];
804 for (int k = 0; k < order; ++k) {
805 matrix[j][k] -= factor * matrix[i][k];
806 inverse[j][k] -= factor * inverse[i][k];
807 }
808 }
809 }
810 }
811 // Main diagonal calculation.
812 for (int i = 0; i < order; ++i) {
813 for (int j = 0; j < order; ++j) {
814 if (i == j) {
815 if (matrix[i][j] == std::complex<double>(0.0, 0.0)) return false;
816
817 std::complex<double> factor = (matrix[i][j] - std::complex<double>(1.0, 0.0)) / matrix[i][j];
818 for (int k = 0; k < order; ++k) {
819 matrix[j][k] -= factor * matrix[i][k];
820 inverse[j][k] -= factor * inverse[i][k];
821 }
822 }
823 }
824 }
825
826 return true;
827}
828
829void ElectricCalculation::ABCtoDQ0(std::complex<double> complexValue, double angle, double& dValue, double& qValue)
830{
831 dValue = -std::real(complexValue) * std::sin(angle) + std::imag(complexValue) * std::cos(angle);
832 qValue = std::real(complexValue) * std::cos(angle) + std::imag(complexValue) * std::sin(angle);
833}
834
835void ElectricCalculation::DQ0toABC(double dValue, double qValue, double angle, std::complex<double>& complexValue)
836{
837 double real = qValue * std::cos(angle) - dValue * std::sin(angle);
838 double imag = qValue * std::sin(angle) + dValue * std::cos(angle);
839 complexValue = std::complex<double>(real, imag);
840}
841
842std::vector<std::complex<double> > ElectricCalculation::GaussianElimination(
843 std::vector<std::vector<std::complex<double> > > matrix,
844 std::vector<std::complex<double> > array)
845{
846 //[Ref] https://en.wikipedia.org/wiki/Gaussian_elimination
847
848 std::vector<std::complex<double> > solution;
849
850 std::vector<std::vector<std::complex<double> > > triangMatrix;
851 triangMatrix.resize(matrix.size());
852 for (unsigned int i = 0; i < matrix.size(); i++) { triangMatrix[i].resize(matrix.size()); }
853
854 for (unsigned int i = 0; i < matrix.size(); i++) { solution.push_back(array[i]); }
855
856 for (unsigned int i = 0; i < matrix.size(); i++) {
857 for (unsigned int j = 0; j < matrix.size(); j++) { triangMatrix[i][j] = matrix[i][j]; }
858 }
859
860 for (unsigned int k = 0; k < matrix.size(); k++) {
861 unsigned int k1 = k + 1;
862 for (unsigned int i = k; i < matrix.size(); i++) {
863 if (triangMatrix[i][k] != std::complex<double>(0.0, 0.0)) {
864 for (unsigned int j = k1; j < matrix.size(); j++) {
865 triangMatrix[i][j] = triangMatrix[i][j] / triangMatrix[i][k];
866 }
867 solution[i] = solution[i] / triangMatrix[i][k];
868 }
869 }
870 for (unsigned int i = k1; i < matrix.size(); i++) {
871 if (triangMatrix[i][k] != std::complex<double>(0.0, 0.0)) {
872 for (unsigned int j = k1; j < matrix.size(); j++) { triangMatrix[i][j] -= triangMatrix[k][j]; }
873 solution[i] -= solution[k];
874 }
875 }
876 }
877 for (int i = static_cast<int>(matrix.size()) - 2; i >= 0; i--) {
878 for (int j = static_cast<int>(matrix.size()) - 1; j >= i + 1; j--) {
879 solution[i] -= triangMatrix[i][j] * solution[j];
880 }
881 }
882
883 return solution;
884}
885
886std::vector<double> ElectricCalculation::GaussianElimination(std::vector<std::vector<double> > matrix,
887 std::vector<double> array)
888{
889 //[Ref] https://en.wikipedia.org/wiki/Gaussian_elimination
890
891 std::vector<double> solution;
892
893 std::vector<std::vector<double> > triangMatrix;
894 triangMatrix.resize(matrix.size());
895 for (unsigned int i = 0; i < matrix.size(); i++) { triangMatrix[i].resize(matrix.size()); }
896
897 for (unsigned int i = 0; i < matrix.size(); i++) { solution.push_back(array[i]); }
898
899 for (unsigned int i = 0; i < matrix.size(); i++) {
900 for (unsigned int j = 0; j < matrix.size(); j++) { triangMatrix[i][j] = matrix[i][j]; }
901 }
902
903 for (unsigned int k = 0; k < matrix.size(); k++) {
904 unsigned int k1 = k + 1;
905 for (unsigned int i = k; i < matrix.size(); i++) {
906 if (triangMatrix[i][k] != 0.0) {
907 for (unsigned int j = k1; j < matrix.size(); j++) {
908 triangMatrix[i][j] = triangMatrix[i][j] / triangMatrix[i][k];
909 }
910 solution[i] = solution[i] / triangMatrix[i][k];
911 }
912 }
913 for (unsigned int i = k1; i < matrix.size(); i++) {
914 if (triangMatrix[i][k] != 0.0) {
915 for (unsigned int j = k1; j < matrix.size(); j++) { triangMatrix[i][j] -= triangMatrix[k][j]; }
916 solution[i] -= solution[k];
917 }
918 }
919 }
920 for (int i = static_cast<int>(matrix.size()) - 2; i >= 0; i--) {
921 for (int j = static_cast<int>(matrix.size()) - 1; j >= i + 1; j--) {
922 solution[i] -= triangMatrix[i][j] * solution[j];
923 }
924 }
925
926 return solution;
927}
928
929Machines::SyncMachineModel ElectricCalculation::GetMachineModel(SyncGenerator* generator)
930{
931 auto data = generator->GetElectricalData();
932 if (data.transTd0 != 0.0) {
933 if (data.transTq0 != 0.0) {
934 if (data.subTd0 != 0.0 || data.subTq0 != 0.0) { return Machines::SM_MODEL_5; }
935 return Machines::SM_MODEL_3;
936 }
937 else {
938 if (data.subTd0 != 0.0 || data.subTq0 != 0.0) { return Machines::SM_MODEL_4; }
939 return Machines::SM_MODEL_2;
940 }
941 }
942
943 return Machines::SM_MODEL_1;
944}
945
946std::vector<std::complex<double> > ElectricCalculation::ComplexMatrixTimesVector(
947 std::vector<std::vector<std::complex<double> > > matrix,
948 std::vector<std::complex<double> > vector)
949{
950 std::vector<std::complex<double> > solution;
951 for (unsigned int i = 0; i < matrix.size(); i++) {
952 solution.push_back(std::complex<double>(0.0, 0.0));
953
954 for (unsigned int j = 0; j < matrix.size(); j++) { solution[i] += matrix[i][j] * vector[j]; }
955 }
956
957 return solution;
958}
959
960void ElectricCalculation::GetLUDecomposition(std::vector<std::vector<std::complex<double> > > matrix,
961 std::vector<std::vector<std::complex<double> > >& matrixL,
962 std::vector<std::vector<std::complex<double> > >& matrixU)
963{
964 // Doolittle method
965 // [Ref] http://www3.nd.edu/~zxu2/acms40390F11/Alg-LU-Crout.pdf
966 // [Ref] http://www.engr.colostate.edu/~thompson/hPage/CourseMat/Tutorials/CompMethods/doolittle.pdf
967
968 int size = static_cast<int>(matrix.size()); // Decomposed matrix size.
969
970 // Set upper and lower matrices sizes.
971 matrixL.resize(size);
972 matrixU.resize(size);
973 for (int i = 0; i < size; i++) {
974 matrixL[i].resize(size);
975 matrixU[i].resize(size);
976 }
977
978 // First row of upper matrix and first column of lower matrix.
979 for (int i = 0; i < size; i++) {
980 matrixU[0][i] = matrix[0][i];
981 matrixL[i][0] = matrix[i][0] / matrixU[0][0];
982 }
983
984 // Lower matrix main diagonal.
985 for (int i = 1; i < size; i++) { matrixL[i][i] = std::complex<double>(1.0, 0.0); }
986
987 for (int i = 1; i < size - 1; i++) {
988 // Upper matrix main diagonal.
989 matrixU[i][i] = matrix[i][i];
990 for (int k = 0; k < i; k++) { matrixU[i][i] -= matrixL[i][k] * matrixU[k][i]; }
991
992 // Others elements of upper matrix
993 for (int j = i + 1; j < size; j++) {
994 matrixU[i][j] = matrix[i][j];
995 for (int k = 0; k < i; k++) { matrixU[i][j] -= matrixL[i][k] * matrixU[k][j]; }
996 }
997
998 // Lower matrix elements
999 for (int j = i + 1; j < size; j++) {
1000 matrixL[j][i] = matrix[j][i];
1001 for (int k = 0; k < i; k++) { matrixL[j][i] -= matrixL[j][k] * matrixU[k][i]; }
1002 matrixL[j][i] = matrixL[j][i] / matrixU[i][i];
1003 }
1004 }
1005
1006 // Last element of upper matrix.
1007 matrixU[size - 1][size - 1] = matrix[size - 1][size - 1];
1008 for (int k = 0; k < size - 1; k++) { matrixU[size - 1][size - 1] -= matrixL[size - 1][k] * matrixU[k][size - 1]; }
1009}
1010
1011std::vector<std::complex<double> > ElectricCalculation::LUEvaluate(std::vector<std::vector<std::complex<double> > > u,
1012 std::vector<std::vector<std::complex<double> > > l,
1013 std::vector<std::complex<double> > b)
1014{
1015 int size = static_cast<int>(b.size());
1016 std::vector<std::complex<double> > x;
1017 std::vector<std::complex<double> > y;
1018 x.resize(size);
1019 y.resize(size);
1020
1021 // Forward
1022 for (int i = 0; i < size; i++) {
1023 y[i] = b[i];
1024 for (int j = 0; j < i; j++) { y[i] -= l[i][j] * y[j]; }
1025 y[i] /= l[i][i];
1026 }
1027 // Backward
1028 for (int i = size - 1; i >= 0; i--) {
1029 x[i] = y[i];
1030 for (int j = i + 1; j < size; j++) { x[i] -= u[i][j] * x[j]; }
1031 x[i] /= u[i][i];
1032 }
1033 return x;
1034}
1035
1036bool ElectricCalculation::GetParentBus(Element* childElement, Bus*& parentBus)
1037{
1038 auto parentList = childElement->GetParentList();
1039 if (parentList.size() < 1) return false;
1040 parentBus = static_cast<Bus*>(parentList[0]);
1041 if (parentBus == nullptr) return false;
1042 if (parentBus->GetElectricalData().number < 0) return false;
1043 return true;
1044}
1045
1046bool ElectricCalculation::GetParentBus(Element* childElement, Bus*& parentBus1, Bus*& parentBus2)
1047{
1048 auto parentList = childElement->GetParentList();
1049 if (parentList.size() < 2) return false;
1050 parentBus1 = static_cast<Bus*>(parentList[0]);
1051 parentBus2 = static_cast<Bus*>(parentList[1]);
1052 if (parentBus1 == nullptr || parentBus2 == nullptr) return false;
1053 if (parentBus1->GetElectricalData().number < 0 || parentBus2->GetElectricalData().number < 0) return false;
1054 return true;
1055}
1056
1057bool ElectricCalculation::CalculateEMTElementsAdmittance(const double& basePower, wxString& errorMsg)
1058{
1059 for (EMTElement* emtElement : m_emtElementList) {
1060 if (!emtElement->IsOnline()) continue;
1061
1062 emtElement->UpdateData();
1063 if (!emtElement->CalculateCurrent(errorMsg)) return false;
1064 auto data = emtElement->GetEMTElementData();
1065 double baseCurrent = basePower / (sqrt(3.0) * data.baseVoltage);
1066 std::complex<double> current = data.currHarmonics[1] / baseCurrent; // Fundamental frequency current
1067 std::complex<double> y0 = current / data.puVoltage;
1068 data.y0 = y0;
1069 data.power = std::complex<double>(0.0, 0.0);
1070 emtElement->SetEMTElementData(data);
1071 }
1072 return true;
1073}
1074
1075bool ElectricCalculation::CalculateEMTElementsPower(const double& basePower, wxString& errorMsg, bool updateCurrent)
1076{
1077 for (EMTElement* emtElement : m_emtElementList) {
1078 if (!emtElement->IsOnline()) continue;
1079
1080 emtElement->UpdateData();
1081 if (updateCurrent) {
1082 if (!emtElement->CalculateCurrent(errorMsg)) return false;
1083 }
1084 auto data = emtElement->GetEMTElementData();
1085 double baseCurrent = basePower / (sqrt(3.0) * data.baseVoltage);
1086 //std::complex<double> current = data.currHarmonics[1];
1087 std::complex<double> current = data.currHarmonics[1] / baseCurrent; // Fundamental frequency current
1088
1089 std::complex<double> i0 = data.y0 * data.puVoltage; // Current already injected via admittance matrix
1090 //std::complex<double> voltage = data.baseVoltage * data.puVoltage;
1091 //std::complex<double> power = (sqrt(3.0) * voltage * std::conj(current)) / basePower;
1092 std::complex<double> power = data.puVoltage * std::conj(current - i0);
1093 data.powerDiff = power - data.power;
1094 data.power = power;
1095 emtElement->SetEMTElementData(data);
1096
1097 //wxString currentStr = data.name + ":\n";
1098 //for (auto const& [harm, current] : data.currHarmonics)
1099 //{
1100 // currentStr += wxString::Format("I(%dh) = %f p.u.\n", harm, abs(current) / baseCurrent);
1101 //}
1102 //currentStr += wxString::Format("S = %e + j%e p.u.\n", data.power.real(), data.power.imag());
1103 //currentStr += wxString::Format("SDiff = %e p.u.\n", abs(data.powerDiff));
1104 //wxMessageBox(currentStr);
1105 }
1106 return true;
1107}
1108
1109double ElectricCalculation::CalculateEMTPowerError(const std::vector<std::complex<double>>& voltage, std::vector<std::complex<double>>& power, const double& basePower, wxString& errorMsg)
1110{
1111 // Update buses voltages
1112 for (Bus* bus : m_busList) {
1113 auto data = bus->GetElectricalData();
1114 if (data.isConnected) {
1115 data.voltage = voltage[data.number];
1116 bus->SetElectricalData(data);
1117 }
1118 }
1119
1120 CalculateEMTElementsPower(basePower, errorMsg);
1121 // Update power array and calculate error
1122 double error = 0.0;
1123 for (EMTElement* emtElement : m_emtElementList) {
1124 if (!emtElement->IsOnline()) continue;
1125
1126 if (!emtElement->GetParentList().empty()) {
1127 if (emtElement->GetParentList()[0] != nullptr) {
1128 int numBus = static_cast<Bus*>(emtElement->GetParentList()[0])->GetElectricalData().number;
1129 auto data = emtElement->GetEMTElementData();
1130 power[numBus] += data.powerDiff;
1131 if (error < std::abs(data.powerDiff))
1132 error = std::abs(data.powerDiff);
1133 }
1134 }
1135 }
1136
1137 return error;
1138}
YBusSequence
Sequence type used when building the system admittance matrix.
@ POSITIVE_SEQ
@ NEGATIVE_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
Element to connect ATP-EMTP.
Definition EMTElement.h:65
void DistributeReactivePower(std::vector< ReactiveMachine > &machines, double qTotal)
Distribute reactive power among synchronous machines connected to the same bus.
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.
virtual void UpdateElementsPowerFlow(std::vector< std::complex< double > > voltage, std::vector< std::complex< double > > power, std::vector< BusType > busType, std::vector< ReactiveLimits > reactiveLimit, double systemPowerBase)
Update the elements after the power flow calculation.
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.
bool CalculateEMTElementsAdmittance(const double &basePower, wxString &errorMsg)
Calculate the admittance of EMT elements.
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.
ElectricCalculation()
Constructor.
double CalculateEMTPowerError(const std::vector< std::complex< double > > &voltage, std::vector< std::complex< double > > &power, const double &basePower, wxString &errorMsg)
Calculate the power mismatch error for EMT simulation.
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.
bool CalculateEMTElementsPower(const double &basePower, wxString &errorMsg, bool updateCurrent=true)
Calculate the power of EMT elements.
std::vector< SyncGenerator * > m_syncGeneratorList
List of synchronous generators in the system.
std::vector< EMTElement * > m_emtElementList
List of electromagnetic transient (EMT) elements in the system.
std::vector< std::complex< double > > GaussianElimination(std::vector< std::vector< std::complex< double > > > matrix, std::vector< std::complex< double > > array)
Solve a linear system using Gaussian elimination (complex version).
void GetNextConnection(const unsigned int &checkBusNumber, const std::vector< std::vector< std::complex< double > > > &yBus, std::vector< bool > &connToSlack)
Recursively check if a bus is electrically connected to the slack bus.
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).
std::vector< SyncMotor * > m_syncMotorList
List of synchronous motors in the system.
std::vector< HarmCurrent * > m_harmCurrentList
List of harmonic current sources in the system.
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 bool InvertMatrix(std::vector< std::vector< std::complex< double > > > matrix, std::vector< std::vector< std::complex< double > > > &inverse)
Invert a matrix.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:112
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
Shunt Harmonic Corrent Source.
Definition HarmCurrent.h:24
Induction motor power element.
Definition IndMotor.h:119
Inductor shunt power element.
Definition Inductor.h:39
Power line element.
Definition Line.h:64
virtual void SetPowerFlowDirection(PowerFlowDirection pfDirection)
Set the direction of the power flow.
Definition Line.cpp:621
Loas shunt power element.
Definition Load.h:74
virtual void SetPowerFlowDirection(PowerFlowDirection pfDirection)
Set the direction of the power flow.
Definition Machines.cpp:379
Abstract class of power elements.
virtual void SetPowerFlowDirection(PowerFlowDirection pfDirection)
Set the direction of the power flow.
Synchronous generator power element.
Synchronous motor (synchronous compensator) power element.
Definition SyncMotor.h:135
Two-winding transformer power element.
Definition Transformer.h:84
virtual void SetPowerFlowDirection(PowerFlowDirection pfDirection)
Set the direction of the power flow.
Auxiliary structure used to distribute reactive power among machines connected to a bus.