Power System Platform  2026w34a-beta
Loading...
Searching...
No Matches
PowerFlow Class Reference

Calculate the power flow (including OLTC tap adjustment). More...

#include <PowerFlow.h>

Inheritance diagram for PowerFlow:
Collaboration diagram for PowerFlow:

Public Member Functions

 PowerFlow (std::vector< Element * > elementList)
 
virtual bool InitPowerFlow (std::vector< BusType > &busType, std::vector< std::complex< double > > &voltage, std::vector< std::complex< double > > &power, std::vector< std::complex< double > > &loadPower, std::vector< ReactiveLimits > &reactiveLimit, double systemPowerBase=100e6, double initAngle=0.0)
 
virtual bool RunGaussSeidel (double systemPowerBase=100e6, int maxIteration=5000, double error=1e-6, double initAngle=0.0, double accFactor=1.0)
 
virtual bool RunNewtonRaphson (double systemPowerBase=100e6, int maxIteration=5000, double error=1e-6, double initAngle=0.0, double inertia=1.0)
 
virtual bool RunGaussNewton (double systemPowerBase=100e6, int maxIteration=5000, double error=1e-6, double initAngle=0.0, double accFactor=1.0, double gaussTol=1e-2, double inertia=1.0)
 
virtual wxString GetErrorMessage ()
 
virtual int GetIterations ()
 
virtual void ResetVoltages ()
 
- Public Member Functions inherited from ElectricCalculation
 ElectricCalculation ()
 Constructor.
 
 ~ElectricCalculation ()
 Destructor.
 
virtual void GetElementsFromList (std::vector< Element * > elementList)
 Separate the power elements from a generic list.
 
virtual bool GetYBus (std::vector< std::vector< std::complex< double > > > &yBus, double systemPowerBase, YBusSequence sequence=POSITIVE_SEQ, bool includeSyncMachines=false, bool allLoadsAsImpedances=false, bool usePowerFlowVoltagesOnImpedances=false)
 Get the admittance matrix from the list of elements (use GetElementsFromList first).
 
virtual bool InvertMatrix (std::vector< std::vector< std::complex< double > > > matrix, std::vector< std::vector< std::complex< double > > > &inverse)
 Invert a matrix.
 
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.
 
void ABCtoDQ0 (std::complex< double > complexValue, double angle, double &dValue, double &qValue)
 Convert a complex phasor in ABC representation to DQ components.
 
void DQ0toABC (double dValue, double qValue, double angle, std::complex< double > &complexValue)
 Convert DQ components to a complex phasor in ABC representation.
 
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< double > GaussianElimination (std::vector< std::vector< double > > matrix, std::vector< double > array)
 Solve a linear system using Gaussian elimination (real version).
 
Machines::SyncMachineModel GetMachineModel (SyncGenerator *generator)
 Get the synchronous machine model used by the generator based on user-defined parameters.
 
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.
 
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.
 
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.
 
bool GetParentBus (Element *childElement, Bus *&parentBus)
 Get the parent bus of a given shunt element.
 
bool GetParentBus (Element *childElement, Bus *&parentBus1, Bus *&parentBus2)
 Get the parent buses of a two-terminal element (branch).
 
bool CalculateEMTElementsAdmittance (const double &basePower, wxString &errorMsg)
 Calculate the admittance of EMT elements.
 
bool CalculateEMTElementsPower (const double &basePower, wxString &errorMsg, bool updateCurrent=true)
 Calculate the power of EMT elements.
 
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.
 
const std::vector< PowerElement * > GetPowerElementList () const
 Get the power elements of the system (use GetElementsFromList first).
 
const std::vector< Bus * > GetBusList () const
 Get the buses of the system (use GetElementsFromList first).
 
const std::vector< Capacitor * > GetCapacitorList () const
 Get the capacitors of the system (use GetElementsFromList first).
 
const std::vector< IndMotor * > GetIndMotorList () const
 Get the induction motors of the system (use GetElementsFromList first).
 
const std::vector< Inductor * > GetInductorList () const
 Get the inductors of the system (use GetElementsFromList first).
 
const std::vector< Line * > GetLineList () const
 Get the lines of the system (use GetElementsFromList first).
 
const std::vector< Load * > GetLoadList () const
 Get the loads of the system (use GetElementsFromList first).
 
const std::vector< SyncGenerator * > GetSyncGeneratorList () const
 Get the synchronous generators of the system (use GetElementsFromList first).
 
const std::vector< SyncMotor * > GetSyncMotorList () const
 Get the synchronous motors of the system (use GetElementsFromList first).
 
const std::vector< Transformer * > GetTransformerList () const
 Get the transformers of the system (use GetElementsFromList first).
 
const std::vector< HarmCurrent * > GetHarmCurrentList () const
 Get the harmonic current source of the system (use GetElementsFromList first).
 
const std::vector< EMTElement * > GetEMTElementList () const
 Get the electromagnetic element list of the system (use GetElementsFromList first).
 

Protected Member Functions

void GetNumPVPQ (std::vector< BusType > busType, int &numPQ, int &numPV)
 
std::vector< std::vector< double > > CalculateJacobianMatrix (const std::vector< std::complex< double > > &voltage, const std::vector< std::complex< double > > &power, const std::vector< BusType > &busType, int numPV, int numPQ)
 
bool CheckReactiveLimits (std::vector< BusType > &busType, std::vector< ReactiveLimits > &reactiveLimit, std::vector< std::complex< double > > &power, std::vector< std::complex< double > > loadPower)
 
double GaussSeidel (std::vector< BusType > busType, std::vector< std::complex< double > > &voltage, std::vector< std::complex< double > > oldVoltage, std::vector< std::complex< double > > &power, double accFactor)
 
void NewtonRaphson (std::vector< BusType > busType, std::vector< std::complex< double > > &voltage, std::vector< std::complex< double > > power, int numPV, int numPQ, std::vector< double > dPdQ, double inertia)
 
bool CalculateMotorsReactivePower (std::vector< std::complex< double > > voltage, std::vector< std::complex< double > > &power)
 
bool HasInvalidValue (const std::vector< std::complex< double > > &voltage)
 
bool AdjustTapChangers (const std::vector< std::complex< double > > &voltage, double systemPowerBase)
 
- Protected Member Functions inherited from ElectricCalculation
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.
 
void DistributeReactivePower (std::vector< ReactiveMachine > &machines, double qTotal)
 Distribute reactive power among synchronous machines connected to the same bus.
 

Protected Attributes

std::vector< std::vector< std::complex< double > > > m_yBus
 
wxString m_errorMsg = ""
 
int m_numberOfBuses = 0
 
int m_iterations = 0
 
int m_tapAdjustmentsCount = 0
 
- Protected Attributes inherited from ElectricCalculation
std::vector< PowerElement * > m_powerElementList
 List of power elements in the system.
 
