3PowerQuality::PowerQuality() {}
5PowerQuality::~PowerQuality() {}
7PowerQuality::PowerQuality(std::vector<Element*> elementList) {
GetElementsFromList(elementList); }
9void PowerQuality::CalculateHarmonicYbusList(
double systemPowerBase, HarmLoadConnection loadConnection)
12 for (
auto it = m_harmYbusList.begin(), itEnd = m_harmYbusList.end(); it != itEnd; ++it) {
13 HarmonicYbus harmYBus = *it;
14 harmYBus.yBus.clear();
15 for (
unsigned int i = 0; i <
m_busList.size(); i++) {
16 std::vector<std::complex<double> > line;
17 for (
unsigned int j = 0; j <
m_busList.size(); j++) { line.push_back(std::complex<double>(0.0, 0.0)); }
18 harmYBus.yBus.push_back(line);
24 for (
auto itYbus = m_harmYbusList.begin(), itYbusEnd = m_harmYbusList.end(); itYbus != itYbusEnd; ++itYbus) {
25 HarmonicYbus harmYBus = *itYbus;
26 CalculateHarmonicYbus(harmYBus.yBus, systemPowerBase, harmYBus.order,
false, loadConnection);
31void PowerQuality::CalculateHarmonicYbus(std::vector<std::vector<std::complex<double> > >& yBus,
32 double systemPowerBase,
34 bool ignoreTransformerConnection,
35 HarmLoadConnection loadConnection)
40 if (load->
IsOnline() && loadConnection != HarmLoadConnection::DISCONNECTED) {
41 auto busData =
static_cast<Bus*
>(load->
GetParentList()[0])->GetElectricalData();
42 if (busData.isConnected) {
43 int n = busData.number;
46 std::complex<double> v =
static_cast<Bus*
>(load->
GetParentList()[0])->GetElectricalData().voltage;
50 std::complex<double> zLoad = (std::abs(v) * std::abs(v)) / std::complex<double>(data.activePower, -data.reactivePower);
51 zLoad = std::complex<double>(zLoad.real(), zLoad.imag() * order);
52 if (std::abs(zLoad.real()) < 1e-6) zLoad = std::complex<double>(1e-6, zLoad.imag());
53 if (std::abs(zLoad.imag()) < 1e-6) zLoad = std::complex<double>(zLoad.real(), 1e-6);
54 std::complex<double> yLoad = 0.0;
61 if (loadConnection == HarmLoadConnection::PARALLEL)
62 yLoad = std::complex<double>(1.0, 0.0) / std::complex<double>(zLoad.real(), 0.0) + std::complex<double>(1.0, 0.0) / std::complex<double>(0.0, zLoad.imag());
63 else if (loadConnection == HarmLoadConnection::SERIES)
64 yLoad = std::complex<double>(1.0, 0.0) / zLoad;
79 auto busData =
static_cast<Bus*
>(capacitor->
GetParentList()[0])->GetElectricalData();
80 if (busData.isConnected) {
81 int n = busData.number;
83 yBus[n][n] += std::complex<double>(0.0, data.reactivePower) * order;
92 auto busData =
static_cast<Bus*
>(inductor->
GetParentList()[0])->GetElectricalData();
93 if (busData.isConnected) {
94 int n = busData.number;
96 yBus[n][n] += std::complex<double>(0.0, -data.reactivePower) / order;
107 int n1 =
static_cast<Bus*
>(line->
GetParentList()[0])->GetElectricalData().number;
108 int n2 =
static_cast<Bus*
>(line->
GetParentList()[1])->GetElectricalData().number;
110 yBus[n1][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance * order);
111 yBus[n2][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance * order);
113 yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance * order);
114 yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance * order);
116 yBus[n1][n1] += std::complex<double>(0.0, (data.capSusceptance * order) / 2.0);
117 yBus[n2][n2] += std::complex<double>(0.0, (data.capSusceptance * order) / 2.0);
128 int n1 =
static_cast<Bus*
>(transformer->
GetParentList()[0])->GetElectricalData().number;
129 int n2 =
static_cast<Bus*
>(transformer->
GetParentList()[1])->GetElectricalData().number;
131 auto yMatrix = GetTransformerHarmAdmmitance(transformer, systemPowerBase, order, ignoreTransformerConnection);
133 yBus[n1][n2] += yMatrix[0][1];
134 yBus[n2][n1] += yMatrix[1][0];
135 yBus[n1][n1] += yMatrix[0][0];
136 yBus[n2][n2] += yMatrix[1][1];
144 int n =
static_cast<Bus*
>(syncGenerator->
GetParentList()[0])->GetElectricalData().number;
146 yBus[n][n] += 1.0 / std::complex<double>(data.positiveResistance, data.positiveReactance * order);
153 int n =
static_cast<Bus*
>(syncMotor->
GetParentList()[0])->GetElectricalData().number;
155 yBus[n][n] += 1.0 / std::complex<double>(data.positiveResistance, data.positiveReactance * order);
160bool PowerQuality::CalculateDistortions(
double systemPowerBase, HarmLoadConnection loadConnection)
163 m_harmYbusList.clear();
164 std::vector<double> harmOrders = GetHarmonicOrdersList();
167 for (
unsigned int i = 0; i < harmOrders.size(); ++i) {
168 HarmonicYbus newYbus;
169 newYbus.order = harmOrders[i];
170 m_harmYbusList.push_back(newYbus);
172 CalculateHarmonicYbusList(systemPowerBase, loadConnection);
175 std::vector<std::vector<std::complex<double> > > iHarmInjList;
176 for (
unsigned int i = 0; i < harmOrders.size(); i++) {
177 std::vector<std::complex<double> > line;
178 for (
unsigned int j = 0; j <
m_busList.size(); j++) { line.push_back(std::complex<double>(0.0, 0.0)); }
179 iHarmInjList.push_back(line);
183 for (
unsigned int i = 0; i < harmOrders.size(); ++i) {
188 for (
unsigned int k = 0; k < harmCurrent->GetElectricalData().harmonicOrder.size(); ++k) {
189 if (harmCurrent->GetElectricalData().harmonicOrder[k] ==
static_cast<int>(harmOrders[i])) {
191 auto busData = parentBus->GetElectricalData();
192 int j = busData.number;
195 double voltage = busData.nominalVoltage;
198 auto puData = harmCurrent->GetPUElectricalData(systemPowerBase, voltage);
200 iHarmInjList[i][j] += std::complex<double>(
201 puData.injHarmCurrent[k] * std::cos(wxDegToRad(puData.injHarmAngle[k])),
202 puData.injHarmCurrent[k] * std::sin(wxDegToRad(puData.injHarmAngle[k])));
209 if (!emtElement->IsOnline())
continue;
210 if (emtElement->GetParentList().size() > 0) {
211 if (emtElement->GetParentList()[0] !=
nullptr) {
212 auto data = emtElement->GetEMTElementData();
213 for (
auto const& [order, current] : data.currHarmonics) {
214 if (order == 1)
continue;
215 else if (order ==
static_cast<int>(harmOrders[i])) {
216 Bus* parentBus =
static_cast<Bus*
>(emtElement->GetParentList()[0]);
217 auto busData = parentBus->GetElectricalData();
218 int j = busData.number;
220 double voltage = busData.nominalVoltage;
222 double baseCurrent = systemPowerBase / (std::sqrt(3.0) * voltage);
224 iHarmInjList[i][j] += current / baseCurrent;
233 std::vector<std::vector<std::complex<double> > > vHarmList;
234 for (
unsigned int i = 0; i < m_harmYbusList.size(); ++i) {
240 auto data = bus->GetElectricalData();
241 data.harmonicOrder.clear();
242 data.harmonicVoltage.clear();
244 for (
unsigned int i = 0; i < vHarmList.size(); ++i) {
245 thd += std::abs(vHarmList[i][data.number]) * std::abs(vHarmList[i][data.number]);
246 data.harmonicVoltage.push_back(vHarmList[i][data.number]);
247 data.harmonicOrder.push_back(
static_cast<int>(harmOrders[i]));
250 thd = std::sqrt(thd) * 100.0;
252 bus->SetElectricalData(data);
258 if (line->IsOnline()) {
261 auto busData1 =
static_cast<Bus*
>(line->GetParentList()[0])->GetElectricalData();
262 auto busData2 =
static_cast<Bus*
>(line->GetParentList()[1])->GetElectricalData();
264 if (busData1.harmonicVoltage.size() != busData1.harmonicVoltage.size()) {
265 m_errorMsg = _(
"Error calculating the harmonic current in \"") + data.name + wxT(
"\".");
270 data.harmonicOrder.clear();
271 data.harmonicCurrent[0].clear();
272 data.harmonicCurrent[1].clear();
275 for (
auto& hVoltage : busData1.harmonicVoltage) {
276 int order = busData1.harmonicOrder[i];
277 data.harmonicOrder.push_back(order);
279 std::complex<double> v1 = hVoltage;
280 std::complex<double> v2 = busData2.harmonicVoltage[i];
281 std::complex<double> zs = std::complex<double>(data.resistance, data.indReactance * order);
282 std::complex<double> bsh = std::complex<double>(0.0, (data.capSusceptance * order) / 2.0);
284 data.harmonicCurrent[0].push_back((v1 - v2) / zs + v1 * bsh);
285 data.harmonicCurrent[1].push_back((v2 - v1) / zs + v2 * bsh);
289 line->SetElectricalData(data);
297 auto busData1 =
static_cast<Bus*
>(transformer->
GetParentList()[0])->GetElectricalData();
298 auto busData2 =
static_cast<Bus*
>(transformer->
GetParentList()[1])->GetElectricalData();
300 if (busData1.harmonicVoltage.size() != busData1.harmonicVoltage.size()) {
301 m_errorMsg = _(
"Error calculating the harmonic current in \"") + data.name + wxT(
"\".");
306 data.harmonicOrder.clear();
307 data.harmonicCurrent[0].clear();
308 data.harmonicCurrent[1].clear();
311 for (
auto& hVoltage : busData1.harmonicVoltage) {
312 int order = busData1.harmonicOrder[i];
313 data.harmonicOrder.push_back(order);
315 std::complex<double> v1 = hVoltage;
316 std::complex<double> v2 = busData2.harmonicVoltage[i];
318 auto yMatrix = GetTransformerHarmAdmmitance(transformer, systemPowerBase, order);
320 data.harmonicCurrent[0].push_back(yMatrix[0][0] * v1 + yMatrix[0][1] * v2);
321 data.harmonicCurrent[1].push_back(yMatrix[1][0] * v1 + yMatrix[1][1] * v2);
326 transformer->SetElectricaData(data);
334std::vector<double> PowerQuality::GetHarmonicOrdersList()
336 std::vector<int> harmOrders;
339 for (
auto it = harmCurrentList.begin(), itEnd = harmCurrentList.end(); it != itEnd; ++it) {
342 auto data = harmCurrent->GetElectricalData();
343 for (
unsigned int i = 0; i < data.harmonicOrder.size(); ++i) {
344 int order = data.harmonicOrder[i];
346 bool newOrder =
true;
347 for (
unsigned int j = 0; j < harmOrders.size(); ++j) {
348 if (order == harmOrders[j]) {
353 if (newOrder) harmOrders.push_back(order);
359 if (!emtElement->IsOnline())
continue;
361 auto data = emtElement->GetEMTElementData();
362 for (
auto const& [order, current] : data.currHarmonics) {
363 if (order == 1)
continue;
364 bool newOrder =
true;
365 for (
int vecOrder : harmOrders) {
366 if (order == vecOrder) {
371 if (newOrder) harmOrders.push_back(order);
375 std::vector<double> doubleHarmOrder;
376 for (
unsigned int i = 0; i < harmOrders.size(); ++i) {
377 doubleHarmOrder.push_back(
static_cast<double>(harmOrders[i]));
379 return doubleHarmOrder;
382std::vector<std::vector< std::complex<double> > > PowerQuality::GetTransformerHarmAdmmitance(
Transformer* transformer,
double systemPowerBase,
double hOrder,
bool ignoreConnection)
386 std::complex<double> ys = std::complex<double>(1.0, 0.0) / std::complex<double>(data.resistance, data.indReactance * hOrder);
387 std::complex<double> zeroCpx(0.0, 0.0);
388 std::vector<std::vector< std::complex<double> > > yMatrix{ {zeroCpx, zeroCpx}, {zeroCpx, zeroCpx} };
391 if ((data.turnsRatio == 1.0 && data.phaseShift == 0.0) || ignoreConnection) {
400 double radPhaseShift = wxDegToRad(data.phaseShift);
402 if (
static_cast<int>(hOrder) % 3 != 0) {
403 if (
static_cast<int>(hOrder) % 3 == 2) {
404 radPhaseShift *= -1.0;
406 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
407 -data.turnsRatio * std::sin(radPhaseShift));
409 yMatrix[0][0] = ys / (std::pow(std::abs(a), 2.0));
410 yMatrix[0][1] = -(ys / std::conj(a));
411 yMatrix[1][0] = -(ys / a);
415 switch (data.connection) {
417 ys = 1.0 / std::complex<double>(
418 data.zeroResistance + 3.0 * (data.primaryGrndResistance + data.secondaryGrndResistance),
419 data.zeroIndReactance +
420 3.0 * (data.primaryGrndReactance + data.secondaryGrndReactance));
421 std::complex<double> a = std::complex<double>(data.turnsRatio, 0.0);
423 yMatrix[0][0] = ys / (a * a);
424 yMatrix[0][1] = -(ys / a);
425 yMatrix[1][0] = -(ys / a);
429 ys = 1.0 / std::complex<double>(data.zeroResistance + 3.0 * (data.secondaryGrndResistance),
430 data.zeroIndReactance + 3.0 * (data.secondaryGrndReactance));
435 ys = 1.0 / std::complex<double>(data.zeroResistance + 3.0 * (data.primaryGrndResistance),
436 data.zeroIndReactance + 3.0 * (data.primaryGrndReactance));
449bool PowerQuality::CalculateFrequencyResponse(
double systemFreq,
454 double systemPowerBase,
455 HarmLoadConnection loadConnection)
458 for (
unsigned int i = 0; i <
m_busList.size(); i++) {
459 auto data =
m_busList[i]->GetElectricalData();
460 data.absImpedanceVector.clear();
461 data.absImpedanceVector.shrink_to_fit();
466 std::vector<std::vector<std::complex<double> > > yBus;
467 for (
unsigned int i = 0; i <
m_busList.size(); i++) {
468 std::vector<std::complex<double> > line;
469 for (
unsigned int j = 0; j <
m_busList.size(); j++) { line.push_back(std::complex<double>(0.0, 0.0)); }
470 yBus.push_back(line);
473 std::vector<std::complex<double> > iInj;
474 for (
unsigned int i = 0; i <
m_busList.size(); i++) { iInj.push_back(std::complex<double>(0.0, 0.0)); }
475 iInj[injBusNumber] = std::complex<double>(1.0, 0.0);
477 if (initFreq < 1e-6) initFreq = stepFreq;
478 double currentFreq = initFreq;
479 while (currentFreq <= endFreq) {
480 m_frequencyList.push_back(currentFreq);
482 double order = currentFreq / systemFreq;
485 for (
unsigned int i = 0; i <
m_busList.size(); i++) {
486 for (
unsigned int j = 0; j <
m_busList.size(); j++) { yBus[i][j] = std::complex<double>(0.0, 0.0); }
489 CalculateHarmonicYbus(yBus, systemPowerBase, order,
true, loadConnection);
491 for (
unsigned int i = 0; i <
m_busList.size(); i++) {
492 auto data =
m_busList[i]->GetElectricalData();
493 if (data.plotPQData) {
496 data.absImpedanceVector.push_back(std::abs(zh[data.number]));
501 currentFreq += stepFreq;
Node for power elements. All others power elements are connected through this.
Shunt capactior power element.
std::vector< Line * > m_lineList
List of transmission lines 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.
std::vector< Capacitor * > m_capacitorList
List of capacitor elements in the system.
std::vector< Transformer * > m_transformerList
List of transformers in the system.
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).
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.
const std::vector< HarmCurrent * > GetHarmCurrentList() const
Get the harmonic current source of the system (use GetElementsFromList first).
virtual std::vector< Element * > GetParentList() const
Get the parent list.
bool IsOnline() const
Checks if the element is online or offline.
Shunt Harmonic Corrent Source.
Inductor shunt power element.
Loas shunt power element.
Synchronous generator power element.
Synchronous motor (synchronous compensator) power element.