Power System Platform  2026w11a-beta
Loading...
Searching...
No Matches
Electromechanical Class Reference

Calculates the electromechanical transient based on disturbances (e.g. system fault). More...

#include <Electromechanical.h>

Inheritance diagram for Electromechanical:
Collaboration diagram for Electromechanical:

Public Member Functions

 Electromechanical (wxWindow *parent, std::vector< Element * > elementList, SimulationData data)
 
bool RunStabilityCalculation ()
 
wxString GetErrorMessage () const
 
std::vector< double > GetTimeVector () const
 
std::vector< double > GetIterationVector () const
 
wxString GetDebugMessage () const
 
- 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 SetEventTimeList ()
 
bool HasEvent (double currentTime)
 
void SetEvent (double currentTime)
 
bool EventTrigger (double eventTime, double currentTime)
 
void InsertSyncMachinesOnYBus ()
 
bool InsertIndMachinesOnYBus ()
 
bool CalculateIndMachinesTransientValues (IndMotor *motor)
 
std::complex< double > GetSyncMachineAdmittance (SyncGenerator *generator)
 
std::complex< double > GetIndMachineAdmittance (IndMotor *motor)
 
bool InitializeDynamicElements ()
 
bool CalculateInjectedCurrents ()
 
void CalculateIntegrationConstants (SyncGenerator *syncGenerator, double id, double iq, double k=1.0)
 
void CalculateIntegrationConstants (IndMotor *indMotor, double ir, double im, double k=1.0)
 
bool SolveMachines ()
 
void SetSyncMachinesModel ()
 
SyncMachineModelData GetSyncMachineModelData (SyncGenerator *syncMachine)
 
double CalculateIntVariables (SyncGenerator *syncGenerator, double id, double iq, double sd, double sq, double pe, double k=1.0)
 
double CalculateIntVariables (IndMotor *indMotor, double ir, double im, double te, double k=1.0)
 
bool CalculateNonIntVariables (SyncGenerator *syncGenerator, double &id, double &iq, double &sd, double &sq, double &pe, double k=1.0)
 
bool CalculateNonIntVariables (IndMotor *indMotor, double &ir, double &im, double &te, double k=1.0)
 
void CalculateReferenceSpeed ()
 
bool CalculateSyncMachineSaturation (SyncGenerator *syncMachine, double &id, double &iq, double &sd, double &sq, bool updateCurrents=true, double k=1.0)
 
void CalculateBusesFrequency (bool hasEvent)
 
void SaveData ()
 
void PreallocateVectors ()
 
- 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

wxWindow * m_parent = nullptr
 
wxString m_errorMsg = _("Unknown error")
 
SimulationData m_simData
 
double m_systemFreq = 60.0
 
double m_refSpeed = 2.0 * M_PI * 60.0
 
bool m_useCOI = false
 
std::vector< std::vector< std::complex< double > > > m_yBus
 
std::vector< std::vector< std::complex< double > > > m_yBusU
 
std::vector< std::vector< std::complex< double > > > m_yBusL
 
std::vector< std::complex< double > > m_vBus
 
std::vector< std::complex< double > > m_iBus
 
double m_powerSystemBase = 100e6
 
double m_simTime = 10.0
 
double m_currentTime = 0.0
 
double m_plotTime = 1e-2
 
double m_timeStep = 1e-2
 
double m_ctrlTimeStepMultiplier = 0.1
 
double m_tolerance = 1e-8
 
int m_maxIterations = 100
 
double m_saturationTolerance = 1e-8
 
int m_currentPoint = 0
 
std::vector< double > m_eventTimeList
 
std::vector< bool > m_eventOccurrenceList
 
std::vector< double > m_timeVector
 
int m_iterationsNum = 0.0
 
std::vector< double > m_iterationsNumVector
 
wxString m_debugMessage = ""
 
- 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

Calculates the electromechanical transient based on disturbances (e.g. system fault).

Author
Thales Lima Oliveira thale.nosp@m.s@uf.nosp@m.u.br
Date
23/09/2017

Definition at line 51 of file Electromechanical.h.

Constructor & Destructor Documentation

◆ Electromechanical()

Electromechanical::Electromechanical ( wxWindow *  parent,
std::vector< Element * >  elementList,
SimulationData  data 
)

Definition at line 33 of file Electromechanical.cpp.

34{
35 m_parent = parent;
36 GetElementsFromList(elementList);
37 SetEventTimeList();
38
39 Bus dummyBus;
40 m_simData = data;
41 m_powerSystemBase = dummyBus.GetValueFromUnit(data.basePower, data.basePowerUnit);
42 m_systemFreq = data.stabilityFrequency;
43 m_simTime = data.stabilitySimulationTime;
44 m_timeStep = data.timeStep;
45 m_tolerance = data.stabilityTolerance;
46 m_maxIterations = data.stabilityMaxIterations;
47
48 m_ctrlTimeStepMultiplier = 1.0 / static_cast<double>(data.controlTimeStepRatio);
49
50 m_plotTime = data.plotTime;
51 m_useCOI = data.useCOI;
52 // If the user use all load as ZIP, updates the portions of each model, otherwise use constant impedance only.
53 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
54 Load* load = *it;
55 auto& loadData = load->GetElectricalDataRef();
56 if (!loadData.useCompLoad) { // If no individual load composition defined.
57 if (data.useCompLoads) { // Use general composition, if defined.
58 loadData.constImpedanceActive = data.constImpedanceActive;
59 loadData.constCurrentActive = data.constCurrentActive;
60 loadData.constPowerActive = data.constPowerActive;
61 loadData.constImpedanceReactive = data.constImpedanceReactive;
62 loadData.constCurrentReactive = data.constCurrentReactive;
63 loadData.constPowerReactive = data.constPowerReactive;
64 }
65 else { // Otherwise, use constant impedance.
66 loadData.constImpedanceActive = 100.0;
67 loadData.constCurrentActive = 0.0;
68 loadData.constPowerActive = 0.0;
69 loadData.constImpedanceReactive = 100.0;
70 loadData.constCurrentReactive = 0.0;
71 loadData.constPowerReactive = 0.0;
72 }
73 }
74
75 loadData.constCurrentUV = data.underVoltageConstCurrent / 100.0;
76 loadData.constPowerUV = data.underVoltageConstPower / 100.0;
77 //load->SetElectricalData(loadData);
78 }
79}
Node for power elements. All others power elements are connected through this.
Definition Bus.h:86
std::vector< Load * > m_loadList
List of load elements in the system.
virtual void GetElementsFromList(std::vector< Element * > elementList)
Separate the power elements from a generic list.
Loas shunt power element.
Definition Load.h:74

◆ ~Electromechanical()

Electromechanical::~Electromechanical ( )

Definition at line 81 of file Electromechanical.cpp.

81{ }

Member Function Documentation

◆ CalculateBusesFrequency()

void Electromechanical::CalculateBusesFrequency ( bool  hasEvent)
protected

Definition at line 1819 of file Electromechanical.cpp.

1820{
1821 bool ignoreEventStep = true;
1822 for (auto& bus : m_busList) {
1823 auto& data = bus->GetElectricalDataRef();
1824 if (!data.plotBus) continue;
1825
1826 if (m_simData.busFreqEstimation == BusFreqEstimation::WASHOUT_FILTER) {
1827 //tex:
1828 // Low-pass filter:
1829 // $$\frac{d}{dt}x_{\theta} = x_{\theta}' = \frac{\omega^{-1}(\theta_k - \theta_0) - x_{\theta}}{T_f}$$
1830 // Washout filter:
1831 // $$\frac{d}{dt}\Delta\omega_{k} = \Delta\omega_{k}' = \frac{x_{\theta}' - \Delta\omega_{k}}{T_w}$$
1832 // Trapezoidal rule:
1833 // $$ y_{k} = y_{k-1} + 0.5 h \cdot (y_{k-1}'+y_{k}') $$
1834
1835 double tf = m_simData.tf;
1836 double tw = m_simData.tw;
1837 double error = 1.0;
1838 double theta0 = std::arg(data.number >= 0 ? data.voltage : 0.0);
1839
1840 // Low-pass filter
1841 auto fdxt = [this, tf, theta0](double theta, double xt) {
1842 return (1.0 / tf) * ((1.0 / (2.0 * M_PI * m_systemFreq)) * (theta - theta0) - xt);
1843 };
1844
1845 // Washout filter
1846 auto fddw = [this, tw](double dxt, double dw) {
1847 return (1.0 / tw) * (dxt - dw);
1848 };
1849
1850 double theta = std::arg(data.number >= 0 ? m_vBus[data.number] : theta0);
1851
1852 double xt = 0, dw = 0, dxt = 0, ddw = 0;
1853 int iterations = 0;
1854 while (error > 1e-6 && iterations <= 100) {
1855 double oldXT = xt, oldDW = dw;
1856
1857 dxt = fdxt(theta, xt);
1858 xt = data.filteredAngle + 0.5 * m_timeStep * (data.dxt + dxt); // Trapezoidal rule
1859 error = std::abs(xt - oldXT);
1860
1861 ddw = fddw(dxt, dw);
1862 dw = data.filteredVelocity + 0.5 * m_timeStep * (data.ddw + ddw); // Trapezoidal rule
1863 error = std::max(error, std::abs(dw - oldDW));
1864 iterations++;
1865 }
1866
1867 //if (ignoreEventStep && hasEvent) {
1868 // xt = data.filteredAngle;
1869 // dw = 0.0;
1870 // dxt = 0.0;
1871 // ddw = 0.0;
1872 //}
1873
1874 data.filteredAngle = xt;
1875 data.filteredVelocity = dw;
1876 data.dxt = dxt;
1877 data.ddw = ddw;
1878
1879 double busVelocity = m_refSpeed + dw * (2.0 * M_PI * m_systemFreq);
1880 data.stabFreq = busVelocity / (2.0 * M_PI);
1881
1882 //bus->SetElectricalData(data);
1883 }
1884 else if (m_simData.busFreqEstimation == BusFreqEstimation::ANGLE_DERIVATION) {
1885 double newAngle = std::arg(data.number >= 0 ? m_vBus[data.number] : 0.0);
1886
1887
1888 //tex: $$\Delta \omega^k=\frac{\theta^k - \theta^{k-1}}{h}$$
1889 double dw = (newAngle - data.oldAngle) / m_timeStep;
1890 //double dw = (1.0 / (2.0 * M_PI * m_systemFreq)) * ((newAngle - data.oldAngle) / m_timeStep); // p.u.
1891
1892 if (m_simData.ignoreBusFreqEventStep && hasEvent) dw = 0.0;
1893
1894 //tex: $$f^k= \frac{\omega_{CoI}+\Delta \omega^k}{2\pi}$$
1895 data.stabFreq = (m_refSpeed + dw) / (2.0 * M_PI);
1896 //data.stabFreq = ((2.0 * M_PI * m_systemFreq) + dw) / (2.0 * M_PI);
1897
1898 data.oldAngle = newAngle;
1899 }
1900 bus->SetElectricalData(data);
1901 }
1902}
std::vector< Bus * > m_busList
List of buses in the system.

◆ CalculateIndMachinesTransientValues()

bool Electromechanical::CalculateIndMachinesTransientValues ( IndMotor motor)
protected

Definition at line 2031 of file Electromechanical.cpp.