std::vector< Bus * > m_busList
 List of buses in the system.
 
std::vector< Capacitor * > m_capacitorList
 List of capacitor elements in the system.
 
std::vector< IndMotor * > m_indMotorList
 List of induction motors in the system.
 
std::vector< Inductor * > m_inductorList
 List of inductors in the system.
 
std::vector< Line * > m_lineList
 List of transmission lines in the system.
 
std::vector< Load * > m_loadList
 List of load elements in the system.
 
std::vector< SyncGenerator * > m_syncGeneratorList
 List of synchronous generators in the system.
 
std::vector< SyncMotor * > m_syncMotorList
 List of synchronous motors in the system.
 
std::vector< Transformer * > m_transformerList
 List of transformers in the system.
 
std::vector< HarmCurrent * > m_harmCurrentList
 List of harmonic current sources in the system.
 
std::vector< EMTElement * > m_emtElementList
 List of electromagnetic transient (EMT) elements in the system.
 

Detailed Description

Calculate the power flow (including OLTC tap adjustment).

Author
Thales Lima Oliveira thale.nosp@m.s@uf.nosp@m.u.br
Luiz Gonzaga Rocha Junior luizg.nosp@m.rj@g.nosp@m.mail..nosp@m.com
Date
06/10/2017

Definition at line 36 of file PowerFlow.h.

Constructor & Destructor Documentation

◆ PowerFlow() [1/2]

PowerFlow::PowerFlow ( )

Definition at line 20 of file PowerFlow.cpp.

◆ PowerFlow() [2/2]

PowerFlow::PowerFlow ( std::vector< Element * >  elementList)

Definition at line 21 of file PowerFlow.cpp.

21: ElectricCalculation() { GetElementsFromList(elementList); }
virtual void GetElementsFromList(std::vector< Element * > elementList)
Separate the power elements from a generic list.

◆ ~PowerFlow()

PowerFlow::~PowerFlow ( )

Definition at line 22 of file PowerFlow.cpp.

22{}

Member Function Documentation

◆ AdjustTapChangers()

bool PowerFlow::AdjustTapChangers ( const std::vector< std::complex< double > > &  voltage,
double  systemPowerBase 
)
protected

Definition at line 930 of file PowerFlow.cpp.

931{
932 if (m_tapAdjustmentsCount >= 40) {
933 // Limit total adjustments to avoid infinite cycling
934 return false;
935 }
936
937 bool anyAdjusted = false;
938
939 for (Transformer* transformer : m_transformerList) {
940 if (!transformer->IsOnline() || transformer->GetParentList().size() < 2) continue;
941
942 TransformerElectricalData data = transformer->GetElectricalData();
943 if (!data.hasTapChanger) continue;
944
945 Bus* bus1 = static_cast<Bus*>(transformer->GetParentList()[0]);
946 Bus* bus2 = static_cast<Bus*>(transformer->GetParentList()[1]);
947 if (!bus1 || !bus2) continue;
948
949 int n1 = bus1->GetElectricalData().number;
950 int n2 = bus2->GetElectricalData().number;
951
952 int nCtrl = (data.oltcControlledBus == 0) ? n1 : n2;
953 bool isPrimary = (data.oltcControlledBus == 0);
954
955 if (nCtrl < 0 || nCtrl >= (int)voltage.size()) continue;
956
957 double vCtrl = std::abs(voltage[nCtrl]);
958 double vTarget = data.oltcTargetVoltage;
959 double deadband = std::max(data.oltcVoltageDeadband, 1e-4);
960
961 if (data.oltcIsDiscrete && data.oltcTapStep > 1e-4) {
962 deadband = std::max(deadband, 0.5 * data.oltcTapStep * vCtrl);
963 }
964
965 double vDiff = vCtrl - vTarget;
966 if (std::abs(vDiff) <= deadband) continue;
967
968 double currentTap = data.turnsRatio;
969 if (currentTap <= 1e-4) currentTap = 1.0;
970
971
972 //tex:
973 // Model in PSP-UFU has ideal turns ratio $a$ on side 1 (Primary):
974 //
975 // $$\frac{V_1}{a} \approx V_2$$
976 // $$V_2 \approx \frac{V_1}{a}, \qquad V_1 \approx aV_2$$
977 //
978 // Secondary (side 2):
979 //
980 // $$\frac{dV_2}{da} \approx -\frac{V_2}{a}$$
981 //
982 // $$\Delta V_2 \approx -\frac{V_2}{a}\Delta a$$
983 // $$\Rightarrow\quad
984 // \Delta a \approx -\frac{a}{V_2}\Delta V_2
985 // = \frac{a}{V_2}(V_2 - V_{\mathrm{target}})
986 // = \frac{a}{V_2}v_{\mathrm{Diff}}$$
987 //
988 // Primary (side 1):
989 //
990 // $$\frac{dV_1}{da} \approx \frac{V_1}{a}$$
991 //
992 // $$\Delta V_1 \approx \frac{V_1}{a}\Delta a$$
993 // $$\Rightarrow\quad
994 // \Delta a \approx \frac{a}{V_1}\Delta V_1
995 // = -\frac{a}{V_1}(V_1 - V_{\mathrm{target}})
996 // = -\frac{a}{V_1}v_{\mathrm{Diff}}$$
997
998 double damping = 0.8;
999 double deltaTap = (isPrimary ? -1.0 : 1.0) * (currentTap / std::max(vCtrl, 0.1)) * vDiff * damping;
1000
1001 if (data.oltcIsDiscrete && data.oltcTapStep > 1e-4) {
1002 double step = data.oltcTapStep;
1003 double numSteps = std::round(deltaTap / step);
1004 if (numSteps == 0.0) {
1005 numSteps = (deltaTap > 0.0) ? 1.0 : -1.0;
1006 }
1007 deltaTap = numSteps * step;
1008 }
1009
1010 double newTap = currentTap + deltaTap;
1011
1012 // Limit to min/max range
1013 if (newTap < data.oltcMinTap) newTap = data.oltcMinTap;
1014 if (newTap > data.oltcMaxTap) newTap = data.oltcMaxTap;
1015
1016 if (data.oltcIsDiscrete && data.oltcTapStep > 1e-4) {
1017 double step = data.oltcTapStep;
1018 newTap = 1.0 + std::round((newTap - 1.0) / step) * step;
1019 if (newTap < data.oltcMinTap) newTap = data.oltcMinTap;
1020 if (newTap > data.oltcMaxTap) newTap = data.oltcMaxTap;
1021 }
1022
1023 if (std::abs(newTap - currentTap) > 1e-5) {
1024 data.turnsRatio = newTap;
1025 transformer->SetElectricaData(data);
1026 anyAdjusted = true;
1027 }
1028 }
1029
1030 if (anyAdjusted) {
1031 m_tapAdjustmentsCount++;
1032 GetYBus(m_yBus, systemPowerBase);
1033 }
1034
1035 return anyAdjusted;
1036}
Node for power elements. All others power elements are connected through this.
Definition Bus.h:87
std::vector< Transformer * > m_transformerList
List of transformers in the system.
virtual bool GetYBus(std::vector< std::vector< std::complex< double > > > &yBus, double systemPowerBase, YBusSequence sequence=POSITIVE_SEQ, bool includeSyncMachines=false, bool allLoadsAsImpedances=false, bool usePowerFlowVoltagesOnImpedances=false)
Get the admittance matrix from the list of elements (use GetElementsFromList first).
Two-winding transformer power element with OLTC support.
Definition Transformer.h:96

