Power System Platform  2026w11a-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 if (data.slackBus) {
670 auto dataG = gen->GetElectricalData();
671 double activePower = (power[data.number].real() + loadPower.real()) * (systemPowerBase / static_cast<double>(syncGeneratorsOnBus.size()));
672 switch (dataG.activePowerUnit) {
674 activePower /= systemPowerBase;
675 break;
677 activePower /= 1e3;
678 break;
680 activePower /= 1e6;
681 break;
682 default:
683 break;
684 }
685
686 dataG.activePower = activePower;
687 gen->SetElectricalData(dataG);
688 }
689
690 auto dataG = gen->GetPUElectricalData(systemPowerBase);
691
693 m.qMax = dataG.maxReactive;
694 m.qMin = dataG.minReactive;
695
696 m.hasMax = dataG.haveMaxReactive;
697 m.hasMin = dataG.haveMinReactive;
698
699 m.machine = gen;
700 m.isGenerator = true;
701
702 machines.push_back(m);
703 }
704 for (auto* mot : syncMotorsOnBus) {
705
706 auto data = mot->GetPUElectricalData(systemPowerBase);
707
709 m.qMax = data.maxReactive;
710 m.qMin = data.minReactive;
711
712 m.hasMax = data.haveMaxReactive;
713 m.hasMin = data.haveMinReactive;
714
715 m.machine = mot;
716 m.isGenerator = false;
717
718 machines.push_back(m);
719 }
720
721 // Distribute the reactive power among the synchronous machines and set their reactive power
722 double qTotal = power[i].imag() + loadPower.imag();
723 DistributeReactivePower(machines, qTotal);
724
725 // Set the reactive power on the machines
726 for (auto& m : machines) {
727
728 if (m.isGenerator) {
729
730 SyncGenerator* gen = static_cast<SyncGenerator*>(m.machine);
731 auto data = gen->GetElectricalData();
732
733 double reactivePower = m.q * systemPowerBase;
734
735 switch (data.reactivePowerUnit) {
737 reactivePower /= systemPowerBase;
738 break;
740 reactivePower /= 1e3;
741 break;
743 reactivePower /= 1e6;
744 break;
745 default:
746 break;
747 }
748
749 data.reactivePower = reactivePower;
750 gen->SetElectricalData(data);
751 }
752 else {
753
754 SyncMotor* mot = static_cast<SyncMotor*>(m.machine);
755 auto data = mot->GetElectricalData();
756
757 double reactivePower = m.q * systemPowerBase;
758
759 switch (data.reactivePowerUnit) {
761 reactivePower /= systemPowerBase;
762 break;
764 reactivePower /= 1e3;
765 break;
767 reactivePower /= 1e6;
768 break;
769 default:
770 break;
771 }
772
773 data.reactivePower = reactivePower;
774 mot->SetElectricalData(data);
775 }
776 }
777 }
778 }
779}
780
781bool ElectricCalculation::InvertMatrix(std::vector<std::vector<std::complex<double> > > matrix,
782 std::vector<std::vector<std::complex<double> > >& inverse)
783{
784 int order = static_cast<int>(matrix.size());
785
786 inverse.clear();
787 // Fill the inverse matrix with identity.
788 for (int i = 0; i < order; ++i) {
789 std::vector<std::complex<double> > line;
790 for (int j = 0; j < order; ++j) {
791 line.push_back(i == j ? std::complex<double>(1.0, 0.0) : std::complex<double>(0.0, 0.0));
792 }
793 inverse.push_back(line);
794 }
795
796 // Check if a main diagonal value of the matrix is zero, if one is zero, try a linear combination to remove it.
797 for (int i = 0; i < order; ++i) {
798 for (int j = 0; j < order; ++j) {
799 if (i == j && matrix[i][j] == std::complex<double>(0.0, 0.0)) {
800 int row = 0;
801 while (row < order) {
802 if (matrix[row][j] != std::complex<double>(0.0, 0.0)) {
803 for (int k = 0; k < order; ++k) {
804 matrix[i][k] += matrix[row][k];
805 inverse[i][k] += inverse[row][k];
806 }
807 break;
808 }
809 row++;
810 }
811 // If all line values are zero, the matrix is singular and the solution is impossible.
812 if (row == order) return false;
813 }
814 }
815 }
816
817 // Linear combinations are made in both matrices, the goal is the input matrix become the identity. The final result
818 // have two matrices: the identity and the inverse of the input.
819 for (int i = 0; i < order; ++i) {
820 for (int j = 0; j < order; ++j) {
821 if (i != j) {
822 if (matrix[i][i] == std::complex<double>(0.0, 0.0)) return false;
823
824 std::complex<double> factor = matrix[j][i] / matrix[i][i];
825 for (int k = 0; k < order; ++k) {
826 matrix[j][k] -= factor * matrix[i][k];
827 inverse[j][k] -= factor * inverse[i][k];
828 }
829 }
830 }
831 }
832 // Main diagonal calculation.
833 for (int i = 0; i < order; ++i) {
834 for (int j = 0; j < order; ++j) {
835 if (i == j) {
836 if (matrix[i][j] == std::complex<double>(0.0, 0.0)) return false;
837
838 std::complex<double> factor = (matrix[i][j] - std::complex<double>(1.0, 0.0)) / matrix[i][j];
839 for (int k = 0; k < order; ++k) {
840 matrix[j][k] -= factor * matrix[i][k];
841 inverse[j][k] -= factor * inverse[i][k];
842 }
843 }
844 }
845 }
846
847 return true;
848}
849
850void ElectricCalculation::ABCtoDQ0(std::complex<double> complexValue, double angle, double& dValue, double& qValue)
851{
852 dValue = -std::real(complexValue) * std::sin(angle) + std::imag(complexValue) * std::cos(angle);
853 qValue = std::real(complexValue) * std::cos(angle) + std::imag(complexValue) * std::sin(angle);
854}
855
856void ElectricCalculation::DQ0toABC(double dValue, double qValue, double angle, std::complex<double>& complexValue)
857{
858 double real = qValue * std::cos(angle) - dValue * std::sin(angle);
859 double imag = qValue * std::sin(angle) + dValue * std::cos(angle);
860 complexValue = std::complex<double>(real, imag);
861}
862
863std::vector<std::complex<double> > ElectricCalculation::GaussianElimination(
864 std::vector<std::vector<std::complex<double> > > matrix,
865 std::vector<std::complex<double> > array)
866{
867 //[Ref] https://en.wikipedia.org/wiki/Gaussian_elimination
868
869 std::vector<std::complex<double> > solution;
870
871 std::vector<std::vector<std::complex<double> > > triangMatrix;
872 triangMatrix.resize(matrix.size());
873 for (unsigned int i = 0; i < matrix.size(); i++) { triangMatrix[i].resize(matrix.size()); }
874
875 for (unsigned int i = 0; i < matrix.size(); i++) { solution.push_back(array[i]); }
876
877 for (unsigned int i = 0; i < matrix.size(); i++) {
878 for (unsigned int j = 0; j < matrix.size(); j++) { triangMatrix[i][j] = matrix[i][j]; }
879 }
880
881 for (unsigned int k = 0; k < matrix.size(); k++) {
882 unsigned int k1 = k + 1;
883 for (unsigned int i = k; i < matrix.size(); i++) {
884 if (triangMatrix[i][k] != std::complex<double>(0.0, 0.0)) {
885 for (unsigned int j = k1; j < matrix.size(); j++) {
886 triangMatrix[i][j] = triangMatrix[i][j] / triangMatrix[i][k];
887 }
888 solution[i] = solution[i] / triangMatrix[i][k];
889 }
890 }
891 for (unsigned int i = k1; i < matrix.size(); i++) {
892 if (triangMatrix[i][k] != std::complex<double>(0.0, 0.0)) {
893 for (unsigned int j = k1; j < matrix.size(); j++) { triangMatrix[i][j] -= triangMatrix[k][j]; }
894 solution[i] -= solution[k];
895 }
896 }
897 }
898 for (int i = static_cast<int>(matrix.size()) - 2; i >= 0; i--) {
899 for (int j = static_cast<int>(matrix.size()) - 1; j >= i + 1; j--) {
900 solution[i] -= triangMatrix[i][j] * solution[j];
901 }
902 }
903
904 return solution;
905}
906
907std::vector<double> ElectricCalculation::GaussianElimination(std::vector<std::vector<double> > matrix,
908 std::vector<double> array)
909{
910 //[Ref] https://en.wikipedia.org/wiki/Gaussian_elimination
911
912 std::vector<double> solution;
913
914 std::vector<std::vector<double> > triangMatrix;
915 triangMatrix.resize(matrix.size());
916 for (unsigned int i = 0; i < matrix.size(); i++) { triangMatrix[i].resize(matrix.size()); }
917
918 for (unsigned int i = 0; i < matrix.size(); i++) { solution.push_back(array[i]); }
919
920 for (unsigned int i = 0; i < matrix.size(); i++) {
921 for (unsigned int j = 0; j < matrix.size(); j++) { triangMatrix[i][j] = matrix[i][j]; }
922 }
923
924 for (unsigned int k = 0; k < matrix.size(); k++) {
925 unsigned int k1 = k + 1;
926 for (unsigned int i = k; i < matrix.size(); i++) {
927 if (triangMatrix[i][k] != 0.0) {
928 for (unsigned int j = k1; j < matrix.size(); j++) {
929 triangMatrix[i][j] = triangMatrix[i][j] / triangMatrix[i][k];
930 }
931 solution[i] = solution[i] / triangMatrix[i][k];
932 }
933 }
934 for (unsigned int i = k1; i < matrix.size(); i++) {
935 if (triangMatrix[i][k] != 0.0) {
936 for (unsigned int j = k1; j < matrix.size(); j++) { triangMatrix[i][j] -= triangMatrix[k][j]; }
937 solution[i] -= solution[k];
938 }
939 }
940 }
941 for (int i = static_cast<int>(matrix.size()) - 2; i >= 0; i--) {
942 for (int j = static_cast<int>(matrix.size()) - 1; j >= i + 1; j--) {
943 solution[i] -= triangMatrix[i][j] * solution[j];
944 }
945 }
946
947 return solution;
948}
949
950Machines::SyncMachineModel ElectricCalculation::GetMachineModel(SyncGenerator* generator)
951{
952 auto data = generator->GetElectricalData();
953 if (data.transTd0 != 0.0) {
954 if (data.transTq0 != 0.0) {
955 if (data.subTd0 != 0.0 || data.subTq0 != 0.0) { return Machines::SM_MODEL_5; }
956 return Machines::SM_MODEL_3;
957 }
958 else {
959 if (data.subTd0 != 0.0 || data.subTq0 != 0.0) { return Machines::SM_MODEL_4; }
960 return Machines::SM_MODEL_2;
961 }
962 }
963
964 return Machines::SM_MODEL_1;
965}
966
967std::vector<std::complex<double> > ElectricCalculation::ComplexMatrixTimesVector(
968 std::vector<std::vector<std::complex<double> > > matrix,
969 std::vector<std::complex<double> > vector)
970{
971 std::vector<std::complex<double> > solution;
972 for (unsigned int i = 0; i < matrix.size(); i++) {
973 solution.push_back(std::complex<double>(0.0, 0.0));
974
975 for (unsigned int j = 0; j < matrix.size(); j++) { solution[i] += matrix[i][j] * vector[j]; }
976 }
977
978 return solution;
979}
980
981void ElectricCalculation::GetLUDecomposition(std::vector<std::vector<std::complex<double> > > matrix,
982 std::vector<std::vector<std::complex<double> > >& matrixL,
983 std::vector<std::vector<std::complex<double> > >& matrixU)
984{
985 // Doolittle method
986 // [Ref] http://www3.nd.edu/~zxu2/acms40390F11/Alg-LU-Crout.pdf
987 // [Ref] http://www.engr.colostate.edu/~thompson/hPage/CourseMat/Tutorials/CompMethods/doolittle.pdf
988
989 int size = static_cast<int>(matrix.size()); // Decomposed matrix size.
990
991 // Set upper and lower matrices sizes.
992 matrixL.resize(size);
993 matrixU.resize(size);
994 for (int i = 0; i < size; i++) {
995 matrixL[i].resize(size);
996 matrixU[i].resize(size);
997 }
998
999 // First row of upper matrix and first column of lower matrix.
1000 for (int i = 0; i < size; i++) {
1001 matrixU[0][i] = matrix[0][i];
1002 matrixL[i][0] = matrix[i][0] / matrixU[0][0];
1003 }
1004
1005 // Lower matrix main diagonal.
1006 for (int i = 1; i < size; i++) { matrixL[i][i] = std::complex<double>(1.0, 0.0); }
1007
1008 for (int i = 1; i < size - 1; i++) {
1009 // Upper matrix main diagonal.
1010 matrixU[i][i] = matrix[i][i];
1011 for (int k = 0; k < i; k++) { matrixU[i][i] -= matrixL[i][k] * matrixU[k][i]; }
1012
1013 // Others elements of upper matrix
1014 for (int j = i + 1; j < size; j++) {
1015 matrixU[i][j] = matrix[i][j];
1016 for (int k = 0; k < i; k++) { matrixU[i][j] -= matrixL[i][k] * matrixU[k][j]; }
1017 }
1018
1019 // Lower matrix elements
1020 for (int j = i + 1; j < size; j++) {
1021 matrixL[j][i] = matrix[j][i];
1022 for (int k = 0; k < i; k++) { matrixL[j][i] -= matrixL[j][k] * matrixU[k][i]; }
1023 matrixL[j][i] = matrixL[j][i] / matrixU[i][i];
1024 }
1025 }
1026
1027 // Last element of upper matrix.
1028 matrixU[size - 1][size - 1] = matrix[size - 1][size - 1];
1029 for (int k = 0; k < size - 1; k++) { matrixU[size - 1][size - 1] -= matrixL[size - 1][k] * matrixU[k][size - 1]; }
1030}
1031
1032std::vector<std::complex<double> > ElectricCalculation::LUEvaluate(std::vector<std::vector<std::complex<double> > > u,
1033 std::vector<std::vector<std::complex<double> > > l,
1034 std::vector<std::complex<double> > b)
1035{
1036 int size = static_cast<int>(b.size());
1037 std::vector<std::complex<double> > x;
1038 std::vector<std::complex<double> > y;
1039 x.resize(size);
1040 y.resize(size);
1041
1042 // Forward
1043 for (int i = 0; i < size; i++) {
1044 y[i] = b[i];
1045 for (int j = 0; j < i; j++) { y[i] -= l[i][j] * y[j]; }
1046 y[i] /= l[i][i];
1047 }
1048 // Backward
1049 for (int i = size - 1; i >= 0; i--) {
1050 x[i] = y[i];
1051 for (int j = i + 1; j < size; j++) { x[i] -= u[i][j] * x[j]; }
1052 x[i] /= u[i][i];
1053 }
1054 return x;
1055}
1056
1057bool ElectricCalculation::GetParentBus(Element* childElement, Bus*& parentBus)
1058{
1059 auto parentList = childElement->GetParentList();
1060 if (parentList.size() < 1) return false;
1061 parentBus = static_cast<Bus*>(parentList[0]);
1062 if (parentBus == nullptr) return false;
1063 if (parentBus->GetElectricalData().number < 0) return false;
1064 return true;
1065}
1066
1067bool ElectricCalculation::GetParentBus(Element* childElement, Bus*& parentBus1, Bus*& parentBus2)
1068{
1069 auto parentList = childElement->GetParentList();
1070 if (parentList.size() < 2) return false;
1071 parentBus1 = static_cast<Bus*>(parentList[0]);
1072 parentBus2 = static_cast<Bus*>(parentList[1]);
1073 if (parentBus1 == nullptr || parentBus2 == nullptr) return false;
1074 if (parentBus1->GetElectricalData().number < 0 || parentBus2->GetElectricalData().number < 0) return false;
1075 return true;
1076}
1077
1078bool ElectricCalculation::CalculateEMTElementsAdmittance(const double& basePower, wxString& errorMsg)
1079{
1080 for (EMTElement* emtElement : m_emtElementList) {
1081 if (!emtElement->IsOnline()) continue;
1082
1083 emtElement->UpdateData();
1084 if (!emtElement->CalculateCurrent(errorMsg)) return false;
1085 auto data = emtElement->GetEMTElementData();
1086 double baseCurrent = basePower / (sqrt(3.0) * data.baseVoltage);
1087 std::complex<double> current = data.currHarmonics[1] / baseCurrent; // Fundamental frequency current
1088 std::complex<double> y0 = current / data.puVoltage;
1089 data.y0 = y0;
1090 data.power = std::complex<double>(0.0, 0.0);
1091 emtElement->SetEMTElementData(data);
1092 }
1093 return true;
1094}
1095
1096bool ElectricCalculation::CalculateEMTElementsPower(const double& basePower, wxString& errorMsg, bool updateCurrent)
1097{
1098 for (EMTElement* emtElement : m_emtElementList) {
1099 if (!emtElement->IsOnline()) continue;
1100
1101 emtElement->UpdateData();
1102 if (updateCurrent) {
1103 if (!emtElement->CalculateCurrent(errorMsg)) return false;
1104 }
1105 auto data = emtElement->GetEMTElementData();
1106 double baseCurrent = basePower / (sqrt(3.0) * data.baseVoltage);
1107 //std::complex<double> current = data.currHarmonics[1];
1108 std::complex<double> current = data.currHarmonics[1] / baseCurrent; // Fundamental frequency current
1109
1110 std::complex<double> i0 = data.y0 * data.puVoltage; // Current already injected via admittance matrix
1111 //std::complex<double> voltage = data.baseVoltage * data.puVoltage;
1112 //std::complex<double> power = (sqrt(3.0) * voltage * std::conj(current)) / basePower;
1113 std::complex<double> power = data.puVoltage * std::conj(current - i0);
1114 data.powerDiff = power - data.power;
1115 data.power = power;
1116 emtElement->SetEMTElementData(data);
1117
1118 //wxString currentStr = data.name + ":\n";
1119 //for (auto const& [harm, current] : data.currHarmonics)
1120 //{
1121 // currentStr += wxString::Format("I(%dh) = %f p.u.\n", harm, abs(current) / baseCurrent);
1122 //}
1123 //currentStr += wxString::Format("S = %e + j%e p.u.\n", data.power.real(), data.power.imag());
1124 //currentStr += wxString::Format("SDiff = %e p.u.\n", abs(data.powerDiff));
1125 //wxMessageBox(currentStr);
1126 }
1127 return true;
1128}
1129
1130double ElectricCalculation::CalculateEMTPowerError(const std::vector<std::complex<double>>& voltage, std::vector<std::complex<double>>& power, const double& basePower, wxString& errorMsg)
1131{
1132 // Update buses voltages
1133 for (Bus* bus : m_busList) {
1134 auto data = bus->GetElectricalData();
1135 if (data.isConnected) {
1136 data.voltage = voltage[data.number];
1137 bus->SetElectricalData(data);
1138 }
1139 }
1140
1141 CalculateEMTElementsPower(basePower, errorMsg);
1142 // Update power array and calculate error
1143 double error = 0.0;
1144 for (EMTElement* emtElement : m_emtElementList) {
1145 if (!emtElement->IsOnline()) continue;
1146
1147 if (!emtElement->GetParentList().empty()) {
1148 if (emtElement->GetParentList()[0] != nullptr) {
1149 int numBus = static_cast<Bus*>(emtElement->GetParentList()[0])->GetElectricalData().number;
1150 auto data = emtElement->GetEMTElementData();
1151 power[numBus] += data.powerDiff;
1152 if (error < std::abs(data.powerDiff))
1153 error = std::abs(data.powerDiff);
1154 }
1155 }
1156 }
1157
1158 return error;
1159}
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:619
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.