2032{
2033 auto& data = motor->GetElectricalDataRef();
2034 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
2035 double k = 1.0; // Power base change factor.
2036 if (data.useMachinePowerAsBase) {
2037 double oldBase = motor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
2038 k = m_powerSystemBase / oldBase;
2039 }
2040
2041 data.terminalVoltage = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().voltage;
2042
2043 // Calculate the induction machine transient constants at the machine base
2044 double r1t = data.r1 * k;
2045 double r2t = data.r2 * k;
2046 double x1t = data.x1 * k;
2047 double x2t = data.x2 * k;
2048 double xmt = data.xm * k;
2049 data.r1t = r1t;
2050 data.r2t = r2t;
2051 data.x1t = x1t;
2052 data.x2t = x2t;
2053 data.xmt = xmt;
2054
2055 double xt = x1t + (x2t * xmt) / (x2t + xmt);
2056 double x0 = x1t + xmt;
2057 data.xt = xt;
2058 data.x0 = x0;
2059
2060 double p = dataPU.activePower;
2061 double v = std::abs(data.terminalVoltage);
2062
2063 //[Ref.] Induction Motor Static Models for Power Flow and Voltage stability Studies
2064 // If the motor is offline, calculate the nominal slip to user-defined power input and 1.0 p.u. voltage
2065 if (!motor->IsOnline()) v = 1.0;
2066 double r1 = data.r1t;
2067 double r2 = data.r2t;
2068 if (data.useKf) r2 *= (1.0 + data.kf * r2t);
2069 double x1 = data.x1t;
2070 double x2 = data.x2t;
2071 double xm = data.xmt;
2072 double k1 = x2 + xm;
2073 double k2 = -x1 * k1 - x2 * xm;
2074 double k3 = xm + x1;
2075 double k4 = r1 * k1;
2076 double a = p * (r1 * r1 + k3 * k3) - v * v * r1;
2077 double b = 2.0 * p * (r1 * k2 + k3 * k4) - v * v * (k2 + k1 * k3);
2078 double c = p * (k2 * k2 + k4 * k4) - v * v * k1 * k4;
2079 double d = (b * b - 4.0 * a * c);
2080 if (d < 0.0) {
2081 m_errorMsg = _("Error on initializate the slip of \"") + data.name + _("\".");
2082 return false;
2083 }
2084 double r2_s = (-b + std::sqrt(d)) / (2.0 * a);
2085 data.s0 = r2 / r2_s;
2086
2087 double qa = k1 * (r2_s * r1 - x1 * k1 - x2 * xm);
2088 double qb = r2_s * (r2_s * (xm + x1) + r1 * k1);
2089 double qc = r2_s * r1 - x1 * k1 - x2 * xm;
2090 double qd = r2_s * (xm + x1) + r1 * k1;
2091 data.q0 = (-v * v * (qa - qb)) / (qc * qc + qd * qd);
2092
2093 data.t0 = (x2t + xmt) / (2.0 * M_PI * m_systemFreq * r2);
2094
2095 //motor->SetElectricalData(data);
2096 return true;
2097}
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

◆ CalculateInjectedCurrents()

bool Electromechanical::CalculateInjectedCurrents ( )
protected

Definition at line 959 of file Electromechanical.cpp.

960{
961 // Reset injected currents vector
962 for (unsigned int i = 0; i < m_iBus.size(); ++i) m_iBus[i] = std::complex<double>(0.0, 0.0);
963
964 // Synchronous machines
965 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
966 SyncGenerator* syncGenerator = *it;
967 auto& data = syncGenerator->GetElectricalDataRef();
968 if (syncGenerator->IsOnline()) {
969 double k = 1.0; // Power base change factor.
970 if (data.useMachineBase) {
971 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
972 k = m_powerSystemBase / oldBase;
973 }
974
975 double ra = data.armResistance * k;
976 double xp = data.potierReactance * k;
977 if (xp == 0.0) xp = 0.8 * data.transXd * k;
978
979 int n = static_cast<Bus*>(syncGenerator->GetParentList()[0])->GetElectricalDataRef().number;
980 std::complex<double> e = std::complex<double>(0.0, 0.0);
981 std::complex<double> v = m_vBus[n];
982 std::complex<double> iInj = m_iBus[n];
983
984 auto smModelData = GetSyncMachineModelData(syncGenerator);
985 DQ0toABC(smModelData.ed, smModelData.eq, data.delta, e);
986 double xd = smModelData.xd;
987 double xq = smModelData.xq;
988
989 double sd = data.sd;
990 double sq = data.sq;
991 double id, iq;
992
993 // Calculate the saturation effect
994 if (data.satFactor != 0.0) {
995 if (!CalculateSyncMachineSaturation(syncGenerator, id, iq, sd, sq, false, k)) return false;
996 }
997
998 double xdq, xds, xqs, xdqs;
999 xdq = 0.5 * (xd + xq);
1000 xds = (xd - xp) / sd + xp;
1001 xqs = (xq - xp) / sq + xp;
1002 xdqs = 0.5 * (xds + xqs);
1003
1004 std::complex<double> y0 = std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0);
1005 std::complex<double> iUnadjusted = y0 * v;
1006
1007 // [Ref] Arrillaga, J.; Arnold, C. P.. "Computer Modelling of Electrical Power Systems". Pg. 225-226
1008 // [Ref] Dommell, H. W.; Sato, N.. "Fast transient stability solutions". IEEE Transactions on Power
1009 // Apparatus and Systems, PAS-91 (4), 1643-1650
1010 std::complex<double> iSaliency = std::complex<double>(0.0, -((0.5 * (xqs - xds)) / (ra * ra + xds * xqs))) *
1011 (std::conj(e) - std::conj(v));
1012 iSaliency = iSaliency * std::cos(2.0 * data.delta) +
1013 iSaliency * std::complex<double>(0.0, std::sin(2.0 * data.delta));
1014
1015 // [Ref] Arrillaga, J.; Arnold, C. P.; Computer Modelling of Electrical Power Systems. Pg. 258-259
1016 std::complex<double> y0s = std::complex<double>(ra, -xdqs) / std::complex<double>(ra * ra + xds * xqs, 0.0);
1017 std::complex<double> iSaturation = y0s * (e - v);
1018
1019 iInj = iUnadjusted + iSaliency + iSaturation;
1020
1021 m_iBus[n] += iInj;
1022
1023 // Remove the current flowing through y0 (i.e. iUnadjusted in this case, y0 is inserted in admittance
1024 // matrix) to calculate the electrical power.
1025 std::complex<double> iMachine = iInj - iUnadjusted;
1026 data.electricalPower = v * std::conj(iMachine);
1027
1028 ABCtoDQ0(iMachine, data.delta, id, iq);
1029
1030 data.id = id;
1031 data.iq = iq;
1032 data.sd = sd;
1033 data.sq = sq;
1034 }
1035 else {
1036 data.electricalPower = std::complex<double>(0.0, 0.0);
1037 }
1038
1039 //syncGenerator->SetElectricalData(data);
1040 }
1041 // Induction motors
1042 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1043 IndMotor* motor = *it;
1044 auto& data = motor->GetElectricalDataRef();
1045 if (motor->IsOnline()) {
1046 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().number;
1047 std::complex<double> y0 = std::complex<double>(1.0, 0.0) / std::complex<double>(data.r1t, data.xt);
1048 std::complex<double> v = m_vBus[n];
1049 std::complex<double> e = std::complex<double>(data.tranEr, data.tranEm);
1050 std::complex<double> iInj = y0 * e;
1051 m_iBus[n] += iInj;
1052
1053 std::complex<double> iMachine = y0 * (v - e);
1054
1055 data.ir = std::real(iMachine);
1056 data.im = std::imag(iMachine);
1057 }
1058 else {
1059 data.ir = 0.0;
1060 data.im = 0.0;
1061 }
1062 //motor->SetElectricalData(data);
1063 }
1064
1065 // Loads
1066 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
1067 Load* load = *it;
1068 auto& data = load->GetElectricalDataRef();
1069 Bus* bus = nullptr;
1070 if (!GetParentBus(load, bus)) {
1071 load->SetOnline(false);
1072 }
1073 else if (load->IsOnline()) {
1074
1075 data.voltage = m_vBus[bus->GetElectricalDataRef().number];
1076 double vAbs = std::abs(data.voltage);
1077
1078 double pz, pi, pp, qz, qi, qp;
1079 pz = data.pz0 * std::pow(vAbs / data.v0, 2);
1080 pi = data.pi0 * (vAbs / data.v0);
1081 pp = data.pp0;
1082 qz = data.qz0 * std::pow(vAbs / data.v0, 2);
1083 qi = data.qi0 * (vAbs / data.v0);
1084 qp = data.qp0;
1085
1086 // If voltage value is low, set the ZIP load to constant impedance.
1087 if (vAbs < data.constCurrentUV) {
1088 pi = data.pi0 * (data.constCurrentUV / data.v0) * std::pow(vAbs / data.constCurrentUV, 2);
1089 qi = data.qi0 * (data.constCurrentUV / data.v0) * std::pow(vAbs / data.constCurrentUV, 2);
1090 }
1091 if (vAbs < data.constPowerUV) {
1092 pp *= std::pow(vAbs / data.constPowerUV, 2);
1093 qp *= std::pow(vAbs / data.constPowerUV, 2);
1094 }
1095
1096 double activePower = pz + pi + pp;
1097 double reactivePower = qz + qi + qp;
1098
1099 std::complex<double> newY = std::complex<double>(activePower, -reactivePower) / (vAbs * vAbs);
1100 m_iBus[bus->GetElectricalDataRef().number] += (data.y0 - newY) * data.voltage;
1101
1102 data.electricalPower = std::complex<double>(activePower, reactivePower);
1103 }
1104 else {
1105 data.voltage = std::complex<double>(0.0, 0.0);
1106 data.electricalPower = std::complex<double>(0.0, 0.0);
1107 }
1108
1109 //load->SetElectricalData(data);
1110 }
1111 return true;
1112}
void ABCtoDQ0(std::complex< double > complexValue, double angle, double &dValue, double &qValue)
Convert a complex phasor in ABC representation to DQ components.
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< SyncGenerator * > m_syncGeneratorList
List of synchronous generators in the system.
void DQ0toABC(double dValue, double qValue, double angle, std::complex< double > &complexValue)
Convert DQ components to a complex phasor in ABC representation.
bool SetOnline(bool online=true)
Set if the element is online or offline.
Definition Element.cpp:378
Induction motor power element.
Definition IndMotor.h:119
Synchronous generator power element.

◆ CalculateIntegrationConstants() [1/2]

void Electromechanical::CalculateIntegrationConstants ( IndMotor indMotor,
double  ir,
double  im,
double  k = 1.0 
)
protected

Definition at line 1186 of file Electromechanical.cpp.

1187{
1188 double w0 = 2.0 * M_PI * m_systemFreq;
1189
1190 auto& data = indMotor->GetElectricalDataRef();
1191
1192 // If the velocity is too low set mechanical torque to zero (a, b and c coeficients)
1193 if (data.slip > 0.99999) {
1194 data.as = 0.0;
1195 data.bs = 0.0;
1196 data.cs = 0.0;
1197 }
1198 else {
1199 data.as = data.aCalc;
1200 data.bs = data.bCalc;
1201 data.cs = data.cCalc;
1202 }
1203
1204 // Mechanical equations
1205 // s
1206 data.icSlip.m = m_timeStep / ((4.0f * data.inertia / w0) / k + data.bs * m_timeStep);
1207 data.icSlip.c = data.slip * (1.0 - 2.0 * data.bs * data.icSlip.m) +
1208 data.icSlip.m * (2.0 * data.as + data.cs * data.slip * data.slip - data.te);
1209
1210 // Electrical equations
1211 // Er
1212 data.icTranEr.m = m_timeStep / (2.0 * data.t0 + m_timeStep);
1213 data.icTranEr.c = data.tranEr * (1.0 - 2.0 * data.icTranEr.m) +
1214 data.icTranEr.m * (w0 * data.t0 * data.slip * data.tranEm - (data.x0 - data.xt) * im);
1215 // Em
1216 data.icTranEm.m = m_timeStep / (2.0 * data.t0 + m_timeStep);
1217 data.icTranEm.c = data.tranEm * (1.0 - 2.0 * data.icTranEm.m) -
1218 data.icTranEm.m * (w0 * data.t0 * data.slip * data.tranEr - (data.x0 - data.xt) * ir);
1219
1220 //indMotor->SetElectricalData(data);
1221}

◆ CalculateIntegrationConstants() [2/2]

void Electromechanical::CalculateIntegrationConstants ( SyncGenerator syncGenerator,
double  id,
double  iq,
double  k = 1.0 
)
protected

Definition at line 1114 of file Electromechanical.cpp.