◆ CalculateJacobianMatrix()

std::vector< std::vector< double > > PowerFlow::CalculateJacobianMatrix ( const std::vector< std::complex< double > > &  voltage,
const std::vector< std::complex< double > > &  power,
const std::vector< BusType > &  busType,
int  numPV,
int  numPQ 
)
protected

Definition at line 759 of file PowerFlow.cpp.

765{
766 int nvar = 2 * numPQ + numPV;
767
768 std::vector<std::vector<double>> J(nvar, std::vector<double>(nvar, 0.0));
769
770 int n = m_numberOfBuses;
771
772 // Precompute magnitudes and angles
773 std::vector<double> V(n);
774 std::vector<double> T(n);
775
776 for (int i = 0; i < n; i++)
777 {
778 V[i] = std::abs(voltage[i]);
779 T[i] = std::arg(voltage[i]);
780 }
781
782 // Compute currents
783 std::vector<std::complex<double>> I(n);
784
785 for (int i = 0; i < n; i++)
786 {
787 I[i] = 0;
788 for (int k = 0; k < n; k++)
789 I[i] += m_yBus[i][k] * voltage[k];
790 }
791
792 // Compute power injections
793 std::vector<std::complex<double>> S(n);
794
795 for (int i = 0; i < n; i++)
796 S[i] = voltage[i] * std::conj(I[i]);
797
798 int rowP = 0;
799 int rowQ = numPV + numPQ;
800
801 for (int i = 0; i < n; i++)
802 {
803 if (busType[i] == BUS_SLACK)
804 continue;
805
806 int colTheta = 0;
807 int colV = numPV + numPQ;
808
809 for (int j = 0; j < n; j++)
810 {
811 if (busType[j] == BUS_SLACK)
812 continue;
813
814 double G = m_yBus[i][j].real();
815 double B = m_yBus[i][j].imag();
816
817 double dtheta = T[i] - T[j];
818
819 if (i == j)
820 {
821 double Pi = S[i].real();
822 double Qi = S[i].imag();
823
824 // J11
825 J[rowP][colTheta] = -Qi - B * V[i] * V[i];
826
827 // J12 only PQ
828 if (busType[i] == BUS_PQ)
829 J[rowQ][colTheta] = Pi - G * V[i] * V[i];
830 }
831 else
832 {
833 // J11
834 J[rowP][colTheta] = V[i] * V[j] * (G * sin(dtheta) - B * cos(dtheta));
835
836 if (busType[i] == BUS_PQ)// J21
837 J[rowQ][colTheta] = -V[i] * V[j] * (G * cos(dtheta) + B * sin(dtheta));
838 }
839
840 colTheta++;
841 }
842
843 colTheta = 0;
844
845 for (int j = 0; j < n; j++)
846 {
847 if (busType[j] != BUS_PQ)
848 continue;
849
850 double G = m_yBus[i][j].real();
851 double B = m_yBus[i][j].imag();
852
853 double dtheta = T[i] - T[j];
854
855 if (i == j)
856 {
857 double Pi = S[i].real();
858 double Qi = S[i].imag();
859
860 // J12
861 J[rowP][colV] = Pi / V[i] + G * V[i];
862
863 if (busType[i] == BUS_PQ) // J22
864 J[rowQ][colV] = Qi / V[i] - B * V[i];
865 }
866 else
867 {
868 // J12
869 J[rowP][colV] = V[i] * (G * cos(dtheta) + B * sin(dtheta));
870
871 if (busType[i] == BUS_PQ) // J22
872 J[rowQ][colV] = V[i] * (G * sin(dtheta) - B * cos(dtheta));
873 }
874 colV++;
875 }
876 rowP++;
877
878 if (busType[i] == BUS_PQ)
879 rowQ++;
880 }
881
882 return J;
883}
@ BUS_SLACK

◆ CalculateMotorsReactivePower()

bool PowerFlow::CalculateMotorsReactivePower ( std::vector< std::complex< double > >  voltage,
std::vector< std::complex< double > > &  power 
)
protected

Definition at line 914 of file PowerFlow.cpp.

916{
917 for (unsigned int i = 0; i < m_indMotorList.size(); ++i) {
918 IndMotor* motor = m_indMotorList[i];
919 auto data = motor->GetElectricalData();
920 if (motor->IsOnline() && data.calcQInPowerFlow) {
921 double oldQ = data.qValue;
922 if (!motor->CalculateReactivePower(std::abs(voltage[data.busNum]))) return false;
923 double dQ = oldQ - motor->GetElectricalData().qValue;
924 power[data.busNum] += std::complex<double>(0.0, dQ);
925 }
926 }
927 return true;
928}
std::vector< IndMotor * > m_indMotorList
List of induction motors in the system.
bool IsOnline() const
Checks if the element is online or offline.
Definition Element.h:229
Induction motor power element.
Definition IndMotor.h:119

◆ CheckReactiveLimits()

bool PowerFlow::CheckReactiveLimits ( std::vector< BusType > &  busType,
std::vector< ReactiveLimits > &  reactiveLimit,
std::vector< std::complex< double > > &  power,
std::vector< std::complex< double > >  loadPower 
)
protected

Definition at line 885 of file PowerFlow.cpp.

889{
890 bool limitReach = false;
891 for (int i = 0; i < m_numberOfBuses; i++) {
892 if (busType[i] == BUS_PV) {
893 if (reactiveLimit[i].maxLimitType == RL_LIMITED) {
894 if (power[i].imag() - loadPower[i].imag() > reactiveLimit[i].maxLimit) {
895 power[i] = std::complex<double>(power[i].real(), reactiveLimit[i].maxLimit + loadPower[i].imag());
896 busType[i] = BUS_PQ;
897 reactiveLimit[i].limitReached = RL_MAX_REACHED;
898 limitReach = true;
899 }
900 }
901 if (reactiveLimit[i].minLimitType == RL_LIMITED) {
902 if (power[i].imag() - loadPower[i].imag() < reactiveLimit[i].minLimit) {
903 power[i] = std::complex<double>(power[i].real(), reactiveLimit[i].minLimit + loadPower[i].imag());
904 busType[i] = BUS_PQ;
905 reactiveLimit[i].limitReached = RL_MIN_REACHED;
906 limitReach = true;
907 }
908 }
909 }
910 }
911 return limitReach;
912}
@ RL_MAX_REACHED
@ RL_MIN_REACHED
@ RL_LIMITED

◆ GaussSeidel()

double PowerFlow::GaussSeidel ( std::vector< BusType >  busType,
std::vector< std::complex< double > > &  voltage,
std::vector< std::complex< double > >  oldVoltage,
std::vector< std::complex< double > > &  power,
double  accFactor 
)
protected

Definition at line 657 of file PowerFlow.cpp.

662{
663 double error = 0.0;
664
665 for (int i = 0; i < m_numberOfBuses; i++) {
666 if (busType[i] == BUS_PQ) {
667 std::complex<double> yeSum(0.0, 0.0);
668 for (int k = 0; k < m_numberOfBuses; k++) {
669 if (i != k) {
670 // Sum { Y[i,k] * E[k] } | k = 1->n; k diff i
671 yeSum += m_yBus[i][k] * voltage[k];
672 }
673 }
674
675 // E[i] = (1/Y[i,i])*((P[i]-jQ[i])/E*[i] - Sum { Y[i,k] * E[k] (k diff i) })
676 std::complex<double> newVolt = (1.0 / m_yBus[i][i]) * (std::conj(power[i]) / std::conj(voltage[i]) - yeSum);
677
678 // Apply the acceleration factor.
679 newVolt = std::complex<double>(accFactor * (newVolt.real() - voltage[i].real()) + voltage[i].real(),
680 accFactor * (newVolt.imag() - voltage[i].imag()) + voltage[i].imag());
681
682 voltage[i] = newVolt;
683 }
684 if (busType[i] == BUS_PV) {
685 std::complex<double> yeSum(0.0, 0.0);
686 for (int k = 0; k < m_numberOfBuses; k++) {
687 if (i != k) {
688 // Sum { Y[i,k] * E[k] } | k = 1->n; k diff i
689 yeSum += m_yBus[i][k] * voltage[k];
690 }
691 }
692 std::complex<double> yeSumT = yeSum + (m_yBus[i][i] * voltage[i]);
693
694 // Q[i] = - Im( E*[i] * Sum { Y[i,k] * E[k] } )
695 std::complex<double> qCalc = std::conj(voltage[i]) * yeSumT;
696 power[i] = std::complex<double>(power[i].real(), -qCalc.imag());
697
698 // E[i] = (1/Y[i,i])*((P[i]-jQ[i])/E*[i] - Sum { Y[i,k] * E[k] (k diff i) })
699 std::complex<double> newVolt = (1.0 / m_yBus[i][i]) * (std::conj(power[i]) / std::conj(voltage[i]) - yeSum);
700
701 // Apply the acceleration factor.
702 newVolt = std::complex<double>(accFactor * (newVolt.real() - voltage[i].real()) + voltage[i].real(),
703 accFactor * (newVolt.imag() - voltage[i].imag()) + voltage[i].imag());
704
705 // Keep the same voltage magnitude.
706 voltage[i] = std::complex<double>(std::abs(voltage[i]) * std::cos(std::arg(newVolt)),
707 std::abs(voltage[i]) * std::sin(std::arg(newVolt)));
708 }
709
710 double busError = std::max(std::abs(voltage[i].real() - oldVoltage[i].real()),
711 std::abs(voltage[i].imag() - oldVoltage[i].imag()));
712
713 if (busError > error) error = busError;
714 }
715 return error;
716}

◆ GetErrorMessage()

virtual wxString PowerFlow::GetErrorMessage ( )
inlinevirtual

Definition at line 67 of file PowerFlow.h.

67{ return m_errorMsg; }

◆ GetIterations()

virtual int PowerFlow::GetIterations ( )
inlinevirtual

Definition at line 68 of file PowerFlow.h.

68{ return m_iterations; }

◆ GetNumPVPQ()

void PowerFlow::GetNumPVPQ ( std::vector< BusType >  busType,
int &  numPQ,
int &  numPV 
)
protected

Definition at line 645 of file PowerFlow.cpp.

646{
647 numPQ = 0;
648 numPV = 0;
649 for (auto it = busType.begin(), itEnd = busType.end(); it != itEnd; ++it) {
650 if (*it == BUS_PQ)
651 numPQ++;
652 else if (*it == BUS_PV)
653 numPV++;
654 }
655}

◆ HasInvalidValue()

bool PowerFlow::HasInvalidValue ( const std::vector< std::complex< double > > &  voltage)
protected

Definition at line 338 of file PowerFlow.cpp.

339{
340 for (size_t i = 0; i < value.size(); i++)
341 {
342 if (!std::isfinite(value[i].real()) ||
343 !std::isfinite(value[i].imag()))
344 {
345 return true;
346 }
347 }
348 return false;
349}

◆ InitPowerFlow()

bool PowerFlow::InitPowerFlow ( std::vector< BusType > &  busType,
std::vector< std::complex< double > > &  voltage,
std::vector< std::complex< double > > &  power,
std::vector< std::complex< double > > &  loadPower,
std::vector< ReactiveLimits > &  reactiveLimit,
double  systemPowerBase = 100e6,
double  initAngle = 0.0 
)
virtual

Definition at line 23 of file PowerFlow.cpp.