1115{
1116 CalculateReferenceSpeed();
1117 auto& data = syncGenerator->GetElectricalDataRef();
1118
1119 double syncXd, syncXq, transXd, transXq, subXd, subXq;
1120 syncXd = data.syncXd * k;
1121 syncXq = data.syncXq * k;
1122 transXd = data.transXd * k;
1123 transXq = data.transXq * k;
1124 subXd = data.subXd * k;
1125 subXq = data.subXq * k;
1126
1127 if (syncXq == 0.0) syncXq = syncXd;
1128 if (transXq == 0.0) transXq = transXd;
1129 if (subXd == 0.0) subXd = subXq;
1130 if (subXq == 0.0) subXq = subXd;
1131
1132 double transTd0, transTq0, subTd0, subTq0;
1133 transTd0 = data.transTd0;
1134 transTq0 = data.transTq0;
1135 subTd0 = data.subTd0;
1136 subTq0 = data.subTq0;
1137
1138 if (subTd0 == 0.0) subTd0 = subTq0;
1139 if (subTq0 == 0.0) subTq0 = subTd0;
1140
1141 // Speed
1142 data.icSpeed.m = m_timeStep / ((4.0f * data.inertia / m_refSpeed) / k + m_timeStep * data.damping * k);
1143 data.icSpeed.c = (1.0f - 2.0f * data.icSpeed.m * data.damping * k) * data.speed +
1144 data.icSpeed.m * (data.pm - data.pe + 2.0f * m_refSpeed * data.damping * k);
1145
1146 // Delta
1147 data.icDelta.m = 0.5f * m_timeStep;
1148 data.icDelta.c = data.delta + data.icDelta.m * (data.speed - 2.0f * m_refSpeed);
1149
1150 // Eq'
1151 if (data.model == Machines::SM_MODEL_2 || data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 ||
1152 data.model == Machines::SM_MODEL_5) {
1153 data.icTranEq.m = m_timeStep / (2.0f * transTd0 + m_timeStep);
1154 data.icTranEq.c = (1.0 - data.icTranEq.m * (1.0 + data.sd)) * data.tranEq +
1155 data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id);
1156 }
1157
1158 // Ed'
1159 if (data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
1160 data.icTranEd.m = m_timeStep / (2.0f * transTq0 + m_timeStep);
1161 data.icTranEd.c =
1162 (1.0 - data.icTranEd.m * (1.0 + data.sq)) * data.tranEd - data.icTranEd.m * (syncXq - transXq) * iq;
1163 }
1164
1165 // Eq''
1166 if (data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
1167 data.icSubEq.m = m_timeStep / (2.0f * subTd0 + m_timeStep);
1168 data.icSubEq.c = (1.0 - data.icSubEq.m * (1.0 + data.sd)) * data.subEq +
1169 data.icSubEq.m * (data.sd * data.tranEq + (transXd - subXd) * id);
1170 }
1171 // Ed''
1172 if (data.model == Machines::SM_MODEL_4) {
1173 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1174 data.icSubEd.c =
1175 (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd - data.icSubEd.m * (syncXq - subXq) * iq;
1176 }
1177 if (data.model == Machines::SM_MODEL_5) {
1178 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1179 data.icSubEd.c = (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd +
1180 data.icSubEd.m * (data.sq * data.tranEd - (transXq - subXq) * iq);
1181 }
1182
1183 //syncGenerator->SetElectricalData(data);
1184}

◆ CalculateIntVariables() [1/2]

double Electromechanical::CalculateIntVariables ( IndMotor indMotor,
double  ir,
double  im,
double  te,
double  k = 1.0 
)
protected

Definition at line 1654 of file Electromechanical.cpp.

1655{
1656 double error = 0.0;
1657 auto& data = indMotor->GetElectricalDataRef();
1658 double w0 = 2.0 * M_PI * m_systemFreq;
1659
1660 // Mechanical differential equations
1661 // Using Newton method to solve the non-linear slip equation: s = Cs + Ms * (C * s^2 - Te):
1662 double slip = data.slip; // Initial value. CAN BE THE PROBLEM ON MOTOR START!
1663 double ds = (data.icSlip.c + data.icSlip.m * (data.cs * slip * slip - te) - slip) /
1664 (1.0 - 2.0 * data.icSlip.m * data.cs * slip * slip);
1665 int iteration = 0;
1666 while (std::abs(ds) > 1e-8) {
1667 slip += ds;
1668 ds = (data.icSlip.c + data.icSlip.m * (data.cs * slip * slip - te) - slip) /
1669 (1.0 - 2.0 * data.icSlip.m * data.cs * slip * slip);
1670 iteration++;
1671 if (iteration > m_maxIterations) break;
1672 }
1673
1674 //if(!indMotor->IsOnline()) slip = 1.0 - 1e-7;
1675 error = std::max(error, std::abs(data.slip - slip));
1676 data.slip = slip;
1677
1678 // Change T'0 with the cage factor
1679 if (data.useKf)
1680 data.t0 = (data.x2t + data.xmt) / (2.0 * M_PI * m_systemFreq * data.r2t * (1.0 + data.kf * data.r2t));
1681
1682 // Electrical differential equations
1683 double tranEr = data.icTranEr.c + data.icTranEr.m * (w0 * data.t0 * slip * data.tranEm - (data.x0 - data.xt) * im);
1684 error = std::max(error, std::abs(data.tranEr - tranEr));
1685
1686 double tranEm = data.icTranEm.c - data.icTranEm.m * (w0 * data.t0 * slip * data.tranEr - (data.x0 - data.xt) * ir);
1687 error = std::max(error, std::abs(data.tranEm - tranEm));
1688
1689 data.tranEr = tranEr;
1690 data.tranEm = tranEm;
1691
1692 //indMotor->SetElectricalData(data);
1693 return error;
1694}

◆ CalculateIntVariables() [2/2]

double Electromechanical::CalculateIntVariables ( SyncGenerator syncGenerator,
double  id,
double  iq,
double  sd,
double  sq,
double  pe,
double  k = 1.0 
)
protected

Definition at line 1524 of file Electromechanical.cpp.

1531{
1532 double error = 0.0;
1533 auto& data = syncGenerator->GetElectricalDataRef();
1534
1535 // Mechanical differential equations.
1536 double w = data.icSpeed.c + data.icSpeed.m * (data.pm - pe);
1537 error = std::max(error, std::abs(data.speed - w) / m_refSpeed);
1538
1539 double delta = data.icDelta.c + data.icDelta.m * w;
1540 error = std::max(error, std::abs(data.delta - delta));
1541
1542 data.speed = w;
1543 data.delta = delta;
1544
1545 // Electrical differential equations
1546 switch (data.model) {
1547 case Machines::SM_MODEL_1: {
1548 // There is no differential equations.
1549 } break;
1550 case Machines::SM_MODEL_2: {
1551 double syncXd, transXd;
1552 syncXd = data.syncXd * k;
1553 transXd = data.transXd * k;
1554
1555 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
1556 (1.0 + data.icTranEq.m * (sd - 1.0));
1557 error = std::max(error, std::abs(data.tranEq - tranEq));
1558
1559 data.tranEq = tranEq;
1560 } break;
1561 case Machines::SM_MODEL_3: {
1562 double syncXd, syncXq, transXd, transXq;
1563 syncXd = data.syncXd * k;
1564 syncXq = data.syncXq * k;
1565 transXd = data.transXd * k;
1566 transXq = data.transXq * k;
1567 if (syncXq == 0.0) syncXq = syncXd;
1568 if (transXq == 0.0) transXq = transXd;
1569
1570 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
1571 (1.0 + data.icTranEq.m * (sd - 1.0));
1572 error = std::max(error, std::abs(data.tranEq - tranEq));
1573
1574 double tranEd =
1575 (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
1576 error = std::max(error, std::abs(data.tranEd - tranEd));
1577
1578 data.tranEq = tranEq;
1579 data.tranEd = tranEd;
1580
1581 if (!syncGenerator->IsOnline()) {
1582 std::complex<double> e;
1583 DQ0toABC(data.tranEd, data.tranEq, data.delta, e);
1584 data.terminalVoltage = e;
1585 }
1586 } break;
1587 case Machines::SM_MODEL_4: {
1588 double syncXd, syncXq, transXd, subXd, subXq;
1589 syncXd = data.syncXd * k;
1590 syncXq = data.syncXq * k;
1591 transXd = data.transXd * k;
1592 subXd = data.subXd * k;
1593 subXq = data.subXq * k;
1594 if (syncXq == 0.0) syncXq = syncXd;
1595 if (subXd == 0.0) subXd = subXq;
1596 if (subXq == 0.0) subXq = subXd;
1597
1598 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
1599 (1.0 + data.icTranEq.m * (sd - 1.0));
1600 error = std::max(error, std::abs(data.tranEq - tranEq));
1601
1602 double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) * id)) /
1603 (1.0 + data.icSubEq.m * (sd - 1.0));
1604 error = std::max(error, std::abs(data.subEq - subEq));
1605
1606 double subEd =
1607 (data.icSubEd.c - data.icSubEd.m * ((syncXq - subXq) * iq)) / (1.0 + data.icSubEd.m * (sq - 1.0));
1608 error = std::max(error, std::abs(data.subEd - subEd));
1609
1610 data.tranEq = tranEq;
1611 data.subEq = subEq;
1612 data.subEd = subEd;
1613 } break;
1614 case Machines::SM_MODEL_5: {
1615 double syncXd, syncXq, transXd, transXq, subXd, subXq;
1616 syncXd = data.syncXd * k;
1617 syncXq = data.syncXq * k;
1618 transXd = data.transXd * k;
1619 transXq = data.transXq * k;
1620 subXd = data.subXd * k;
1621 subXq = data.subXq * k;
1622 if (syncXq == 0.0) syncXq = syncXd;
1623 if (transXq == 0.0) transXq = transXd;
1624 if (subXd == 0.0) subXd = subXq;
1625 if (subXq == 0.0) subXq = subXd;
1626
1627 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
1628 (1.0 + data.icTranEq.m * (sd - 1.0));
1629 error = std::max(error, std::abs(data.tranEq - tranEq));
1630
1631 double tranEd =
1632 (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
1633 error = std::max(error, std::abs(data.tranEd - tranEd));
1634
1635 double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) * id)) /
1636 (1.0 + data.icSubEq.m * (sd - 1.0));
1637 error = std::max(error, std::abs(data.subEq - subEq));
1638
1639 double subEd = (data.icSubEd.c + data.icSubEd.m * (sq * tranEd - (transXq - subXq) * iq)) /
1640 (1.0 + data.icSubEd.m * (sq - 1.0));
1641 error = std::max(error, std::abs(data.subEd - subEd));
1642
1643 data.tranEq = tranEq;
1644 data.tranEd = tranEd;
1645 data.subEq = subEq;
1646 data.subEd = subEd;
1647 } break;
1648 }
1649
1650 //syncGenerator->SetElectricalData(data);
1651 return error;
1652}

◆ CalculateNonIntVariables() [1/2]

bool Electromechanical::CalculateNonIntVariables ( IndMotor indMotor,
double &  ir,
double &  im,
double &  te,
double  k = 1.0 
)
protected

Definition at line 1503 of file Electromechanical.cpp.

1504{
1505 auto& data = indMotor->GetElectricalDataRef();
1506 if (indMotor->IsOnline()) {
1507 double w0 = 2.0 * M_PI * m_systemFreq;
1508 te = (data.tranEr * ir + data.tranEm * im) / w0;
1509 }
1510 else {
1511 te = ir = im = 0.0;
1512 }
1513 data.te = te;
1514 data.ir = ir;
1515 data.im = im;
1516 data.oldTe = te;
1517 data.oldIr = ir;
1518 data.oldIm = im;
1519 //indMotor->SetElectricalData(data);
1520
1521 return true;
1522}

◆ CalculateNonIntVariables() [2/2]

bool Electromechanical::CalculateNonIntVariables ( SyncGenerator syncGenerator,
double &  id,
double &  iq,
double &  sd,
double &  sq,
double &  pe,
double  k = 1.0 
)
protected

Definition at line 1463 of file Electromechanical.cpp.

1470{
1471 auto& data = syncGenerator->GetElectricalDataRef();
1472 Bus* bus = nullptr;
1473 if (GetParentBus(syncGenerator, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalDataRef().number]; }
1474
1475 double vd, vq;
1476 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1477
1478 if (data.satFactor != 0.0) {
1479 if (!CalculateSyncMachineSaturation(syncGenerator, id, iq, sd, sq, true, k)) return false;
1480 data.sd = sd;
1481 data.sq = sq;
1482 data.oldSd = sd;
1483 data.oldSq = sq;
1484 }
1485
1486 if (syncGenerator->IsOnline()) {
1487 pe = id * vd + iq * vq + (id * id + iq * iq) * data.armResistance * k;
1488 }
1489 else {
1490 pe = id = iq = 0.0f;
1491 }
1492 data.pe = pe;
1493 data.id = id;
1494 data.iq = iq;
1495 data.oldPe = pe;
1496 data.oldId = id;
1497 data.oldIq = iq;
1498 //syncGenerator->SetElectricalData(data);
1499
1500 return true;
1501}

◆ CalculateReferenceSpeed()

void Electromechanical::CalculateReferenceSpeed ( )
protected

Definition at line 1696 of file Electromechanical.cpp.