30{
31 double radInitAngle = wxDegToRad(initAngle);
32
33 // Calculate EMT Elements admittance to add to the Ybus.
34 if (!CalculateEMTElementsAdmittance(systemPowerBase, m_errorMsg)) return false;
35
36 // Calculate the Ybus.
37 if (!GetYBus(m_yBus, systemPowerBase)) {
38 m_errorMsg = _("No buses found on the system.");
39 return false;
40 }
41
42 // Calculate EMT elements power for 1.0 p.u. voltage
43 if (!CalculateEMTElementsPower(systemPowerBase, m_errorMsg, false)) return false;
44
45 // Number of buses in the system.
46 //m_numberOfBuses = static_cast<int>(m_busList.size());
47 m_numberOfBuses = static_cast<int>(m_yBus.size());
48
49 busType.clear();
50 voltage.clear();
51 power.clear();
52 loadPower.clear();
53 reactiveLimit.clear();
54
55 reactiveLimit.resize(m_numberOfBuses);
56
57 int busNumber = 0;
58 Bus* slackBus = nullptr;
59 for (auto itb = m_busList.begin(); itb != m_busList.end(); itb++) {
60 Bus* bus = *itb;
61 BusElectricalData data = bus->GetElectricalData();
62 if (data.isConnected) {
63 // Fill the bus type
64 if (data.slackBus) {
65 busType.push_back(BUS_SLACK);
66 slackBus = bus;
67 }
68 // If the bus have controlled voltage, check if at least one synchronous machine is connected, then set the
69 // bus type.
70 else if (data.isVoltageControlled) {
71 bool hasSyncMachine = false;
72 // Synchronous generator
73 for (auto itsg = m_syncGeneratorList.begin(); itsg != m_syncGeneratorList.end(); itsg++) {
74 SyncGenerator* syncGenerator = *itsg;
75 if (bus == syncGenerator->GetParentList()[0] && syncGenerator->IsOnline()) hasSyncMachine = true;
76 }
77 // Synchronous motor
78 for (auto itsm = m_syncMotorList.begin(); itsm != m_syncMotorList.end(); itsm++) {
79 SyncMotor* syncMotor = *itsm;
80 if (bus == syncMotor->GetParentList()[0] && syncMotor->IsOnline()) hasSyncMachine = true;
81 }
82 if (hasSyncMachine)
83 busType.push_back(BUS_PV);
84 else
85 busType.push_back(BUS_PQ);
86 }
87 else
88 busType.push_back(BUS_PQ);
89
90 // Fill the voltages array
91 double v = 1.0;
92 double t = 0.0;
93 if (data.slackBus) {
94 v = data.controlledVoltage;
95 t = radInitAngle;
96 }
97 else if (busType[busNumber] == BUS_PV) {
98 v = data.controlledVoltage;
99 t = std::arg(data.voltage);
100 }
101 else {
102 v = std::abs(data.voltage);
103 if (v <= 0.1) v = 1.0; // Avoid very low voltage magnitude that can cause convergence problems.
104 t = std::arg(data.voltage);
105 }
106 voltage.push_back(std::complex<double>(v * std::cos(t), v * std::sin(t)));
107
108 //if (data.isVoltageControlled && busType[busNumber] != BUS_PQ) {
109 // voltage.push_back(std::complex<double>(data.controlledVoltage * std::cos(radInitAngle),
110 // data.controlledVoltage * std::sin(radInitAngle)));
111 //}
112 //else {
113 // voltage.push_back(std::complex<double>(std::cos(radInitAngle), std::sin(radInitAngle)));
114 //}
115
116 // Fill the power array
117 power.push_back(std::complex<double>(0.0, 0.0)); // Initial value
118 loadPower.push_back(std::complex<double>(0.0, 0.0));
119
120 // Synchronous generator
121 for (auto itsg = m_syncGeneratorList.begin(); itsg != m_syncGeneratorList.end(); itsg++) {
122 SyncGenerator* syncGenerator = *itsg;
123 if (syncGenerator->IsOnline()) {
124 if (bus == syncGenerator->GetParentList()[0]) {
125 SyncGeneratorElectricalData childData = syncGenerator->GetPUElectricalData(systemPowerBase);
126 power[busNumber] += std::complex<double>(childData.activePower, childData.reactivePower);
127
128 if (busType[busNumber] == BUS_PV) {
129 if (childData.haveMaxReactive && reactiveLimit[busNumber].maxLimitType != RL_UNLIMITED_SOURCE) {
130 reactiveLimit[busNumber].maxLimitType = RL_LIMITED;
131 reactiveLimit[busNumber].maxLimit += childData.maxReactive;
132 }
133 else if (!childData.haveMaxReactive)
134 reactiveLimit[busNumber].maxLimitType = RL_UNLIMITED_SOURCE;
135
136 if (childData.haveMinReactive && reactiveLimit[busNumber].minLimitType != RL_UNLIMITED_SOURCE) {
137 reactiveLimit[busNumber].minLimitType = RL_LIMITED;
138 reactiveLimit[busNumber].minLimit += childData.minReactive;
139 }
140 else if (!childData.haveMinReactive)
141 reactiveLimit[busNumber].minLimitType = RL_UNLIMITED_SOURCE;
142 }
143 }
144 }
145 }
146 // Synchronous motor
147 for (auto itsm = m_syncMotorList.begin(); itsm != m_syncMotorList.end(); itsm++) {
148 SyncMotor* syncMotor = *itsm;
149 if (syncMotor->IsOnline()) {
150 if (bus == syncMotor->GetParentList()[0]) {
151 SyncMotorElectricalData childData = syncMotor->GetPUElectricalData(systemPowerBase);
152 power[busNumber] += std::complex<double>(-childData.activePower, childData.reactivePower);
153 loadPower[busNumber] += std::complex<double>(-childData.activePower, 0.0);
154
155 if (busType[busNumber] == BUS_PV) {
156 if (childData.haveMaxReactive && reactiveLimit[busNumber].maxLimitType != RL_UNLIMITED_SOURCE) {
157 reactiveLimit[busNumber].maxLimitType = RL_LIMITED;
158 reactiveLimit[busNumber].maxLimit += childData.maxReactive;
159 }
160 else if (!childData.haveMaxReactive)
161 reactiveLimit[busNumber].maxLimitType = RL_UNLIMITED_SOURCE;
162
163 if (childData.haveMinReactive && reactiveLimit[busNumber].minLimitType != RL_UNLIMITED_SOURCE) {
164 reactiveLimit[busNumber].minLimitType = RL_LIMITED;
165 reactiveLimit[busNumber].minLimit += childData.minReactive;
166 }
167 else if (!childData.haveMinReactive)
168 reactiveLimit[busNumber].minLimitType = RL_UNLIMITED_SOURCE;
169 }
170 }
171 }
172 }
173 // Load
174 for (auto itl = m_loadList.begin(); itl != m_loadList.end(); itl++) {
175 Load* load = *itl;
176 if (load->IsOnline()) {
177 if (bus == load->GetParentList()[0]) {
178 LoadElectricalData childData = load->GetPUElectricalData(systemPowerBase);
179 if (childData.loadType == CONST_POWER) {
180 power[busNumber] += std::complex<double>(-childData.activePower, -childData.reactivePower);
181 loadPower[busNumber] += std::complex<double>(-childData.activePower, -childData.reactivePower);
182 }
183 }
184 }
185 }
186
187 // Induction motor
188 for (auto itim = m_indMotorList.begin(); itim != m_indMotorList.end(); itim++) {
189 IndMotor* indMotor = *itim;
190 if (indMotor->IsOnline()) {
191 if (bus == indMotor->GetParentList()[0]) {
192 IndMotorElectricalData childData = indMotor->GetPUElectricalData(systemPowerBase);
193 double reactivePower = childData.reactivePower;
194
195 if (childData.calcQInPowerFlow) {
196 indMotor->InitPowerFlowMotor(systemPowerBase, data.number);
197 if (!indMotor->CalculateReactivePower(std::abs(voltage[childData.busNum]))) {
198 m_errorMsg = _("It was not possible to solve the induction motors.");
199 return false;
200 }
201 reactivePower = indMotor->GetElectricalData().qValue;
202 }
203
204 power[busNumber] += std::complex<double>(-childData.activePower, -reactivePower);
205 loadPower[busNumber] += std::complex<double>(-childData.activePower, -reactivePower);
206 }
207 }
208 }
209
210 // EMTElements
211 for (EMTElement* emtElement : m_emtElementList) {
212 if (emtElement->IsOnline() && !emtElement->GetParentList().empty()) {
213 if (bus == emtElement->GetParentList()[0]) {
214 EMTElementData childData = emtElement->GetEMTElementData();
215 power[busNumber] -= childData.power;
216 loadPower[busNumber] -= childData.power;
217 }
218 }
219 }
220
221 busNumber++;
222 }
223 }
224
225 // Check if have slack bus and if have generation on the slack bus
226 bool haveSlackBus = false;
227 bool slackBusHaveGeneration = false;
228 for (unsigned int i = 0; i < busType.size(); i++) {
229 if (busType[i] == BUS_SLACK) {
230 for (auto itsg = m_syncGeneratorList.begin(); itsg != m_syncGeneratorList.end(); itsg++) {
231 SyncGenerator* syncGenerator = *itsg;
232 if (syncGenerator->IsOnline() && slackBus == syncGenerator->GetParentList()[0]) slackBusHaveGeneration = true;
233 }
234 haveSlackBus = true;
235 }
236 }
237 if (!haveSlackBus) {
238 m_errorMsg = _("There is no slack bus on the system.");
239 return false;
240 }
241 if (!slackBusHaveGeneration) {
242 m_errorMsg = _("The slack bus don't have generation.");
243 return false;
244 }
245
246 m_tapAdjustmentsCount = 0;
247 return true;
248}
@ RL_UNLIMITED_SOURCE
Element to connect ATP-EMTP.
Definition EMTElement.h:83
std::vector< Bus * > m_busList
List of buses in the system.
std::vector< Load * > m_loadList
List of load elements in the system.
bool CalculateEMTElementsAdmittance(const double &basePower, wxString &errorMsg)
Calculate the admittance of EMT elements.
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< SyncMotor * > m_syncMotorList
List of synchronous motors in the system.
virtual std::vector< Element * > GetParentList() const
Get the parent list.
Definition Element.h:567
Loas shunt power element.
Definition Load.h:74
Synchronous generator power element.
Synchronous motor (synchronous compensator) power element.
Definition SyncMotor.h:135

◆ NewtonRaphson()

void PowerFlow::NewtonRaphson ( std::vector< BusType >  busType,
std::vector< std::complex< double > > &  voltage,
std::vector< std::complex< double > >  power,
int  numPV,
int  numPQ,
std::vector< double >  dPdQ,
double  inertia 
)
protected

Definition at line 718 of file PowerFlow.cpp.

725{
726 // Jacobian matrix
727 std::vector<std::vector<double> > jacobMatrix = CalculateJacobianMatrix(voltage, power, busType, numPV, numPQ);
728
729 // Apply inertia
730 for (unsigned int i = 0; i < dPdQ.size(); ++i) { dPdQ[i] = inertia * dPdQ[i]; }
731
732 // Calculate DeltaTheta DeltaV array
733 std::vector<double> dTdV = GaussianElimination(jacobMatrix, dPdQ);
734
735 // Update voltage array
736 int indexDT = 0;
737 int indexDV = numPQ + numPV;
738 for (int i = 0; i < m_numberOfBuses; i++) {
739 if (busType[i] != BUS_SLACK) {
740 if (busType[i] == BUS_PV) {
741 double newV = std::abs(voltage[i]);
742 double newT = std::arg(voltage[i]) + dTdV[indexDT];
743 voltage[i] = std::complex<double>(newV * std::cos(newT), newV * std::sin(newT));
744 indexDT++;
745 }
746 else {
747 //double newV = std::abs(voltage[i]) * (1.0 + dTdV[indexDV]);
748 double newV = std::abs(voltage[i]) + dTdV[indexDV];
749 double newT = std::arg(voltage[i]) + dTdV[indexDT];
750 voltage[i] = std::complex<double>(newV * std::cos(newT), newV * std::sin(newT));
751 indexDV++;
752 indexDT++;
753 }
754 }
755 }
756}
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).

◆ ResetVoltages()

void PowerFlow::ResetVoltages ( )
virtual

Definition at line 619 of file PowerFlow.cpp.

620{
621 for (auto* bus : m_busList) {
622 BusElectricalData data = bus->GetElectricalData();
623 if (bus->IsOnline()) {
624 data.voltage = std::complex<double>(1.0, 0.0);
625 if (data.isVoltageControlled) {
626 data.voltage = std::complex<double>(data.controlledVoltage, 0.0);
627 }
628 }
629 else
630 data.voltage = std::complex<double>(0.0, 0.0);
631 data.harmonicOrder.clear();
632 data.harmonicVoltage.clear();
633 data.thd = 0.0;
634 bus->SetElectricalData(data);
635 }
636 for (auto* transf : m_transformerList) {
637 auto data = transf->GetElectricalData();
638 if (data.hasTapChanger && data.nominalTurnsRatio > 0.0) {
639 data.turnsRatio = data.nominalTurnsRatio;
640 transf->SetElectricaData(data);
641 }
642 }
643}

◆ RunGaussNewton()

bool PowerFlow::RunGaussNewton ( double  systemPowerBase = 100e6,
int  maxIteration = 5000,
double  error = 1e-6,
double  initAngle = 0.0,
double  accFactor = 1.0,
double  gaussTol = 1e-2,
double  inertia = 1.0 
)
virtual

Definition at line 479 of file PowerFlow.cpp.