1697{
1698 if (m_useCOI) {
1699 double sumHW = 0.0;
1700 double sumH = 0.0;
1701 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1702 SyncGenerator* syncGenerator = *it;
1703 if (syncGenerator->IsOnline()) {
1704 auto& data = syncGenerator->GetElectricalDataRef();
1705 double k = 1.0; // Power base change factor.
1706 if (data.useMachineBase) {
1707 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1708 k = m_powerSystemBase / oldBase;
1709 }
1710 sumH += data.inertia / k;
1711 sumHW += data.inertia * data.speed / k;
1712 }
1713 }
1714 m_refSpeed = sumHW / sumH;
1715 }
1716 else {
1717 m_refSpeed = 2.0 * M_PI * m_systemFreq;
1718 }
1719}

◆ CalculateSyncMachineSaturation()

bool Electromechanical::CalculateSyncMachineSaturation ( SyncGenerator syncMachine,
double &  id,
double &  iq,
double &  sd,
double &  sq,
bool  updateCurrents = true,
double  k = 1.0 
)
protected

Definition at line 1721 of file Electromechanical.cpp.

1728{
1729 // [Ref] Arrillaga, J.; Arnold, C. P.. "Computer Modelling of Electrical Power Systems". Pg. 254-260
1730 auto& data = syncMachine->GetElectricalDataRef();
1731 auto smDataModel = GetSyncMachineModelData(syncMachine);
1732
1733 Bus* bus = nullptr;
1734 if (GetParentBus(syncMachine, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalDataRef().number]; }
1735 //else {
1736 //data.terminalVoltage = 0.0;
1737 //return true;
1738 //}
1739 double idCalc = id;
1740 double iqCalc = iq;
1741 double sdCalc = sd;
1742 double sqCalc = sq;
1743
1744 double vd, vq;
1745 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1746 double deltaVd = smDataModel.ed - vd;
1747 double deltaVq = smDataModel.eq - vq;
1748
1749 double ra = data.armResistance * k;
1750 double xd = smDataModel.xd;
1751 double xq = smDataModel.xq;
1752
1753 double syncXd = data.syncXd * k;
1754 double syncXq = data.syncXq * k;
1755 if (data.model == Machines::SM_MODEL_1) {
1756 syncXq = data.transXd * k;
1757 syncXd = syncXq;
1758 }
1759 else if (data.syncXq == 0.0)
1760 syncXq = data.syncXd * k;
1761
1762 double xp = data.potierReactance * k;
1763 if (xp == 0.0) xp = 0.8 * data.transXd * k;
1764 double satFacd = (data.satFactor - 1.2) / std::pow(1.2, 7);
1765 double satFacq = satFacd * (syncXq / syncXd);
1766
1767 bool exit = false;
1768 int iterations = 0;
1769 while (!exit) {
1770 double oldSd = sdCalc;
1771 double oldSq = sqCalc;
1772
1773 // Saturated reactances.
1774 double xds = (xd - xp) / sdCalc + xp;
1775 double xqs = (xq - xp) / sqCalc + xp;
1776 // dq currents.
1777 double den = 1.0 / (ra * ra + xds * xqs);
1778 iqCalc = den * (ra * deltaVq + xds * deltaVd);
1779 idCalc = den * (-xqs * deltaVq + ra * deltaVd);
1780 // Potier voltages
1781 double epq = vq + ra * iqCalc - xp * idCalc;
1782 double epd = vd + ra * idCalc + xp * iqCalc;
1783 // Saturation factors.
1784 // Gauss
1785 /*sdCalc = 1.0 + satFacd * std::pow(epq, 6);
1786 sqCalc = 1.0 + satFacq * std::pow(epd, 6);*/
1787
1788 // Newton-raphson
1789 double f1 = 1.0 - sdCalc + satFacd * std::pow(epq, 6);
1790 double f2 = 1.0 - sqCalc + satFacq * std::pow(epd, 6);
1791 double dF1dSd =
1792 (6.0 * satFacd * std::pow(epq, 5) * xp * (xd - xp) * deltaVq) / ((sdCalc - 1.0) * xp + xd) - 1.0;
1793 double dF2dSq =
1794 (6.0 * satFacq * std::pow(epd, 5) * xp * (xq - xp) * deltaVd) / ((sqCalc - 1.0) * xp + xq) - 1.0;
1795
1796 sdCalc = sdCalc - f1 / dF1dSd;
1797 sqCalc = sqCalc - f2 / dF2dSq;
1798
1799 double error = std::abs(sdCalc - oldSd) + std::abs(sqCalc - oldSq);
1800 if (error < m_saturationTolerance) exit = true;
1801
1802 iterations++;
1803 if ((iterations >= m_maxIterations) && !exit) {
1804 m_errorMsg =
1805 _("It was not possible to solve the saturation of the synchronous machine \"") + data.name + wxT("\".");
1806 return false;
1807 }
1808 }
1809
1810 sd = sdCalc;
1811 sq = sqCalc;
1812 if (updateCurrents) {
1813 id = idCalc;
1814 iq = iqCalc;
1815 }
1816 return true;
1817}

◆ EventTrigger()

bool Electromechanical::EventTrigger ( double  eventTime,
double  currentTime 
)
inlineprotected

Definition at line 570 of file Electromechanical.cpp.

571{
572 return (((eventTime - m_timeStep) < currentTime) && (eventTime >= currentTime));
573}

◆ GetDebugMessage()

wxString Electromechanical::GetDebugMessage ( ) const
inline

Definition at line 63 of file Electromechanical.h.

63{ return m_debugMessage; }

◆ GetErrorMessage()

wxString Electromechanical::GetErrorMessage ( ) const
inline

Definition at line 58 of file Electromechanical.h.

58{ return m_errorMsg; }

◆ GetIndMachineAdmittance()

std::complex< double > Electromechanical::GetIndMachineAdmittance ( IndMotor motor)
protected

Definition at line 2007 of file Electromechanical.cpp.

2008{
2009 const auto& data = motor->GetElectricalDataRef();
2010 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
2011 std::complex<double> v = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().voltage;
2012 std::complex<double> y0 = (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
2013 // The difference between calculated and defined reactive power
2014 std::complex<double> yq = std::complex<double>(0.0, data.q0 - dataPU.reactivePower) / (std::abs(v) * std::abs(v));
2015 return y0 + yq;
2016}

◆ GetIterationVector()

std::vector< double > Electromechanical::GetIterationVector ( ) const
inline

Definition at line 62 of file Electromechanical.h.

62{ return m_iterationsNumVector; }

◆ GetSyncMachineAdmittance()

std::complex< double > Electromechanical::GetSyncMachineAdmittance ( SyncGenerator generator)
protected

Definition at line 575 of file Electromechanical.cpp.

576{
577 const auto& data = generator->GetElectricalDataRef();
578 double k = 1.0; // Power base change factor.
579 if (data.useMachineBase) {
580 double oldBase = generator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
581 k = m_powerSystemBase / oldBase;
582 }
583
584 double ra = data.armResistance * k;
585 auto smModelData = GetSyncMachineModelData(generator);
586 double xd = smModelData.xd;
587 double xq = smModelData.xq;
588 double xdq = 0.5 * (xd + xq);
589 return (std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0));
590}

◆ GetSyncMachineModelData()

SyncMachineModelData Electromechanical::GetSyncMachineModelData ( SyncGenerator syncMachine)
protected

Definition at line 1904 of file Electromechanical.cpp.

1905{
1906 SyncMachineModelData smModelData;
1907
1908 const auto& data = syncMachine->GetElectricalDataRef();
1909 double k = 1.0; // Power base change factor.
1910 if (data.useMachineBase) {
1911 double oldBase = syncMachine->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1912 k = m_powerSystemBase / oldBase;
1913 }
1914
1915 switch (data.model) {
1916 case Machines::SM_MODEL_1: {
1917 smModelData.ed = data.tranEd;
1918 smModelData.eq = data.tranEq;
1919 smModelData.xq = data.transXd * k;
1920 smModelData.xd = smModelData.xq;
1921 } break;
1922 case Machines::SM_MODEL_2: {
1923 smModelData.ed = data.tranEd;
1924 smModelData.eq = data.tranEq;
1925 smModelData.xd = data.transXd * k;
1926 smModelData.xq = data.transXq * k;
1927 if (smModelData.xq == 0.0) {
1928 smModelData.xq = data.syncXq * k;
1929 if (smModelData.xq == 0.0) { smModelData.xq = data.syncXd * k; }
1930 }
1931 } break;
1932 case Machines::SM_MODEL_3: {
1933 smModelData.ed = data.tranEd;
1934 smModelData.eq = data.tranEq;
1935 smModelData.xd = data.transXd * k;
1936 smModelData.xq = data.transXq * k;
1937 if (smModelData.xq == 0.0) smModelData.xq = smModelData.xd;
1938 } break;
1939 case Machines::SM_MODEL_4:
1940 case Machines::SM_MODEL_5: {
1941 smModelData.ed = data.subEd;
1942 smModelData.eq = data.subEq;
1943 smModelData.xd = data.subXd * k;
1944 smModelData.xq = data.subXq * k;
1945 if (smModelData.xd == 0.0) smModelData.xd = smModelData.xq;
1946 if (smModelData.xq == 0.0) smModelData.xq = smModelData.xd;
1947 } break;
1948 }
1949 return smModelData;
1950}
Synchronous machine data for different models.

◆ GetTimeVector()

std::vector< double > Electromechanical::GetTimeVector ( ) const
inline

Definition at line 59 of file Electromechanical.h.

59{ return m_timeVector; }

◆ HasEvent()

bool Electromechanical::HasEvent ( double  currentTime)
protected

Definition at line 245 of file Electromechanical.cpp.

246{
247 for (unsigned int i = 0; i < m_eventTimeList.size(); ++i) {
248 if (!m_eventOccurrenceList[i]) {
249 if (EventTrigger(m_eventTimeList[i], currentTime)) {
250 m_eventOccurrenceList[i] = true;
251 return true;
252 }
253 }
254 }
255 return false;
256}

◆ InitializeDynamicElements()

bool Electromechanical::InitializeDynamicElements ( )
protected

Definition at line 592 of file Electromechanical.cpp.

593{
594 // Buses
595 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
596 Bus* bus = *it;
597 auto& data = bus->GetElectricalDataRef();
598 data.stabVoltageVector.clear();
599 data.stabVoltageVector.shrink_to_fit();
600 data.stabFreqVector.clear();
601 data.stabFreqVector.shrink_to_fit();
602 data.stabFreq = m_systemFreq;
603 data.oldAngle = std::arg(data.voltage);
604 data.filteredAngle = 0.0;
605 data.dxt = 0.0;
606 data.filteredVelocity = 0.0;
607 data.ddw = 0.0;
608 //bus->SetElectricalData(data);
609 }
610 // Loads
611 for (auto* load : m_loadList) {
612 auto dataPU = load->GetPUElectricalData(m_powerSystemBase);
613 auto& data = load->GetElectricalDataRef();
614
615 double activePower = dataPU.activePower;
616 double reactivePower = dataPU.reactivePower;
617
618 Bus* bus = nullptr;
619 if (GetParentBus(load, bus))
620 {
621 data.voltage = bus->GetElectricalDataRef().voltage;
622 }
623 else {
624 load->SetOnline(false);
625 }
626
627 data.v0 = std::abs(data.voltage);
628 data.y0 = std::complex<double>(activePower, -reactivePower) / (data.v0 * data.v0);
629
630 if (data.loadType == CONST_IMPEDANCE) {
631 std::complex<double> s0 = std::complex<double>(activePower, -reactivePower) * (data.v0 * data.v0);
632 activePower = s0.real();
633 reactivePower = -s0.imag();
634 }
635
636 data.pz0 = (data.constImpedanceActive / 100.0) * activePower;
637 data.pi0 = (data.constCurrentActive / 100.0) * activePower;
638 data.pp0 = (data.constPowerActive / 100.0) * activePower;
639
640 data.qz0 = (data.constImpedanceReactive / 100.0) * reactivePower;
641 data.qi0 = (data.constCurrentReactive / 100.0) * reactivePower;
642 data.qp0 = (data.constPowerReactive / 100.0) * reactivePower;
643
644 data.voltageVector.clear();
645 data.voltageVector.shrink_to_fit();
646 data.electricalPowerVector.clear();
647 data.electricalPowerVector.shrink_to_fit();
648
649 if (load->IsOnline())
650 data.electricalPower = std::complex<double>(activePower, reactivePower);
651 else {
652 data.electricalPower = std::complex<double>(0.0, 0.0);
653 data.voltage = std::complex<double>(0.0, 0.0);
654 }
655
656 //load->SetElectricalData(data);
657 }
658 // Synchronous generators
659 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
660 SyncGenerator* syncGenerator = *it;
661 auto dataPU = syncGenerator->GetPUElectricalData(m_powerSystemBase);
662 auto& data = syncGenerator->GetElectricalDataRef();
663 double k = 1.0; // Power base change factor.
664 if (data.useMachineBase) {
665 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
666 k = m_powerSystemBase / oldBase;
667 }
668 Bus* bus = nullptr;
669 if (GetParentBus(syncGenerator, bus))
670 data.terminalVoltage = bus->GetElectricalDataRef().voltage;
671 else
672 data.terminalVoltage = std::complex<double>(1.0, 0.0);
673
674 std::complex<double> conjS(dataPU.activePower, -dataPU.reactivePower);
675 std::complex<double> vt = data.terminalVoltage;
676 std::complex<double> ia = conjS / std::conj(vt);
677
678 double xd = data.syncXd * k;
679 double xq = data.syncXq * k;
680 double ra = data.armResistance * k;
681
682 if (data.model == Machines::SM_MODEL_1) {
683 xq = data.transXd * k;
684 xd = xq;
685 }
686 else if (data.syncXq == 0.0)
687 xq = data.syncXd * k;
688
689 double sd = 1.0;
690 double sq = 1.0;
691 double satF = 1.0;
692 double xp = data.potierReactance * k;
693 bool hasSaturation = false;
694 if (data.satFactor != 0.0) { // Have saturation.
695 satF = (data.satFactor - 1.2) / std::pow(1.2, 7);
696 if (xp == 0.0) xp = 0.8 * (data.transXd * k);
697 hasSaturation = true;
698 }
699
700 // Initialize state variables
701 std::complex<double> eq0 = vt + std::complex<double>(ra, xq) * ia;
702 double delta = std::arg(eq0);
703
704 double id0, iq0, vd0, vq0;
705 ABCtoDQ0(ia, delta, id0, iq0);
706 ABCtoDQ0(vt, delta, vd0, vq0);
707
708 // Initialize saturation
709 double xqs = xq;
710 double xds = xd;
711 if (hasSaturation) {
712 double oldDelta = 0;
713 bool exit = false;
714 int numIt = 0;
715 while (!exit) {
716 oldDelta = delta;
717 ABCtoDQ0(ia, delta, id0, iq0);
718 ABCtoDQ0(vt, delta, vd0, vq0);
719
720 // Direct-axis Potier voltage.
721 double epd = vd0 + ra * id0 + xp * iq0;
722
723 sq = 1.0 + satF * (xq / xd) * std::pow(epd, 6);
724 xqs = (xq - xp) / sq + xp;
725 eq0 = data.terminalVoltage + std::complex<double>(ra, xqs) * ia;
726 delta = std::arg(eq0);
727 if (std::abs(delta - oldDelta) < m_saturationTolerance) {
728 exit = true;
729 }
730 else if (numIt >= m_maxIterations) {
731 m_errorMsg = _("Error on initializate the saturation values of \"") + data.name + _("\".");
732 return false;
733 }
734 numIt++;
735 }
736 // Quadrature-axis Potier voltage.
737 double epq = vq0 + ra * iq0 - xp * id0;
738 sd = 1.0 + satF * std::pow(epq, 6);
739 xds = (xd - xp) / sd + xp;
740 }
741
742 double ef0 = vq0 + ra * iq0 - xds * id0;
743
744 data.initialFieldVoltage = ef0 * sd;
745 data.fieldVoltage = data.initialFieldVoltage;
746 data.pm = std::real((data.terminalVoltage * std::conj(ia)) + (std::abs(ia) * std::abs(ia) * ra));
747 syncGenerator->IsOnline() ? data.speed = 2.0 * M_PI * m_systemFreq : data.speed = 2.0 * M_PI * data.ocFrequency;
748 data.delta = delta;
749 data.pe = data.pm;
750 data.electricalPower = std::complex<double>(dataPU.activePower, dataPU.reactivePower);
751 if (!syncGenerator->IsOnline()) data.electricalPower = std::complex<double>(0.0, 0.0);
752 data.sd = sd;
753 data.sq = sq;
754 data.id = id0;
755 data.iq = iq0;
756
757 // Variables to extrapolate.
758 data.oldIq = iq0;
759 data.oldId = id0;
760 data.oldPe = data.pe;
761 data.oldSd = sd;
762 data.oldSq = sq;
763
764 switch (data.model) {
765 case Machines::SM_MODEL_1: {
766 data.tranEq = std::abs(eq0);
767
768 data.tranEd = 0.0;
769 data.subEq = 0.0;
770 data.subEd = 0.0;
771 } break;
772 case Machines::SM_MODEL_2: {
773 double tranXd = data.transXd * k;
774
775 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
776 data.tranEd = 0.0;
777 data.subEd = 0.0;
778 data.subEq = 0.0;
779 } break;
780 case Machines::SM_MODEL_3: {
781 double tranXd = data.transXd * k;
782 double tranXq = data.transXq * k;
783 if (tranXq == 0.0) tranXq = tranXd;
784
785 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
786 data.tranEd = -(xq - tranXq) * (iq0 / sq);
787
788 data.subEd = 0.0;
789 data.subEq = 0.0;
790 } break;
791 case Machines::SM_MODEL_4: {
792 double tranXd = data.transXd * k;
793 double subXd = data.subXd * k;
794 double subXq = data.subXq * k;
795 if (subXd == 0.0) subXd = subXq;
796 if (subXq == 0.0) subXq = subXd;
797
798 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
799 data.tranEd = 0.0;
800 data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
801 data.subEd = -(xq - subXq) * (iq0 / sq);
802 } break;
803 case Machines::SM_MODEL_5: {
804 double tranXd = data.transXd * k;
805 double tranXq = data.transXq * k;
806 double subXd = data.subXd * k;
807 double subXq = data.subXq * k;
808 if (subXd == 0.0) subXd = subXq;
809 if (subXq == 0.0) subXq = subXd;
810
811 data.tranEq = ef0 + (xd - tranXd) * (id0 / sd);
812 data.tranEd = -(xq - tranXq) * (iq0 / sq);
813 data.subEq = data.tranEq + (tranXd - subXd) * (id0 / sd);
814 data.subEd = data.tranEd - (tranXq - subXq) * (iq0 / sq);
815 } break;
816 default:
817 break;
818 }
819
820 // Initialize controllers
821 if (data.useAVR) {
822 //if (data.avrSolver) delete data.avrSolver;
823 //data.avrSolver =
824 // new ControlElementSolver(data.avr, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
825 data.avrSolver = std::make_shared<ControlElementSolver>(data.avr, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
826
827 data.avrSolver->SetSwitchStatus(syncGenerator->IsOnline());
828 data.avrSolver->SetCurrentTime(m_currentTime);
829 data.avrSolver->SetTerminalVoltage(std::abs(data.terminalVoltage));
830 data.avrSolver->SetInitialTerminalVoltage(std::abs(data.terminalVoltage));
831 data.avrSolver->SetActivePower(dataPU.activePower);
832 data.avrSolver->SetReactivePower(dataPU.reactivePower);
833 data.avrSolver->SetVelocity(data.speed);
834 data.avrSolver->SetInitialVelocity(data.speed);
835 data.avrSolver->InitializeValues(false);
836 if (!data.avrSolver->IsOK()) {
837 m_errorMsg = _("Error on initializate the AVR of \"") + data.name + wxT("\".\n") +
838 data.avrSolver->GetErrorMessage();
839 //syncGenerator->SetElectricalData(data);
840 return false;
841 }
842 }
843 if (data.useSpeedGovernor) {
844 //if (data.speedGovSolver) delete data.speedGovSolver;
845 //data.speedGovSolver =
846 // new ControlElementSolver(data.speedGov, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
847
848 data.speedGovSolver = std::make_shared<ControlElementSolver>(data.speedGov, m_timeStep * m_ctrlTimeStepMultiplier, m_tolerance, m_parent);
849 data.speedGovSolver->SetSwitchStatus(syncGenerator->IsOnline());
850 data.speedGovSolver->SetCurrentTime(m_currentTime);
851 data.speedGovSolver->SetActivePower(dataPU.activePower);
852 data.speedGovSolver->SetReactivePower(dataPU.reactivePower);
853 data.speedGovSolver->SetVelocity(data.speed);
854 data.speedGovSolver->SetInitialVelocity(data.speed);
855 data.speedGovSolver->SetInitialMecPower(data.pm);
856 data.speedGovSolver->InitializeValues(false);
857 if (!data.speedGovSolver->IsOK()) {
858 m_errorMsg = _("Error on initializate the speed governor of \"") + data.name + wxT("\".\n") +
859 data.speedGovSolver->GetErrorMessage();
860 //syncGenerator->SetElectricalData(data);
861 return false;
862 }
863 }
864 //}
865 //} else {
866 // Initialize open circuit machine.
867 //}
868 // Reset plot data
869 data.terminalVoltageVector.clear();
870 data.terminalVoltageVector.shrink_to_fit();
871 data.electricalPowerVector.clear();
872 data.electricalPowerVector.shrink_to_fit();
873 data.mechanicalPowerVector.clear();
874 data.mechanicalPowerVector.shrink_to_fit();
875 data.freqVector.clear();
876 data.freqVector.shrink_to_fit();
877 data.fieldVoltageVector.clear();
878 data.fieldVoltageVector.shrink_to_fit();
879 data.deltaVector.clear();
880 data.deltaVector.shrink_to_fit();
881
882 //syncGenerator->SetElectricalData(data);
883 }
884 // Induction motors
885 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
886 IndMotor* indMotor = *it;
887 auto dataPU = indMotor->GetPUElectricalData(m_powerSystemBase);
888 auto& data = indMotor->GetElectricalDataRef();
889
890 Bus* connBus = static_cast<Bus*>(indMotor->GetParentList()[0]);
891 if (connBus->GetElectricalDataRef().number < 0) indMotor->SetOnline(false);
892
893 double w0 = 2.0 * M_PI * m_systemFreq;
894 std::complex<double> i1 = std::complex<double>(dataPU.activePower, -data.q0) / std::conj(data.terminalVoltage);
895 double ir = std::real(i1);
896 double im = std::imag(i1);
897 std::complex<double> e = data.terminalVoltage - std::complex<double>(data.r1t, data.x1t) * i1;
898 double te = std::real(e * std::conj(i1)) / w0;
899
900 double wi = w0 * (1 - data.s0); // Initial velocity
901 data.as = te * (data.aw + (data.bw * w0) / wi + (data.cw * w0 * w0) / (wi * wi));
902 data.bs = te * ((data.bw * w0) / wi + (2.0 * data.cw * w0 * w0) / (wi * wi));
903 data.cs = (te * data.cw * w0 * w0) / (wi * wi);
904
905 data.aCalc = data.as;
906 data.bCalc = data.bs;
907 data.cCalc = data.cs;
908
909 if (indMotor->IsOnline()) {
910 std::complex<double> tranE =
911 (std::complex<double>(0, data.x0 - data.xt) * i1) / std::complex<double>(1.0, w0 * data.s0 * data.t0);
912
913 data.tranEr = std::real(tranE);
914 data.tranEm = std::imag(tranE);
915
916 data.slip = data.s0;
917 data.ir = ir;
918 data.im = im;
919 data.te = te;
920 }
921 else { // Offline
922 data.tranEr = 0.0;
923 data.tranEm = 0.0;
924 data.slip = 1.0 - 1e-7;
925 data.ir = 0.0;
926 data.im = 0.0;
927 data.te = 0.0;
928 }
929
930 // Variables to extrapolate.
931 data.oldTe = data.te;
932 data.oldIr = data.ir;
933 data.oldIm = data.im;
934
935 // Reset plot data
936 data.slipVector.clear();
937 data.slipVector.shrink_to_fit();
938 data.electricalTorqueVector.clear();
939 data.electricalTorqueVector.shrink_to_fit();
940 data.mechanicalTorqueVector.clear();
941 data.mechanicalTorqueVector.shrink_to_fit();
942 data.velocityVector.clear();
943 data.velocityVector.shrink_to_fit();
944 data.currentVector.clear();
945 data.currentVector.shrink_to_fit();
946 data.terminalVoltageVector.clear();
947 data.terminalVoltageVector.shrink_to_fit();
948 data.activePowerVector.clear();
949 data.activePowerVector.shrink_to_fit();
950 data.reactivePowerVector.clear();
951 data.reactivePowerVector.shrink_to_fit();
952
953 //indMotor->SetElectricalData(data);
954 }
955 CalculateReferenceSpeed();
956 return true;
957}