486{
487 std::vector<BusType> busType; // Bus type
488 std::vector<std::complex<double> > voltage; // Voltage of buses
489 std::vector<std::complex<double> > power; // Injected power
490 std::vector<std::complex<double> > loadPower; // Only the load power
491 std::vector<ReactiveLimits> reactiveLimit; // Limit of reactive power on PV buses
492
493 if (!InitPowerFlow(busType, voltage, power, loadPower, reactiveLimit, systemPowerBase, initAngle)) return false;
494
495 // Gauss-Seidel method
496 std::vector<std::complex<double> > oldVoltage; // Old voltage array.
497 oldVoltage.resize(voltage.size());
498
499 auto oldBusType = busType;
500
501 int iteration = 0; // Current itaration number.
502
503 while (true) {
504 // Reach the max number of iterations.
505 if (iteration >= maxIteration) {
506 m_errorMsg = _("The maximum number of iterations was reached.");
507 return false;
508 }
509
510 // Calculate induction motor reactive power
511 if (!CalculateMotorsReactivePower(voltage, power)) {
512 m_errorMsg = _("It was not possible to solve the induction motors.");
513 return false;
514 }
515
516 // Update the old voltage array to current iteration values.
517 for (int i = 0; i < m_numberOfBuses; i++) oldVoltage[i] = voltage[i];
518
519 double iterationError = GaussSeidel(busType, voltage, oldVoltage, power, accFactor);
520
521 if (iterationError < gaussTol) break;
522
523 iteration++;
524 }
525
526 // Newton-Raphson method
527 int numPQ = 0; // Number of PQ buses
528 int numPV = 0; // Number of PV buses
529 GetNumPVPQ(busType, numPQ, numPV);
530
531 // DeltaP and DeltaQ array
532 std::vector<double> dPdQ;
533 dPdQ.resize(numPV + 2 * numPQ, 0.0);
534
535 while (true) {
536 // Reach the max number of iterations.
537 if (iteration >= maxIteration) {
538 m_errorMsg = _("The maximum number of iterations was reached.");
539 return false;
540 }
541
542 // Calculate induction motor reactive power
543 if (!CalculateMotorsReactivePower(voltage, power)) {
544 m_errorMsg = _("It was not possible to solve the induction motors.");
545 return false;
546 }
547
548 // Calculate dPdQ array
549
550 // Fill it with zeros
551 std::fill(dPdQ.begin(), dPdQ.end(), 0.0);
552
553 int indexDP = 0;
554 int indexDQ = numPQ + numPV;
555 for (int i = 0; i < m_numberOfBuses; i++) {
556 if (busType[i] != BUS_SLACK) {
557 for (int j = 0; j < m_numberOfBuses; j++) {
558 // PV ou PQ bus
559 std::complex<double> sInj = std::conj(m_yBus[i][j]) * voltage[i] * std::conj(voltage[j]);
560 dPdQ[indexDP] += sInj.real();
561
562 // PQ bus
563 if (busType[i] == BUS_PQ) dPdQ[indexDQ] += sInj.imag();
564 }
565
566 // PQ or PV bus
567 dPdQ[indexDP] = power[i].real() - dPdQ[indexDP];
568 indexDP++;
569
570 // PQ bus
571 if (busType[i] == BUS_PQ) {
572 dPdQ[indexDQ] = power[i].imag() - dPdQ[indexDQ];
573 indexDQ++;
574 }
575 }
576 }
577
578 // Calculate the iteration error
579 double iterationError = 0.0;
580 for (unsigned int i = 0; i < dPdQ.size(); ++i) {
581 if (iterationError < std::abs(dPdQ[i])) iterationError = std::abs(dPdQ[i]);
582 }
583
584 // Check if the iteration error is less than tolerance, also check if any reactive limit was reached.
585 // If any reactive limit was reached, change the bus type.
586 if (iterationError < error) {
587 double emtPowerError = CalculateEMTPowerError(voltage, power, systemPowerBase, m_errorMsg);
588
589 bool qLimitReached = CheckReactiveLimits(busType, reactiveLimit, power, loadPower);
590 bool tapAdjusted = AdjustTapChangers(voltage, systemPowerBase);
591
592 if (!qLimitReached && !tapAdjusted && emtPowerError < error)
593 break;
594 else {
595 GetNumPVPQ(busType, numPQ, numPV);
596 dPdQ.clear();
597 dPdQ.resize(numPV + 2 * numPQ, 0.0);
598 }
599 }
600
601 NewtonRaphson(busType, voltage, power, numPV, numPQ, dPdQ, inertia);
602
603 iteration++;
604 }
605 m_iterations = iteration;
606
607 // Adjust the power array.
608 for (int i = 0; i < m_numberOfBuses; i++) {
609 std::complex<double> sBus = std::complex<double>(0.0, 0.0);
610 for (int j = 0; j < m_numberOfBuses; j++) sBus += voltage[i] * std::conj(voltage[j]) * std::conj(m_yBus[i][j]);
611 power[i] = sBus;
612 }
613
614 UpdateElementsPowerFlow(voltage, power, oldBusType, reactiveLimit, systemPowerBase);
615
616 return true;
617}
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.
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.

◆ RunGaussSeidel()

bool PowerFlow::RunGaussSeidel ( double  systemPowerBase = 100e6,
int  maxIteration = 5000,
double  error = 1e-6,
double  initAngle = 0.0,
double  accFactor = 1.0 
)
virtual

Definition at line 250 of file PowerFlow.cpp.

255{
256 std::vector<BusType> busType; // Bus type
257 std::vector<std::complex<double> > voltage; // Voltage of buses
258 std::vector<std::complex<double> > power; // Injected power
259 std::vector<std::complex<double> > loadPower; // Only the load power
260 std::vector<ReactiveLimits> reactiveLimit; // Limit of reactive power on PV buses
261
262 if (!InitPowerFlow(busType, voltage, power, loadPower, reactiveLimit, systemPowerBase, initAngle)) return false;
263
264 // Gauss-Seidel method
265 std::vector<std::complex<double> > oldVoltage; // Old voltage array.
266 oldVoltage.resize(voltage.size());
267
268 auto oldBusType = busType;
269
270 int iteration = 0; // Current itaration number.
271 double emtPowerError = 1e3; // EMT Elements power error.
272
273 while (true) {
274 // Reach the max number of iterations.
275 if (iteration >= maxIteration) {
276 m_errorMsg = _("The maximum number of iterations was reached.");
277 return false;
278 }
279
280 // Calculate induction motor reactive power
281 if (!CalculateMotorsReactivePower(voltage, power)) {
282 m_errorMsg = _("It was not possible to solve the induction motors.");
283 return false;
284 }
285
286 // Update the old voltage array to current iteration values.
287 for (int i = 0; i < m_numberOfBuses; i++) oldVoltage[i] = voltage[i];
288
289 double iterationError = GaussSeidel(busType, voltage, oldVoltage, power, accFactor);
290
291 if (HasInvalidValue(voltage)) {
292 m_errorMsg = _("The power flow solution has invalid voltage values.");
293 ResetVoltages();
294 return false;
295 }
296 if (HasInvalidValue(power)) {
297 m_errorMsg = _("The power flow solution has invalid power values.");
298 ResetVoltages();
299 return false;
300 }
301
302 if (iterationError < error) {
303 // Calculate power error in EMT elements.
304 if (!m_emtElementList.empty()) {
305 double oldError = emtPowerError;
306 emtPowerError = CalculateEMTPowerError(voltage, power, systemPowerBase, m_errorMsg);
307 //wxMessageBox(wxString::Format(wxT("It %d, EMT Power Error: %e (base = %e)"), iteration, emtPowerError, error));
308 if (abs(emtPowerError - oldError) < 1e-12) { // Without change in error the convergence is impossible
309 m_errorMsg = _("Impossible to reach a convergence with the current Electromagnetic Transient Elements.");
310 return false;
311 }
312 }
313 else emtPowerError = 0.0;
314
315 bool qLimitReached = CheckReactiveLimits(busType, reactiveLimit, power, loadPower);
316 bool tapAdjusted = AdjustTapChangers(voltage, systemPowerBase);
317
318 if (!qLimitReached && !tapAdjusted && emtPowerError < error) break;
319 }
320
321 iteration++;
322 }
323 m_iterations = iteration;
324
325 // Adjust the power array.
326 for (int i = 0; i < m_numberOfBuses; i++) {
327 std::complex<double> sBus = std::complex<double>(0.0, 0.0);
328 for (int j = 0; j < m_numberOfBuses; j++) sBus += voltage[i] * std::conj(voltage[j]) * std::conj(m_yBus[i][j]);
329 power[i] = sBus;
330 }
331
332
333 UpdateElementsPowerFlow(voltage, power, oldBusType, reactiveLimit, systemPowerBase);
334
335 return true;
336}