◆ InsertIndMachinesOnYBus()

bool Electromechanical::InsertIndMachinesOnYBus ( )
protected

Definition at line 2018 of file Electromechanical.cpp.

2019{
2020 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
2021 IndMotor* motor = *it;
2022 if (!CalculateIndMachinesTransientValues(motor)) return false;
2023 if (motor->IsOnline()) {
2024 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().number;
2025 m_yBus[n][n] += GetIndMachineAdmittance(motor);
2026 }
2027 }
2028 return true;
2029}

◆ InsertSyncMachinesOnYBus()

void Electromechanical::InsertSyncMachinesOnYBus ( )
protected

Definition at line 547 of file Electromechanical.cpp.

548{
549 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
550 SyncGenerator* syncGenerator = *it;
551 if (syncGenerator->IsOnline()) {
552 Bus* connBus = static_cast<Bus*>(syncGenerator->GetParentList()[0]);
553 if (connBus->GetElectricalDataRef().number < 0) {
554 syncGenerator->SetOnline(false);
555 //auto data = syncGenerator->GetElectricalData();
556 //data.activePower = 0.0;
557 //data.reactivePower = 0.0;
558 //syncGenerator->SetElectricalData(data);
559 }
560
561 if (syncGenerator->IsOnline()) {
562 const auto& data = syncGenerator->GetElectricalDataRef();
563 int n = connBus->GetElectricalDataRef().number;
564 m_yBus[n][n] += GetSyncMachineAdmittance(syncGenerator);
565 }
566 }
567 }
568}

◆ PreallocateVectors()

void Electromechanical::PreallocateVectors ( )
protected

Definition at line 1952 of file Electromechanical.cpp.

1953{
1954 int numPoints = static_cast<unsigned int>(m_simTime / m_plotTime) + 100;
1955
1956 m_timeVector.reserve(numPoints);
1957 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1958 SyncGenerator* syncGenerator = *it;
1959 auto& data = syncGenerator->GetElectricalDataRef();
1960 if (data.plotSyncMachine) {
1961 data.terminalVoltageVector.reserve(numPoints);
1962 data.electricalPowerVector.reserve(numPoints);
1963 data.mechanicalPowerVector.reserve(numPoints);
1964 data.freqVector.reserve(numPoints);
1965 data.fieldVoltageVector.reserve(numPoints);
1966 data.deltaVector.reserve(numPoints);
1967 //syncGenerator->SetElectricalData(data);
1968 }
1969 }
1970 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
1971 Bus* bus = *it;
1972 auto& data = bus->GetElectricalDataRef();
1973 if (data.plotBus) {
1974 data.stabVoltageVector.reserve(numPoints);
1975 data.stabFreqVector.reserve(numPoints);
1976 //bus->SetElectricalData(data);
1977 }
1978 }
1979 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
1980 Load* load = *it;
1981 auto& data = load->GetElectricalDataRef();
1982 if (data.plotLoad) {
1983 data.voltageVector.reserve(numPoints);
1984 data.electricalPowerVector.reserve(numPoints);
1985 //load->SetElectricalData(data);
1986 }
1987 }
1988 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1989 IndMotor* motor = *it;
1990 auto& data = motor->GetElectricalDataRef();
1991 if (data.plotIndMachine) {
1992 data.slipVector.reserve(numPoints);
1993 data.electricalTorqueVector.reserve(numPoints);
1994 data.mechanicalTorqueVector.reserve(numPoints);
1995 data.velocityVector.reserve(numPoints);
1996 data.currentVector.reserve(numPoints);
1997 data.terminalVoltageVector.reserve(numPoints);
1998 data.activePowerVector.reserve(numPoints);
1999 data.reactivePowerVector.reserve(numPoints);
2000 //motor->SetElectricalData(data);
2001 }
2002 }
2003
2004 m_iterationsNumVector.reserve(numPoints);
2005}

◆ RunStabilityCalculation()

bool Electromechanical::RunStabilityCalculation ( )

Definition at line 83 of file Electromechanical.cpp.

84{
85 wxProgressDialog pbd(_("Running simulation"), _("Initializing..."), 100, m_parent,
86 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_SMOOTH);
87
88 SetSyncMachinesModel();
89
90 // Calculate the admittance matrix with the synchronous machines.
91 if (!GetYBus(m_yBus, m_powerSystemBase, POSITIVE_SEQ, false, true, true)) {
92 m_errorMsg = _("It was not possible to build the admittance matrix.");
93 return false;
94 }
95 InsertSyncMachinesOnYBus();
96 if (!InsertIndMachinesOnYBus()) return false;
97 GetLUDecomposition(m_yBus, m_yBusL, m_yBusU);
98
99 // Get buses voltages.
100 m_vBus.clear();
101 m_vBus.resize(m_yBus.size());
102 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
103 Bus* bus = *it;
104 const auto& data = bus->GetElectricalDataRef();
105 if (data.number >= 0) m_vBus[data.number] = data.voltage;
106 }
107 m_vBus.shrink_to_fit();
108
109 // Calculate injected currents
110 m_iBus = ComplexMatrixTimesVector(m_yBus, m_vBus);
111 for (unsigned int i = 0; i < m_iBus.size(); ++i) {
112 if (std::abs(m_iBus[i]) < 1e-5) m_iBus[i] = std::complex<double>(0.0, 0.0);
113 }
114
115 if (!InitializeDynamicElements()) return false;
116 PreallocateVectors(); // Reserve the vectors' memory with a estimated size, this can optimize the simulation.
117
118#ifdef _DEBUG
119 for (auto* syncGenerator : m_syncGeneratorList) {
120 auto data = syncGenerator->GetPUElectricalData(m_powerSystemBase);
121 m_debugMessage += "------\n";
122 m_debugMessage += data.name + ":\n";
123 m_debugMessage += "P = " + wxString::FromDouble(data.electricalPower.real(), 5) + " p.u.\n";
124 m_debugMessage += "Q = " + wxString::FromDouble(data.electricalPower.imag(), 5) + " p.u.\n";
125 m_debugMessage += "If0 = " + wxString::FromDouble(data.initialFieldVoltage, 5) + " p.u.\n";
126 m_debugMessage += "If = " + wxString::FromDouble(data.fieldVoltage, 5) + " p.u.\n";
127 m_debugMessage += "delta = " + wxString::FromDouble(data.delta, 5) + " rad?\n";
128 m_debugMessage += "Vf = " + wxString::FromDouble(data.fieldVoltage, 5) + " p.u.\n";
129 m_debugMessage += "Vf0 = " + wxString::FromDouble(data.initialFieldVoltage, 5) + " p.u.\n";
130 m_debugMessage += "Vt = " + wxString::FromDouble(std::abs(data.terminalVoltage), 5) + " p.u.\n";
131 m_debugMessage += "theta = " + wxString::FromDouble(std::arg(data.terminalVoltage), 5) + " rad\n";
132 m_debugMessage += "Pe = " + wxString::FromDouble(data.pe, 5) + " p.u.\n";
133 m_debugMessage += "Pm = " + wxString::FromDouble(data.pm, 5) + " p.u.\n";
134 m_debugMessage += "w = " + wxString::FromDouble(data.speed, 5) + " p.u.\n";
135 m_debugMessage += "Id = " + wxString::FromDouble(data.id, 5) + " p.u.\n";
136 m_debugMessage += "Iq = " + wxString::FromDouble(data.iq, 5) + " p.u.\n";
137 m_debugMessage += "Ed' = " + wxString::FromDouble(data.tranEd, 5) + " p.u.\n";
138 m_debugMessage += "Eq' = " + wxString::FromDouble(data.tranEq, 5) + " p.u.\n";
139 m_debugMessage += "Ed'' = " + wxString::FromDouble(data.subEd, 5) + " p.u.\n";
140 m_debugMessage += "Eq'' = " + wxString::FromDouble(data.subEq, 5) + " p.u.\n";
141 m_debugMessage += "------\n\n";
142 }
143#endif
144
145 double pbdTime = m_plotTime;
146 m_currentTime = 0.0;
147 double currentPlotTime = 0.0;
148 double currentPbdTime = 0.0;
149 while (m_currentTime <= m_simTime) {
150 bool hasEvent = false;
151 if (HasEvent(m_currentTime)) {
152 hasEvent = true;
153 SetEvent(m_currentTime);
154 GetLUDecomposition(m_yBus, m_yBusL, m_yBusU);
155 }
156
157
158
159 if (currentPlotTime >= m_plotTime || m_currentTime == 0.0) {
160 m_timeVector.emplace_back(m_currentTime);
161
162 #ifdef _DEBUG
163 auto t0 = std::chrono::high_resolution_clock::now();
164 #endif //_DEBUG
165
166 SaveData();
167
168 #ifdef _DEBUG
169 auto t1 = std::chrono::high_resolution_clock::now();
170 totalSaveData += std::chrono::duration<double, std::milli>(t1 - t0).count();
171 saveDataCalls++;
172 #endif //_DEBUG
173
174 currentPlotTime = 0.0;
175 }
176
177 if (currentPbdTime > pbdTime) {
178 if (!pbd.Update((m_currentTime / m_simTime) * 100, wxString::Format("Time = %.2fs", m_currentTime))) {
179 m_errorMsg = wxString::Format(_("Simulation cancelled at %.2fs."), m_currentTime);
180 pbd.Update(100);
181 return false;
182 }
183 currentPbdTime = 0.0;
184 }
185
186 #ifdef _DEBUG
187 auto t0 = std::chrono::high_resolution_clock::now();
188 #endif //_DEBUG
189
190 if (!SolveMachines()) return false;
191
192 #ifdef _DEBUG
193 auto t1 = std::chrono::high_resolution_clock::now();
194 totalSolveMachines += std::chrono::duration<double, std::milli>(t1 - t0).count();
195 solveMachinesCalls++;
196
197 t0 = std::chrono::high_resolution_clock::now();
198 #endif //_DEBUG
199
200 CalculateBusesFrequency(hasEvent);
201
202 #ifdef _DEBUG
203 t1 = std::chrono::high_resolution_clock::now();
204 totalCalcFreq += std::chrono::duration<double, std::milli>(t1 - t0).count();
205 calcFreqCalls++;
206 #endif //_DEBUG
207
208 m_currentTime += m_timeStep;
209 currentPlotTime += m_timeStep;
210 currentPbdTime += m_timeStep;
211 }
212 #ifdef _DEBUG
213 wxLogMessage("SaveData: chamadas=%zu total=%.2f ms media=%.3f ms", saveDataCalls, totalSaveData, totalSaveData / saveDataCalls);
214 wxLogMessage("SolveMachines: chamadas=%zu total=%.2f ms media=%.3f ms", solveMachinesCalls, totalSolveMachines, totalSolveMachines / solveMachinesCalls);
215 wxLogMessage("CalculateBusesFrequency: chamadas=%zu total=%.2f ms media=%.3f ms", calcFreqCalls, totalCalcFreq, totalCalcFreq / calcFreqCalls);
216 #endif //_DEBUG
217
218 return true;
219}
@ POSITIVE_SEQ
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.
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).
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.

◆ SaveData()

void Electromechanical::SaveData ( )
protected

Definition at line 1405 of file Electromechanical.cpp.

1406{
1407
1408 for(auto& syncGenerator : m_syncGeneratorList) {
1409 auto& data = syncGenerator->GetElectricalDataRef();
1410 if (data.plotSyncMachine) { syncGenerator->SavePlotData(); }
1411 }
1412
1413 for(auto& bus : m_busList) {
1414 auto& data = bus->GetElectricalDataRef();
1415 if (data.plotBus) {
1416 data.stabVoltageVector.emplace_back(data.number >= 0 ? m_vBus[data.number] : 0.0);
1417 data.stabFreqVector.emplace_back(data.number >= 0 ? data.stabFreq : 0.0);
1418 }
1419 }
1420
1421 for(auto& load : m_loadList) {
1422 auto& data = load->GetElectricalDataRef();
1423 if (data.plotLoad) {
1424 data.voltageVector.emplace_back(data.voltage);
1425 data.electricalPowerVector.emplace_back(data.electricalPower);
1426 //load->SetElectricalData(data);
1427 }
1428 }
1429 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1430 IndMotor* motor = *it;
1431 auto& data = motor->GetElectricalDataRef();
1432 if (data.plotIndMachine) {
1433 data.slipVector.emplace_back(data.slip * 100.0);
1434 data.electricalTorqueVector.emplace_back(data.te * 2.0 * M_PI * m_systemFreq);
1435 double tm = (data.as - data.bs * data.slip + data.cs * data.slip * data.slip) * 2.0 * M_PI * m_systemFreq;
1436 data.mechanicalTorqueVector.emplace_back(tm);
1437 double w = (1.0 - data.slip) * 2.0 * M_PI * m_systemFreq;
1438 data.velocityVector.emplace_back(w);
1439 std::complex<double> i1 = std::complex<double>(data.ir, data.im);
1440 data.currentVector.emplace_back(std::abs(i1));
1441 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().number;
1442 std::complex<double> v = n >= 0 ? m_vBus[n] : 0.0;
1443 data.terminalVoltageVector.emplace_back(std::abs(v));
1444 std::complex<double> s = v * std::conj(i1);
1445 data.activePowerVector.emplace_back(std::real(s));
1446 data.reactivePowerVector.emplace_back(std::imag(s));
1447 //motor->SetElectricalData(data);
1448 }
1449 }
1450 m_iterationsNumVector.emplace_back(m_iterationsNum);
1451}

◆ SetEvent()

void Electromechanical::SetEvent ( double  currentTime)
protected

Definition at line 258 of file Electromechanical.cpp.

259{
260 // Fault
261 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
262 Bus* bus = *it;
263 const auto& data = bus->GetElectricalDataRef();
264 int n = data.number;
265 if (data.stabHasFault && n >= 0) {
266
267 // Insert fault
268 if (EventTrigger(data.stabFaultTime, currentTime)) {
269 double r, x;
270 r = data.stabFaultResistance;
271 x = data.stabFaultReactance;
272 if (x < 1e-5) x = 1e-5;
273 m_yBus[n][n] += std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
274 }
275
276 // Remove fault
277 else if (EventTrigger(data.stabFaultTime + data.stabFaultLength, currentTime)) {
278 double r, x;
279 r = data.stabFaultResistance;
280 x = data.stabFaultReactance;
281 if (x < 1e-5) x = 1e-5;
282 m_yBus[n][n] -= std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
283 }
284 }
285 }
286
287 // SyncGenerator switching
288 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
289 SyncGenerator* generator = *it;
290 auto swData = generator->GetSwitchingData();
291 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
292 if (EventTrigger(swData.swTime[i], currentTime)) {
293 // Remove machine (only connected machines)
294 if (swData.swType[i] == SwitchingType::SW_REMOVE && generator->IsOnline()) {
295 generator->SetOnline(false);
296 int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalDataRef().number;
297 if (n >= 0) m_yBus[n][n] -= GetSyncMachineAdmittance(generator);
298 }
299
300 // Insert machine (only disconnected machines)
301 if (swData.swType[i] == SwitchingType::SW_INSERT && !generator->IsOnline() && generator->GetParentList().size() == 1) {
302 if (generator->SetOnline(true)) {
303 int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalDataRef().number;
304 if (n >= 0) m_yBus[n][n] += GetSyncMachineAdmittance(generator);
305 }
306 }
307 }
308 }
309 }
310
311 // Induction motor switching
312 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
313 IndMotor* motor = *it;
314 auto swData = motor->GetSwitchingData();
315 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
316 if (EventTrigger(swData.swTime[i], currentTime)) {
317 // Remove machine (only connected machines)
318 if (swData.swType[i] == SwitchingType::SW_REMOVE && motor->IsOnline() && motor->GetParentList().size() == 1) {
319 const auto& data = motor->GetElectricalDataRef();
320 motor->SetOnline(false);
321 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().number;
322 if (n >= 0) m_yBus[n][n] -= (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
323 }
324
325 // Insert machine (only disconnected machines)
326 if (swData.swType[i] == SwitchingType::SW_INSERT && !motor->IsOnline() && motor->GetParentList().size() == 1) {
327 const auto& data = motor->GetElectricalDataRef();
328 if (motor->SetOnline(true)) {
329 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalDataRef().number;
330 if (n >= 0) m_yBus[n][n] += (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
331 }
332 }
333 }
334 }
335 }
336
337 // Load switching
338 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
339 Load* load = *it;
340 auto swData = load->GetSwitchingData();
341 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
342 if (EventTrigger(swData.swTime[i], currentTime)) {
343 // Remove load (only connected loads)
344 if (swData.swType[i] == SwitchingType::SW_REMOVE && load->IsOnline() && load->GetParentList().size() == 1) {
345 load->SetOnline(false);
346 auto data = load->GetPUElectricalData(m_powerSystemBase);
347 Bus* parentBus = static_cast<Bus*>(load->GetParentList()[0]);
348 int n = parentBus->GetElectricalDataRef().number;
349 std::complex<double> v = parentBus->GetElectricalDataRef().voltage;
350 if (n >= 0) {
351 m_yBus[n][n] -=
352 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
353 }
354 }
355
356 // Insert load (only disconnected load)
357 if (swData.swType[i] == SwitchingType::SW_INSERT && !load->IsOnline() && load->GetParentList().size() == 1) {
358 if (load->SetOnline(true)) {
359 auto data = load->GetPUElectricalData(m_powerSystemBase);
360 Bus* parentBus = static_cast<Bus*>(load->GetParentList()[0]);
361 int n = parentBus->GetElectricalDataRef().number;
362 std::complex<double> v = parentBus->GetElectricalDataRef().voltage;
363 if (n >= 0) {
364 m_yBus[n][n] +=
365 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
366 }
367 }
368 }
369 }
370 }
371 }
372
373 // Line switching
374 for (auto it = m_lineList.begin(), itEnd = m_lineList.end(); it != itEnd; ++it) {
375 Line* line = *it;
376 auto swData = line->GetSwitchingData();
377 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
378 if (EventTrigger(swData.swTime[i], currentTime)) {
379 // Remove line (only connected lines)
380 if (swData.swType[i] == SwitchingType::SW_REMOVE && line->IsOnline()) {
381 line->SetOnline(false);
382 const auto& data = line->GetElectricalDataRef();
383
384 int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalDataRef().number;
385 int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalDataRef().number;
386 if (n1 >= 0 && n2 >= 0) {
387 m_yBus[n1][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
388 m_yBus[n2][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
389
390 m_yBus[n1][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
391 m_yBus[n2][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
392
393 m_yBus[n1][n1] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
394 m_yBus[n2][n2] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
395 }
396 }
397
398 // Insert line (only disconnected lines)
399 if (swData.swType[i] == SwitchingType::SW_INSERT && !line->IsOnline() && line->GetParentList().size() == 2) {
400 if (line->SetOnline(true)) {
401 auto& data = line->GetElectricalDataRef();
402
403 int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalDataRef().number;
404 int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalDataRef().number;
405 if (n1 >= 0 && n2 >= 0) {
406 m_yBus[n1][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
407 m_yBus[n2][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
408
409 m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
410 m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
411
412 m_yBus[n1][n1] += std::complex<double>(0.0, data.capSusceptance / 2.0);
413 m_yBus[n2][n2] += std::complex<double>(0.0, data.capSusceptance / 2.0);
414 }
415 }
416 }
417 }
418 }
419 }
420
421 // Transformer switching
422 for (auto it = m_transformerList.begin(), itEnd = m_transformerList.end(); it != itEnd; ++it) {
423 Transformer* transformer = *it;
424 auto swData = transformer->GetSwitchingData();
425 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
426 if (EventTrigger(swData.swTime[i], currentTime)) {
427 // Remove transformer (only connected transformers)
428 if (swData.swType[i] == SwitchingType::SW_REMOVE && transformer->IsOnline()) {
429 transformer->SetOnline(false);
430 const auto& data = transformer->GetElectricalDataRef();
431
432 int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalDataRef().number;
433 int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalDataRef().number;
434 if (n1 >= 0 && n2 >= 0) {
435 if (data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
436 m_yBus[n1][n2] -= -1.0 / std::complex<double>(data.resistance, data.indReactance);
437 m_yBus[n2][n1] -= -1.0 / std::complex<double>(data.resistance, data.indReactance);
438
439 m_yBus[n1][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
440 m_yBus[n2][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
441 }
442 else {
443 // Complex turns ratio
444 double radPhaseShift = wxDegToRad(data.phaseShift);
445 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
446 -data.turnsRatio * std::sin(radPhaseShift));
447
448 // Transformer admitance
449 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
450 m_yBus[n1][n1] -= y / std::pow(std::abs(a), 2.0);
451 m_yBus[n1][n2] -= -(y / std::conj(a));
452 m_yBus[n2][n1] -= -(y / a);
453 m_yBus[n2][n2] -= y;
454 }
455 }
456 }
457
458 // Insert transformer (only disconnected transformers)
459 if (swData.swType[i] == SwitchingType::SW_INSERT && !transformer->IsOnline() &&
460 transformer->GetParentList().size() == 2) {
461 if (transformer->SetOnline(true)) {
462 const auto& data = transformer->GetElectricalDataRef();
463
464 int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalDataRef().number;
465 int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalDataRef().number;
466 if (n1 >= 0 && n2 >= 0) {
467 if (data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
468 m_yBus[n1][n2] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
469 m_yBus[n2][n1] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
470
471 m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
472 m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
473 }
474 else {
475 // Complex turns ratio
476 double radPhaseShift = wxDegToRad(data.phaseShift);
477 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
478 -data.turnsRatio * std::sin(radPhaseShift));
479
480 // Transformer admitance
481 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
482 m_yBus[n1][n1] += y / std::pow(std::abs(a), 2.0);
483 m_yBus[n1][n2] += -(y / std::conj(a));
484 m_yBus[n2][n1] += -(y / a);
485 m_yBus[n2][n2] += y;
486 }
487 }
488 }
489 }
490 }
491 }
492 }
493
494 // Capacitor switching
495 for (auto it = m_capacitorList.begin(), itEnd = m_capacitorList.end(); it != itEnd; ++it) {
496 Capacitor* capacitor = *it;
497 auto swData = capacitor->GetSwitchingData();
498 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
499 if (EventTrigger(swData.swTime[i], currentTime)) {
500 // Remove capacitor (only connected capacitors)
501 if (swData.swType[i] == SwitchingType::SW_REMOVE && capacitor->IsOnline()) {
502 capacitor->SetOnline(false);
503 auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
504 int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalDataRef().number;
505 if (n >= 0) m_yBus[n][n] -= std::complex<double>(0.0, data.reactivePower);
506 }
507
508 // Insert capacitor (only disconnected capacitors)
509 if (swData.swType[i] == SwitchingType::SW_INSERT && !capacitor->IsOnline() && capacitor->GetParentList().size() == 1) {
510 if (capacitor->SetOnline(true)) {
511 auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
512 int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalDataRef().number;
513 if (n >= 0) m_yBus[n][n] += std::complex<double>(0.0, data.reactivePower);
514 }
515 }
516 }
517 }
518 }
519
520 // Inductor switching
521 for (auto it = m_inductorList.begin(), itEnd = m_inductorList.end(); it != itEnd; ++it) {
522 Inductor* inductor = *it;
523 auto swData = inductor->GetSwitchingData();
524 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
525 if (EventTrigger(swData.swTime[i], currentTime)) {
526 // Remove inductor (only connected inductors)
527 if (swData.swType[i] == SwitchingType::SW_REMOVE && inductor->IsOnline()) {
528 inductor->SetOnline(false);
529 auto data = inductor->GetPUElectricalData(m_powerSystemBase);
530 int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalDataRef().number;
531 if (n >= 0) m_yBus[n][n] -= std::complex<double>(0.0, -data.reactivePower);
532 }
533
534 // Insert inductor (only disconnected inductors)
535 if (swData.swType[i] == SwitchingType::SW_INSERT && !inductor->IsOnline() && inductor->GetParentList().size() == 1) {
536 if (inductor->SetOnline(true)) {
537 auto data = inductor->GetPUElectricalData(m_powerSystemBase);
538 int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalDataRef().number;
539 if (n >= 0) m_yBus[n][n] += std::complex<double>(0.0, -data.reactivePower);
540 }
541 }
542 }
543 }
544 }
545}
Shunt capactior power element.
Definition Capacitor.h:39
std::vector< Line * > m_lineList
List of transmission lines 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< Inductor * > m_inductorList
List of inductors in the system.
Inductor shunt power element.
Definition Inductor.h:39
Power line element.
Definition Line.h:64
virtual SwitchingData GetSwitchingData()
Returns the switching data of the element.
Two-winding transformer power element.
Definition Transformer.h:84

◆ SetEventTimeList()

void Electromechanical::SetEventTimeList ( )
protected

Definition at line 221 of file Electromechanical.cpp.

222{
223 // Fault
224 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
225 Bus* bus = *it;
226 const auto& data = bus->GetElectricalDataRef();
227 if (data.stabHasFault) {
228 m_eventTimeList.emplace_back(data.stabFaultTime);
229 m_eventOccurrenceList.push_back(false);
230 m_eventTimeList.emplace_back(data.stabFaultTime + data.stabFaultLength);
231 m_eventOccurrenceList.push_back(false);
232 }
233 }
234 // Switching
235 for (auto it = m_powerElementList.begin(), itEnd = m_powerElementList.end(); it != itEnd; ++it) {
236 PowerElement* element = *it;
237 SwitchingData swData = element->GetSwitchingData();
238 for (unsigned int i = 0; i < swData.swTime.size(); ++i) {
239 m_eventTimeList.emplace_back(swData.swTime[i]);
240 m_eventOccurrenceList.push_back(false);
241 }
242 }
243}
std::vector< PowerElement * > m_powerElementList
List of power elements in the system.
Abstract class of power elements.
Switching data of power elements.
std::vector< double > swTime

◆ SetSyncMachinesModel()

void Electromechanical::SetSyncMachinesModel ( )
protected

Definition at line 1453 of file Electromechanical.cpp.

1454{
1455 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1456 SyncGenerator* syncGenerator = *it;
1457 auto& data = syncGenerator->GetElectricalDataRef();
1458 data.model = GetMachineModel(syncGenerator);
1459 //syncGenerator->SetElectricalData(data);
1460 }
1461}
Machines::SyncMachineModel GetMachineModel(SyncGenerator *generator)
Get the synchronous machine model used by the generator based on user-defined parameters.

◆ SolveMachines()

bool Electromechanical::SolveMachines ( )
protected

Definition at line 1223 of file Electromechanical.cpp.

1224{
1225 // First interation values and extrapolations
1226 // Synchronous machines
1227 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1228 SyncGenerator* syncGenerator = *it;
1229 const auto& data = syncGenerator->GetElectricalDataRef();
1230
1231 if (syncGenerator->IsOnline()) {
1232 double id, iq, pe, sd, sq;
1233 pe = data.pe;
1234 id = data.id;
1235 iq = data.iq;
1236 sd = data.sd;
1237 sq = data.sq;
1238
1239 double k = 1.0; // Power base change factor.
1240 if (data.useMachineBase) {
1241 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1242 k = m_powerSystemBase / oldBase;
1243 }
1244
1245 // Calculate integration constants.
1246 CalculateIntegrationConstants(syncGenerator, id, iq, k);
1247
1248 if (!CalculateNonIntVariables(syncGenerator, id, iq, sd, sq, pe, k)) return false;
1249 // Extrapolate nonintegrable variables.
1250 id = 2.0 * id - data.oldId;
1251 iq = 2.0 * iq - data.oldIq;
1252 pe = 2.0 * pe - data.oldPe;
1253 sd = 2.0 * sd - data.oldSd;
1254 sq = 2.0 * sq - data.oldSq;
1255
1256 CalculateIntVariables(syncGenerator, id, iq, sd, sq, pe, k);
1257 }
1258 else {
1259 CalculateIntegrationConstants(syncGenerator, 0.0f, 0.0f);
1260 }
1261 }
1262 // Induction machines
1263 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1264 IndMotor* indMotor = *it;
1265 const auto& data = indMotor->GetElectricalDataRef();
1266
1267 //if(indMotor->IsOnline()) {
1268 double ir, im, te;
1269 te = data.te;
1270 ir = data.ir;
1271 im = data.im;
1272
1273 double k = 1.0; // Power base change factor.
1274 if (data.useMachinePowerAsBase) {
1275 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1276 k = m_powerSystemBase / oldBase;
1277 }
1278
1279 // Calculate integration constants.
1280 CalculateIntegrationConstants(indMotor, ir, im, k);
1281
1282 if (!CalculateNonIntVariables(indMotor, ir, im, te, k)) return false;
1283 // Extrapolate nonintegrable variables.
1284 ir = 2.0 * ir - data.oldIr;
1285 im = 2.0 * im - data.oldIm;
1286 te = 2.0 * te - data.oldTe;
1287
1288 CalculateIntVariables(indMotor, ir, im, te, k);
1289 //} else {
1290 //CalculateIntegrationConstants(indMotor, 0.0f, 0.0f);
1291 //}
1292 }
1293
1294 double error = 1.0;
1295 int iterations = 0;
1296 while (error > m_tolerance) {
1297 error = 0.0;
1298
1299 // Calculate the injected currents.
1300 if (!CalculateInjectedCurrents()) return false;
1301
1302 // Calculate the buses voltages.
1303 m_vBus = LUEvaluate(m_yBusU, m_yBusL, m_iBus);
1304
1305 // Solve synchronous machine equations.
1306 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1307 SyncGenerator* syncGenerator = *it;
1308
1309 const auto& data = syncGenerator->GetElectricalDataRef();
1310
1311 double id = data.id;
1312 double iq = data.iq;
1313 double pe = data.pe;
1314 double sd = data.sd;
1315 double sq = data.sq;
1316
1317 // Power base change factor.
1318 double k = 1.0;
1319 if (data.useMachineBase) {
1320 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1321 k = m_powerSystemBase / oldBase;
1322 }
1323
1324 // Calculate id, iq, dq, sd
1325 if (!CalculateNonIntVariables(syncGenerator, id, iq, sd, sq, pe, k)) return false;
1326
1327 double genError = CalculateIntVariables(syncGenerator, id, iq, sd, sq, pe, k);
1328
1329 if (genError > error) error = genError;
1330 }
1331
1332 // Solve induction machine equation
1333 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1334 IndMotor* indMotor = *it;
1335
1336 const auto& data = indMotor->GetElectricalDataRef();
1337
1338 double ir = data.ir;
1339 double im = data.im;
1340 double te = data.te;
1341
1342 // Power base change factor.
1343 double k = 1.0;
1344 if (data.useMachinePowerAsBase) {
1345 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1346 k = m_powerSystemBase / oldBase;
1347 }
1348
1349 // Calculate te
1350 if (!CalculateNonIntVariables(indMotor, ir, im, te, k)) return false;
1351
1352 double motorError = CalculateIntVariables(indMotor, ir, im, te, k);
1353
1354 if (motorError > error) error = motorError;
1355 }
1356
1357 ++iterations;
1358
1359 if (iterations > m_maxIterations) {
1360 m_errorMsg = _("Impossible to solve the system machines.\nCheck the system parameters and/or "
1361 "decrease the time step.");
1362 return false;
1363 }
1364 }
1365 m_iterationsNum = iterations;
1366
1367 // Solve controllers.
1368 int ctrlRatio = static_cast<int>(1 / m_ctrlTimeStepMultiplier);
1369 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1370 SyncGenerator* syncGenerator = *it;
1371 auto& data = syncGenerator->GetElectricalDataRef();
1372 if (data.useAVR && data.avrSolver) {
1373 data.avrSolver->SetSwitchStatus(syncGenerator->IsOnline());
1374 data.avrSolver->SetCurrentTime(m_currentTime);
1375 data.avrSolver->SetTerminalVoltage(std::abs(data.terminalVoltage));
1376 data.avrSolver->SetDeltaActivePower((data.electricalPower.real() - data.avrSolver->GetActivePower()) /
1377 m_timeStep);
1378 data.avrSolver->SetActivePower(data.electricalPower.real());
1379 data.avrSolver->SetReactivePower(data.electricalPower.imag());
1380 data.avrSolver->SetDeltaVelocity((data.speed - data.avrSolver->GetVelocity()) / m_timeStep);
1381 data.avrSolver->SetVelocity(data.speed);
1382
1383 for (int i = 0; i < ctrlRatio; ++i) data.avrSolver->SolveNextStep();
1384
1385 data.fieldVoltage = data.initialFieldVoltage + data.avrSolver->GetFieldVoltage();
1386 }
1387
1388 if (data.useSpeedGovernor && data.speedGovSolver) {
1389 data.speedGovSolver->SetSwitchStatus(syncGenerator->IsOnline());
1390 data.speedGovSolver->SetCurrentTime(m_currentTime);
1391 data.speedGovSolver->SetVelocity(data.speed);
1392 data.speedGovSolver->SetActivePower(data.electricalPower.real());
1393 data.speedGovSolver->SetReactivePower(data.electricalPower.imag());
1394
1395 for (int i = 0; i < ctrlRatio; ++i) data.speedGovSolver->SolveNextStep();
1396
1397 data.pm = data.speedGovSolver->GetMechanicalPower();
1398 }
1399 //syncGenerator->SetElectricalData(data);
1400 }
1401
1402 return true;
1403}
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.

Member Data Documentation

◆ m_ctrlTimeStepMultiplier

double Electromechanical::m_ctrlTimeStepMultiplier = 0.1
protected

Definition at line 134 of file Electromechanical.h.

◆ m_currentPoint

int Electromechanical::m_currentPoint = 0
protected

Definition at line 139 of file Electromechanical.h.

◆ m_currentTime

double Electromechanical::m_currentTime = 0.0
protected

Definition at line 131 of file Electromechanical.h.

◆ m_debugMessage

wxString Electromechanical::m_debugMessage = ""
protected

Definition at line 149 of file Electromechanical.h.

◆ m_errorMsg

wxString Electromechanical::m_errorMsg = _("Unknown error")
protected

Definition at line 115 of file Electromechanical.h.

◆ m_eventOccurrenceList

std::vector<bool> Electromechanical::m_eventOccurrenceList
protected

Definition at line 142 of file Electromechanical.h.

◆ m_eventTimeList

std::vector<double> Electromechanical::m_eventTimeList
protected

Definition at line 141 of file Electromechanical.h.

◆ m_iBus

std::vector<std::complex<double> > Electromechanical::m_iBus
protected

Definition at line 127 of file Electromechanical.h.

◆ m_iterationsNum

int Electromechanical::m_iterationsNum = 0.0
protected

Definition at line 147 of file Electromechanical.h.

◆ m_iterationsNumVector

std::vector<double> Electromechanical::m_iterationsNumVector
protected

Definition at line 148 of file Electromechanical.h.

◆ m_maxIterations

int Electromechanical::m_maxIterations = 100
protected

Definition at line 136 of file Electromechanical.h.

◆ m_parent

wxWindow* Electromechanical::m_parent = nullptr
protected

Definition at line 114 of file Electromechanical.h.

◆ m_plotTime

double Electromechanical::m_plotTime = 1e-2
protected

Definition at line 132 of file Electromechanical.h.

◆ m_powerSystemBase

double Electromechanical::m_powerSystemBase = 100e6
protected

Definition at line 129 of file Electromechanical.h.

◆ m_refSpeed

double Electromechanical::m_refSpeed = 2.0 * M_PI * 60.0
protected

Definition at line 119 of file Electromechanical.h.

◆ m_saturationTolerance

double Electromechanical::m_saturationTolerance = 1e-8
protected

Definition at line 137 of file Electromechanical.h.

◆ m_simData

SimulationData Electromechanical::m_simData
protected

Definition at line 116 of file Electromechanical.h.

◆ m_simTime

double Electromechanical::m_simTime = 10.0
protected

Definition at line 130 of file Electromechanical.h.

◆ m_systemFreq

double Electromechanical::m_systemFreq = 60.0
protected

Definition at line 118 of file Electromechanical.h.

◆ m_timeStep

double Electromechanical::m_timeStep = 1e-2
protected

Definition at line 133 of file Electromechanical.h.

◆ m_timeVector

std::vector<double> Electromechanical::m_timeVector
protected

Definition at line 144 of file Electromechanical.h.

◆ m_tolerance

double Electromechanical::m_tolerance = 1e-8
protected

Definition at line 135 of file Electromechanical.h.

◆ m_useCOI

bool Electromechanical::m_useCOI = false
protected

Definition at line 120 of file Electromechanical.h.

◆ m_vBus

std::vector<std::complex<double> > Electromechanical::m_vBus
protected

Definition at line 126 of file Electromechanical.h.

◆ m_yBus

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

Definition at line 122 of file Electromechanical.h.

◆ m_yBusL

std::vector<std::vector<std::complex<double> > > Electromechanical::m_yBusL
protected

Definition at line 124 of file Electromechanical.h.

◆ m_yBusU

std::vector<std::vector<std::complex<double> > > Electromechanical::m_yBusU
protected

Definition at line 123 of file Electromechanical.h.


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