◆ RunNewtonRaphson()

bool PowerFlow::RunNewtonRaphson ( double  systemPowerBase = 100e6,
int  maxIteration = 5000,
double  error = 1e-6,
double  initAngle = 0.0,
double  inertia = 1.0 
)
virtual

Definition at line 351 of file PowerFlow.cpp.

356{
357 std::vector<BusType> busType; // Bus type
358 std::vector<std::complex<double> > voltage; // Voltage of buses
359 std::vector<std::complex<double> > power; // Injected power
360 std::vector<std::complex<double> > loadPower; // Only the load power
361 std::vector<ReactiveLimits> reactiveLimit; // Limit of reactive power on PV buses
362
363 if (!InitPowerFlow(busType, voltage, power, loadPower, reactiveLimit, systemPowerBase, initAngle)) return false;
364 auto oldBusType = busType;
365
366 // Newton-Raphson method
367 int numPQ = 0; // Number of PQ buses
368 int numPV = 0; // Number of PV buses
369 GetNumPVPQ(busType, numPQ, numPV);
370
371 // DeltaP and DeltaQ array
372 std::vector<double> dPdQ;
373 dPdQ.resize(numPV + 2 * numPQ, 0.0);
374
375 int iteration = 0; // Current iteration number.
376 while (true) {
377 // Reach the max number of iterations.
378 if (iteration >= maxIteration) {
379 m_errorMsg = _("The maximum number of iterations was reached.");
380 return false;
381 }
382
383 // Calculate induction motor reactive power
384 if (!CalculateMotorsReactivePower(voltage, power)) {
385 m_errorMsg = _("It was not possible to solve the induction motors.");
386 return false;
387 }
388
389 // Calculate dPdQ array
390
391 // Fill it with zeros
392 std::fill(dPdQ.begin(), dPdQ.end(), 0.0);
393
394 int indexDP = 0;
395 int indexDQ = numPQ + numPV;
396 for (int i = 0; i < m_numberOfBuses; i++) {
397 if (busType[i] != BUS_SLACK) {
398 for (int j = 0; j < m_numberOfBuses; j++) {
399 // PV ou PQ bus
400 std::complex<double> sInj = std::conj(m_yBus[i][j]) * voltage[i] * std::conj(voltage[j]);
401 dPdQ[indexDP] += sInj.real();
402
403 // PQ bus
404 if (busType[i] == BUS_PQ) dPdQ[indexDQ] += sInj.imag();
405 }
406
407 // PQ or PV bus
408 dPdQ[indexDP] = power[i].real() - dPdQ[indexDP];
409 indexDP++;
410
411 // PQ bus
412 if (busType[i] == BUS_PQ) {
413 dPdQ[indexDQ] = power[i].imag() - dPdQ[indexDQ];
414 indexDQ++;
415 }
416 }
417 }
418
419 // Calculate the iteration error
420 double iterationError = 0.0;
421 for (unsigned int i = 0; i < dPdQ.size(); ++i) {
422 if (iterationError < std::abs(dPdQ[i])) iterationError = std::abs(dPdQ[i]);
423 }
424
425 // Check if the iteration error is less than tolerance, also check if any reactive limit was reached.
426 // If any reactive limit was reached, change the bus type.
427 if (iterationError < error) {
428 for (int i = 0; i < m_numberOfBuses; i++) {
429 std::complex<double> sBus(0.0, 0.0);
430 for (int j = 0; j < m_numberOfBuses; j++)
431 sBus += voltage[i] * std::conj(voltage[j]) * std::conj(m_yBus[i][j]);
432 power[i] = sBus;
433 }
434
435 double emtPowerError = CalculateEMTPowerError(voltage, power, systemPowerBase, m_errorMsg);
436
437 bool qLimitReached = CheckReactiveLimits(busType, reactiveLimit, power, loadPower);
438 bool tapAdjusted = AdjustTapChangers(voltage, systemPowerBase);
439
440 if (!qLimitReached && !tapAdjusted && emtPowerError < error)
441 break;
442 else {
443 GetNumPVPQ(busType, numPQ, numPV);
444 dPdQ.assign(numPV + 2 * numPQ, 0.0);
445 }
446 }
447
448 NewtonRaphson(busType, voltage, power, numPV, numPQ, dPdQ, inertia);
449
450 if (HasInvalidValue(voltage)) {
451 m_errorMsg = _("The power flow solution has invalid voltage values.");
452 ResetVoltages();
453 return false;
454 }
455 if (HasInvalidValue(power)) {
456 m_errorMsg = _("The power flow solution has invalid power values.");
457 ResetVoltages();
458 return false;
459 }
460
461 iteration++;
462 }
463 m_iterations = iteration;
464
465
466
467 // Adjust the power array.
468 for (int i = 0; i < m_numberOfBuses; i++) {
469 std::complex<double> sBus = std::complex<double>(0.0, 0.0);
470 for (int j = 0; j < m_numberOfBuses; j++) sBus += voltage[i] * std::conj(voltage[j]) * std::conj(m_yBus[i][j]);
471 power[i] = sBus;
472 }
473
474 UpdateElementsPowerFlow(voltage, power, oldBusType, reactiveLimit, systemPowerBase);
475
476 return true;
477}

Member Data Documentation

◆ m_errorMsg

wxString PowerFlow::m_errorMsg = ""
protected

Definition at line 111 of file PowerFlow.h.

◆ m_iterations

int PowerFlow::m_iterations = 0
protected

Definition at line 113 of file PowerFlow.h.

◆ m_numberOfBuses

int PowerFlow::m_numberOfBuses = 0
protected

Definition at line 112 of file PowerFlow.h.

◆ m_tapAdjustmentsCount

int PowerFlow::m_tapAdjustmentsCount = 0
protected

Definition at line 114 of file PowerFlow.h.

◆ m_yBus

std::vector<std::vector<std::complex<double> > > PowerFlow::m_yBus
protected

Definition at line 110 of file PowerFlow.h.


The documentation for this class was generated from the following files: