Power System Platform  2026w10a-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 21 of file Electromechanical.cpp.

22{
23 m_parent = parent;
24 GetElementsFromList(elementList);
25 SetEventTimeList();
26
27 Bus dummyBus;
28 m_simData = data;
29 m_powerSystemBase = dummyBus.GetValueFromUnit(data.basePower, data.basePowerUnit);
30 m_systemFreq = data.stabilityFrequency;
31 m_simTime = data.stabilitySimulationTime;
32 m_timeStep = data.timeStep;
33 m_tolerance = data.stabilityTolerance;
34 m_maxIterations = data.stabilityMaxIterations;
35
36 m_ctrlTimeStepMultiplier = 1.0 / static_cast<double>(data.controlTimeStepRatio);
37
38 m_plotTime = data.plotTime;
39 m_useCOI = data.useCOI;
40 // If the user use all load as ZIP, updates the portions of each model, otherwise use constant impedance only.
41 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
42 Load* load = *it;
43 auto loadData = load->GetElectricalData();
44 if (!loadData.useCompLoad) { // If no individual load composition defined.
45 if (data.useCompLoads) { // Use general composition, if defined.
46 loadData.constImpedanceActive = data.constImpedanceActive;
47 loadData.constCurrentActive = data.constCurrentActive;
48 loadData.constPowerActive = data.constPowerActive;
49 loadData.constImpedanceReactive = data.constImpedanceReactive;
50 loadData.constCurrentReactive = data.constCurrentReactive;
51 loadData.constPowerReactive = data.constPowerReactive;
52 }
53 else { // Otherwise, use constant impedance.
54 loadData.constImpedanceActive = 100.0;
55 loadData.constCurrentActive = 0.0;
56 loadData.constPowerActive = 0.0;
57 loadData.constImpedanceReactive = 100.0;
58 loadData.constCurrentReactive = 0.0;
59 loadData.constPowerReactive = 0.0;
60 }
61 }
62
63 loadData.constCurrentUV = data.underVoltageConstCurrent / 100.0;
64 loadData.constPowerUV = data.underVoltageConstPower / 100.0;
65 load->SetElectricalData(loadData);
66 }
67}
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 69 of file Electromechanical.cpp.

69{ }

Member Function Documentation

◆ CalculateBusesFrequency()

void Electromechanical::CalculateBusesFrequency ( bool  hasEvent)
protected

Definition at line 1771 of file Electromechanical.cpp.

1772{
1773 bool ignoreEventStep = true;
1774 for (auto& bus : m_busList) {
1775 auto data = bus->GetElectricalData();
1776 if (!data.plotBus) continue;
1777
1778 if (m_simData.busFreqEstimation == BusFreqEstimation::WASHOUT_FILTER) {
1779 //tex:
1780 // Low-pass filter:
1781 // $$\frac{d}{dt}x_{\theta} = x_{\theta}' = \frac{\omega^{-1}(\theta_k - \theta_0) - x_{\theta}}{T_f}$$
1782 // Washout filter:
1783 // $$\frac{d}{dt}\Delta\omega_{k} = \Delta\omega_{k}' = \frac{x_{\theta}' - \Delta\omega_{k}}{T_w}$$
1784 // Trapezoidal rule:
1785 // $$ y_{k} = y_{k-1} + 0.5 h \cdot (y_{k-1}'+y_{k}') $$
1786
1787 double tf = m_simData.tf;
1788 double tw = m_simData.tw;
1789 double error = 1.0;
1790 double theta0 = std::arg(data.number >= 0 ? data.voltage : 0.0);
1791
1792 // Low-pass filter
1793 auto fdxt = [this, tf, theta0](double theta, double xt) {
1794 return (1.0 / tf) * ((1.0 / (2.0 * M_PI * m_systemFreq)) * (theta - theta0) - xt);
1795 };
1796
1797 // Washout filter
1798 auto fddw = [this, tw](double dxt, double dw) {
1799 return (1.0 / tw) * (dxt - dw);
1800 };
1801
1802 double theta = std::arg(data.number >= 0 ? m_vBus[data.number] : theta0);
1803
1804 double xt = 0, dw = 0, dxt = 0, ddw = 0;
1805 int iterations = 0;
1806 while (error > 1e-6 && iterations <= 100) {
1807 double oldXT = xt, oldDW = dw;
1808
1809 dxt = fdxt(theta, xt);
1810 xt = data.filteredAngle + 0.5 * m_timeStep * (data.dxt + dxt); // Trapezoidal rule
1811 error = std::abs(xt - oldXT);
1812
1813 ddw = fddw(dxt, dw);
1814 dw = data.filteredVelocity + 0.5 * m_timeStep * (data.ddw + ddw); // Trapezoidal rule
1815 error = std::max(error, std::abs(dw - oldDW));
1816 iterations++;
1817 }
1818
1819 //if (ignoreEventStep && hasEvent) {
1820 // xt = data.filteredAngle;
1821 // dw = 0.0;
1822 // dxt = 0.0;
1823 // ddw = 0.0;
1824 //}
1825
1826 data.filteredAngle = xt;
1827 data.filteredVelocity = dw;
1828 data.dxt = dxt;
1829 data.ddw = ddw;
1830
1831 double busVelocity = m_refSpeed + dw * (2.0 * M_PI * m_systemFreq);
1832 data.stabFreq = busVelocity / (2.0 * M_PI);
1833
1834 bus->SetElectricalData(data);
1835 }
1836 else if (m_simData.busFreqEstimation == BusFreqEstimation::ANGLE_DERIVATION) {
1837 double newAngle = std::arg(data.number >= 0 ? m_vBus[data.number] : 0.0);
1838
1839
1840 //tex: $$\Delta \omega^k=\frac{\theta^k - \theta^{k-1}}{h}$$
1841 double dw = (newAngle - data.oldAngle) / m_timeStep;
1842 //double dw = (1.0 / (2.0 * M_PI * m_systemFreq)) * ((newAngle - data.oldAngle) / m_timeStep); // p.u.
1843
1844 if (m_simData.ignoreBusFreqEventStep && hasEvent) dw = 0.0;
1845
1846 //tex: $$f^k= \frac{\omega_{CoI}+\Delta \omega^k}{2\pi}$$
1847 data.stabFreq = (m_refSpeed + dw) / (2.0 * M_PI);
1848 //data.stabFreq = ((2.0 * M_PI * m_systemFreq) + dw) / (2.0 * M_PI);
1849
1850 data.oldAngle = newAngle;
1851 }
1852 bus->SetElectricalData(data);
1853 }
1854}
std::vector< Bus * > m_busList
List of buses in the system.

◆ CalculateIndMachinesTransientValues()

bool Electromechanical::CalculateIndMachinesTransientValues ( IndMotor motor)
protected

Definition at line 1975 of file Electromechanical.cpp.

1976{
1977 auto data = motor->GetElectricalData();
1978 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
1979 double k = 1.0; // Power base change factor.
1980 if (data.useMachinePowerAsBase) {
1981 double oldBase = motor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1982 k = m_powerSystemBase / oldBase;
1983 }
1984
1985 data.terminalVoltage = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalData().voltage;
1986
1987 // Calculate the induction machine transient constants at the machine base
1988 double r1t = data.r1 * k;
1989 double r2t = data.r2 * k;
1990 double x1t = data.x1 * k;
1991 double x2t = data.x2 * k;
1992 double xmt = data.xm * k;
1993 data.r1t = r1t;
1994 data.r2t = r2t;
1995 data.x1t = x1t;
1996 data.x2t = x2t;
1997 data.xmt = xmt;
1998
1999 double xt = x1t + (x2t * xmt) / (x2t + xmt);
2000 double x0 = x1t + xmt;
2001 data.xt = xt;
2002 data.x0 = x0;
2003
2004 double p = dataPU.activePower;
2005 double v = std::abs(data.terminalVoltage);
2006
2007 //[Ref.] Induction Motor Static Models for Power Flow and Voltage stability Studies
2008 // If the motor is offline, calculate the nominal slip to user-defined power input and 1.0 p.u. voltage
2009 if (!motor->IsOnline()) v = 1.0;
2010 double r1 = data.r1t;
2011 double r2 = data.r2t;
2012 if (data.useKf) r2 *= (1.0 + data.kf * r2t);
2013 double x1 = data.x1t;
2014 double x2 = data.x2t;
2015 double xm = data.xmt;
2016 double k1 = x2 + xm;
2017 double k2 = -x1 * k1 - x2 * xm;
2018 double k3 = xm + x1;
2019 double k4 = r1 * k1;
2020 double a = p * (r1 * r1 + k3 * k3) - v * v * r1;
2021 double b = 2.0 * p * (r1 * k2 + k3 * k4) - v * v * (k2 + k1 * k3);
2022 double c = p * (k2 * k2 + k4 * k4) - v * v * k1 * k4;
2023 double d = (b * b - 4.0 * a * c);
2024 if (d < 0.0) {
2025 m_errorMsg = _("Error on initializate the slip of \"") + data.name + _("\".");
2026 return false;
2027 }
2028 double r2_s = (-b + std::sqrt(d)) / (2.0 * a);
2029 data.s0 = r2 / r2_s;
2030
2031 double qa = k1 * (r2_s * r1 - x1 * k1 - x2 * xm);
2032 double qb = r2_s * (r2_s * (xm + x1) + r1 * k1);
2033 double qc = r2_s * r1 - x1 * k1 - x2 * xm;
2034 double qd = r2_s * (xm + x1) + r1 * k1;
2035 data.q0 = (-v * v * (qa - qb)) / (qc * qc + qd * qd);
2036
2037 data.t0 = (x2t + xmt) / (2.0 * M_PI * m_systemFreq * r2);
2038
2039 motor->SetElectricalData(data);
2040 return true;
2041}
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 910 of file Electromechanical.cpp.

911{
912 // Reset injected currents vector
913 for (unsigned int i = 0; i < m_iBus.size(); ++i) m_iBus[i] = std::complex<double>(0.0, 0.0);
914
915 // Synchronous machines
916 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
917 SyncGenerator* syncGenerator = *it;
918 auto data = syncGenerator->GetElectricalData();
919 if (syncGenerator->IsOnline()) {
920 double k = 1.0; // Power base change factor.
921 if (data.useMachineBase) {
922 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
923 k = m_powerSystemBase / oldBase;
924 }
925
926 double ra = data.armResistance * k;
927 double xp = data.potierReactance * k;
928 if (xp == 0.0) xp = 0.8 * data.transXd * k;
929
930 int n = static_cast<Bus*>(syncGenerator->GetParentList()[0])->GetElectricalData().number;
931 std::complex<double> e = std::complex<double>(0.0, 0.0);
932 std::complex<double> v = m_vBus[n];
933 std::complex<double> iInj = m_iBus[n];
934
935 auto smModelData = GetSyncMachineModelData(syncGenerator);
936 DQ0toABC(smModelData.ed, smModelData.eq, data.delta, e);
937 double xd = smModelData.xd;
938 double xq = smModelData.xq;
939
940 double sd = data.sd;
941 double sq = data.sq;
942 double id, iq;
943
944 // Calculate the saturation effect
945 if (data.satFactor != 0.0) {
946 if (!CalculateSyncMachineSaturation(syncGenerator, id, iq, sd, sq, false, k)) return false;
947 }
948
949 double xdq, xds, xqs, xdqs;
950 xdq = 0.5 * (xd + xq);
951 xds = (xd - xp) / sd + xp;
952 xqs = (xq - xp) / sq + xp;
953 xdqs = 0.5 * (xds + xqs);
954
955 std::complex<double> y0 = std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0);
956 std::complex<double> iUnadjusted = y0 * v;
957
958 // [Ref] Arrillaga, J.; Arnold, C. P.. "Computer Modelling of Electrical Power Systems". Pg. 225-226
959 // [Ref] Dommell, H. W.; Sato, N.. "Fast transient stability solutions". IEEE Transactions on Power
960 // Apparatus and Systems, PAS-91 (4), 1643-1650
961 std::complex<double> iSaliency = std::complex<double>(0.0, -((0.5 * (xqs - xds)) / (ra * ra + xds * xqs))) *
962 (std::conj(e) - std::conj(v));
963 iSaliency = iSaliency * std::cos(2.0 * data.delta) +
964 iSaliency * std::complex<double>(0.0, std::sin(2.0 * data.delta));
965
966 // [Ref] Arrillaga, J.; Arnold, C. P.; Computer Modelling of Electrical Power Systems. Pg. 258-259
967 std::complex<double> y0s = std::complex<double>(ra, -xdqs) / std::complex<double>(ra * ra + xds * xqs, 0.0);
968 std::complex<double> iSaturation = y0s * (e - v);
969
970 iInj = iUnadjusted + iSaliency + iSaturation;
971
972 m_iBus[n] += iInj;
973
974 // Remove the current flowing through y0 (i.e. iUnadjusted in this case, y0 is inserted in admittance
975 // matrix) to calculate the electrical power.
976 std::complex<double> iMachine = iInj - iUnadjusted;
977 data.electricalPower = v * std::conj(iMachine);
978
979 ABCtoDQ0(iMachine, data.delta, id, iq);
980
981 data.id = id;
982 data.iq = iq;
983 data.sd = sd;
984 data.sq = sq;
985 }
986 else {
987 data.electricalPower = std::complex<double>(0.0, 0.0);
988 }
989
990 syncGenerator->SetElectricalData(data);
991 }
992 // Induction motors
993 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
994 IndMotor* motor = *it;
995 auto data = motor->GetElectricalData();
996 if (motor->IsOnline()) {
997 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalData().number;
998 std::complex<double> y0 = std::complex<double>(1.0, 0.0) / std::complex<double>(data.r1t, data.xt);
999 std::complex<double> v = m_vBus[n];
1000 std::complex<double> e = std::complex<double>(data.tranEr, data.tranEm);
1001 std::complex<double> iInj = y0 * e;
1002 m_iBus[n] += iInj;
1003
1004 std::complex<double> iMachine = y0 * (v - e);
1005
1006 data.ir = std::real(iMachine);
1007 data.im = std::imag(iMachine);
1008 }
1009 else {
1010 data.ir = 0.0;
1011 data.im = 0.0;
1012 }
1013 motor->SetElectricalData(data);
1014 }
1015
1016 // Loads
1017 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
1018 Load* load = *it;
1019 auto data = load->GetElectricalData();
1020 Bus* bus = nullptr;
1021 if (!GetParentBus(load, bus)) {
1022 load->SetOnline(false);
1023 }
1024 else if (load->IsOnline()) {
1025
1026 data.voltage = m_vBus[bus->GetElectricalData().number];
1027 double vAbs = std::abs(data.voltage);
1028
1029 double pz, pi, pp, qz, qi, qp;
1030 pz = data.pz0 * std::pow(vAbs / data.v0, 2);
1031 pi = data.pi0 * (vAbs / data.v0);
1032 pp = data.pp0;
1033 qz = data.qz0 * std::pow(vAbs / data.v0, 2);
1034 qi = data.qi0 * (vAbs / data.v0);
1035 qp = data.qp0;
1036
1037 // If voltage value is low, set the ZIP load to constant impedance.
1038 if (vAbs < data.constCurrentUV) {
1039 pi = data.pi0 * (data.constCurrentUV / data.v0) * std::pow(vAbs / data.constCurrentUV, 2);
1040 qi = data.qi0 * (data.constCurrentUV / data.v0) * std::pow(vAbs / data.constCurrentUV, 2);
1041 }
1042 if (vAbs < data.constPowerUV) {
1043 pp *= std::pow(vAbs / data.constPowerUV, 2);
1044 qp *= std::pow(vAbs / data.constPowerUV, 2);
1045 }
1046
1047 double activePower = pz + pi + pp;
1048 double reactivePower = qz + qi + qp;
1049
1050 std::complex<double> newY = std::complex<double>(activePower, -reactivePower) / (vAbs * vAbs);
1051 m_iBus[bus->GetElectricalData().number] += (data.y0 - newY) * data.voltage;
1052
1053 data.electricalPower = std::complex<double>(activePower, reactivePower);
1054 }
1055 else {
1056 data.voltage = std::complex<double>(0.0, 0.0);
1057 data.electricalPower = std::complex<double>(0.0, 0.0);
1058 }
1059
1060 load->SetElectricalData(data);
1061 }
1062 return true;
1063}
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:447
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 1137 of file Electromechanical.cpp.

1138{
1139 double w0 = 2.0 * M_PI * m_systemFreq;
1140
1141 auto data = indMotor->GetElectricalData();
1142
1143 // If the velocity is too low set mechanical torque to zero (a, b and c coeficients)
1144 if (data.slip > 0.99999) {
1145 data.as = 0.0;
1146 data.bs = 0.0;
1147 data.cs = 0.0;
1148 }
1149 else {
1150 data.as = data.aCalc;
1151 data.bs = data.bCalc;
1152 data.cs = data.cCalc;
1153 }
1154
1155 // Mechanical equations
1156 // s
1157 data.icSlip.m = m_timeStep / ((4.0f * data.inertia / w0) / k + data.bs * m_timeStep);
1158 data.icSlip.c = data.slip * (1.0 - 2.0 * data.bs * data.icSlip.m) +
1159 data.icSlip.m * (2.0 * data.as + data.cs * data.slip * data.slip - data.te);
1160
1161 // Electrical equations
1162 // Er
1163 data.icTranEr.m = m_timeStep / (2.0 * data.t0 + m_timeStep);
1164 data.icTranEr.c = data.tranEr * (1.0 - 2.0 * data.icTranEr.m) +
1165 data.icTranEr.m * (w0 * data.t0 * data.slip * data.tranEm - (data.x0 - data.xt) * im);
1166 // Em
1167 data.icTranEm.m = m_timeStep / (2.0 * data.t0 + m_timeStep);
1168 data.icTranEm.c = data.tranEm * (1.0 - 2.0 * data.icTranEm.m) -
1169 data.icTranEm.m * (w0 * data.t0 * data.slip * data.tranEr - (data.x0 - data.xt) * ir);
1170
1171 indMotor->SetElectricalData(data);
1172}

◆ CalculateIntegrationConstants() [2/2]

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

Definition at line 1065 of file Electromechanical.cpp.

1066{
1067 CalculateReferenceSpeed();
1068 auto data = syncGenerator->GetElectricalData();
1069
1070 double syncXd, syncXq, transXd, transXq, subXd, subXq;
1071 syncXd = data.syncXd * k;
1072 syncXq = data.syncXq * k;
1073 transXd = data.transXd * k;
1074 transXq = data.transXq * k;
1075 subXd = data.subXd * k;
1076 subXq = data.subXq * k;
1077
1078 if (syncXq == 0.0) syncXq = syncXd;
1079 if (transXq == 0.0) transXq = transXd;
1080 if (subXd == 0.0) subXd = subXq;
1081 if (subXq == 0.0) subXq = subXd;
1082
1083 double transTd0, transTq0, subTd0, subTq0;
1084 transTd0 = data.transTd0;
1085 transTq0 = data.transTq0;
1086 subTd0 = data.subTd0;
1087 subTq0 = data.subTq0;
1088
1089 if (subTd0 == 0.0) subTd0 = subTq0;
1090 if (subTq0 == 0.0) subTq0 = subTd0;
1091
1092 // Speed
1093 data.icSpeed.m = m_timeStep / ((4.0f * data.inertia / m_refSpeed) / k + m_timeStep * data.damping * k);
1094 data.icSpeed.c = (1.0f - 2.0f * data.icSpeed.m * data.damping * k) * data.speed +
1095 data.icSpeed.m * (data.pm - data.pe + 2.0f * m_refSpeed * data.damping * k);
1096
1097 // Delta
1098 data.icDelta.m = 0.5f * m_timeStep;
1099 data.icDelta.c = data.delta + data.icDelta.m * (data.speed - 2.0f * m_refSpeed);
1100
1101 // Eq'
1102 if (data.model == Machines::SM_MODEL_2 || data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 ||
1103 data.model == Machines::SM_MODEL_5) {
1104 data.icTranEq.m = m_timeStep / (2.0f * transTd0 + m_timeStep);
1105 data.icTranEq.c = (1.0 - data.icTranEq.m * (1.0 + data.sd)) * data.tranEq +
1106 data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id);
1107 }
1108
1109 // Ed'
1110 if (data.model == Machines::SM_MODEL_3 || data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
1111 data.icTranEd.m = m_timeStep / (2.0f * transTq0 + m_timeStep);
1112 data.icTranEd.c =
1113 (1.0 - data.icTranEd.m * (1.0 + data.sq)) * data.tranEd - data.icTranEd.m * (syncXq - transXq) * iq;
1114 }
1115
1116 // Eq''
1117 if (data.model == Machines::SM_MODEL_4 || data.model == Machines::SM_MODEL_5) {
1118 data.icSubEq.m = m_timeStep / (2.0f * subTd0 + m_timeStep);
1119 data.icSubEq.c = (1.0 - data.icSubEq.m * (1.0 + data.sd)) * data.subEq +
1120 data.icSubEq.m * (data.sd * data.tranEq + (transXd - subXd) * id);
1121 }
1122 // Ed''
1123 if (data.model == Machines::SM_MODEL_4) {
1124 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1125 data.icSubEd.c =
1126 (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd - data.icSubEd.m * (syncXq - subXq) * iq;
1127 }
1128 if (data.model == Machines::SM_MODEL_5) {
1129 data.icSubEd.m = m_timeStep / (2.0f * subTq0 + m_timeStep);
1130 data.icSubEd.c = (1.0f - data.icSubEd.m * (1.0 + data.sq)) * data.subEd +
1131 data.icSubEd.m * (data.sq * data.tranEd - (transXq - subXq) * iq);
1132 }
1133
1134 syncGenerator->SetElectricalData(data);
1135}

◆ CalculateIntVariables() [1/2]

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

Definition at line 1606 of file Electromechanical.cpp.

1607{
1608 double error = 0.0;
1609 auto data = indMotor->GetElectricalData();
1610 double w0 = 2.0 * M_PI * m_systemFreq;
1611
1612 // Mechanical differential equations
1613 // Using Newton method to solve the non-linear slip equation: s = Cs + Ms * (C * s^2 - Te):
1614 double slip = data.slip; // Initial value. CAN BE THE PROBLEM ON MOTOR START!
1615 double ds = (data.icSlip.c + data.icSlip.m * (data.cs * slip * slip - te) - slip) /
1616 (1.0 - 2.0 * data.icSlip.m * data.cs * slip * slip);
1617 int iteration = 0;
1618 while (std::abs(ds) > 1e-8) {
1619 slip += ds;
1620 ds = (data.icSlip.c + data.icSlip.m * (data.cs * slip * slip - te) - slip) /
1621 (1.0 - 2.0 * data.icSlip.m * data.cs * slip * slip);
1622 iteration++;
1623 if (iteration > m_maxIterations) break;
1624 }
1625
1626 //if(!indMotor->IsOnline()) slip = 1.0 - 1e-7;
1627 error = std::max(error, std::abs(data.slip - slip));
1628 data.slip = slip;
1629
1630 // Change T'0 with the cage factor
1631 if (data.useKf)
1632 data.t0 = (data.x2t + data.xmt) / (2.0 * M_PI * m_systemFreq * data.r2t * (1.0 + data.kf * data.r2t));
1633
1634 // Electrical differential equations
1635 double tranEr = data.icTranEr.c + data.icTranEr.m * (w0 * data.t0 * slip * data.tranEm - (data.x0 - data.xt) * im);
1636 error = std::max(error, std::abs(data.tranEr - tranEr));
1637
1638 double tranEm = data.icTranEm.c - data.icTranEm.m * (w0 * data.t0 * slip * data.tranEr - (data.x0 - data.xt) * ir);
1639 error = std::max(error, std::abs(data.tranEm - tranEm));
1640
1641 data.tranEr = tranEr;
1642 data.tranEm = tranEm;
1643
1644 indMotor->SetElectricalData(data);
1645 return error;
1646}

◆ 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 1476 of file Electromechanical.cpp.

1483{
1484 double error = 0.0;
1485 auto data = syncGenerator->GetElectricalData();
1486
1487 // Mechanical differential equations.
1488 double w = data.icSpeed.c + data.icSpeed.m * (data.pm - pe);
1489 error = std::max(error, std::abs(data.speed - w) / m_refSpeed);
1490
1491 double delta = data.icDelta.c + data.icDelta.m * w;
1492 error = std::max(error, std::abs(data.delta - delta));
1493
1494 data.speed = w;
1495 data.delta = delta;
1496
1497 // Electrical differential equations
1498 switch (data.model) {
1499 case Machines::SM_MODEL_1: {
1500 // There is no differential equations.
1501 } break;
1502 case Machines::SM_MODEL_2: {
1503 double syncXd, transXd;
1504 syncXd = data.syncXd * k;
1505 transXd = data.transXd * k;
1506
1507 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
1508 (1.0 + data.icTranEq.m * (sd - 1.0));
1509 error = std::max(error, std::abs(data.tranEq - tranEq));
1510
1511 data.tranEq = tranEq;
1512 } break;
1513 case Machines::SM_MODEL_3: {
1514 double syncXd, syncXq, transXd, transXq;
1515 syncXd = data.syncXd * k;
1516 syncXq = data.syncXq * k;
1517 transXd = data.transXd * k;
1518 transXq = data.transXq * k;
1519 if (syncXq == 0.0) syncXq = syncXd;
1520 if (transXq == 0.0) transXq = transXd;
1521
1522 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
1523 (1.0 + data.icTranEq.m * (sd - 1.0));
1524 error = std::max(error, std::abs(data.tranEq - tranEq));
1525
1526 double tranEd =
1527 (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
1528 error = std::max(error, std::abs(data.tranEd - tranEd));
1529
1530 data.tranEq = tranEq;
1531 data.tranEd = tranEd;
1532
1533 if (!syncGenerator->IsOnline()) {
1534 std::complex<double> e;
1535 DQ0toABC(data.tranEd, data.tranEq, data.delta, e);
1536 data.terminalVoltage = e;
1537 }
1538 } break;
1539 case Machines::SM_MODEL_4: {
1540 double syncXd, syncXq, transXd, subXd, subXq;
1541 syncXd = data.syncXd * k;
1542 syncXq = data.syncXq * k;
1543 transXd = data.transXd * k;
1544 subXd = data.subXd * k;
1545 subXq = data.subXq * k;
1546 if (syncXq == 0.0) syncXq = syncXd;
1547 if (subXd == 0.0) subXd = subXq;
1548 if (subXq == 0.0) subXq = subXd;
1549
1550 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
1551 (1.0 + data.icTranEq.m * (sd - 1.0));
1552 error = std::max(error, std::abs(data.tranEq - tranEq));
1553
1554 double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) * id)) /
1555 (1.0 + data.icSubEq.m * (sd - 1.0));
1556 error = std::max(error, std::abs(data.subEq - subEq));
1557
1558 double subEd =
1559 (data.icSubEd.c - data.icSubEd.m * ((syncXq - subXq) * iq)) / (1.0 + data.icSubEd.m * (sq - 1.0));
1560 error = std::max(error, std::abs(data.subEd - subEd));
1561
1562 data.tranEq = tranEq;
1563 data.subEq = subEq;
1564 data.subEd = subEd;
1565 } break;
1566 case Machines::SM_MODEL_5: {
1567 double syncXd, syncXq, transXd, transXq, subXd, subXq;
1568 syncXd = data.syncXd * k;
1569 syncXq = data.syncXq * k;
1570 transXd = data.transXd * k;
1571 transXq = data.transXq * k;
1572 subXd = data.subXd * k;
1573 subXq = data.subXq * k;
1574 if (syncXq == 0.0) syncXq = syncXd;
1575 if (transXq == 0.0) transXq = transXd;
1576 if (subXd == 0.0) subXd = subXq;
1577 if (subXq == 0.0) subXq = subXd;
1578
1579 double tranEq = (data.icTranEq.c + data.icTranEq.m * (data.fieldVoltage + (syncXd - transXd) * id)) /
1580 (1.0 + data.icTranEq.m * (sd - 1.0));
1581 error = std::max(error, std::abs(data.tranEq - tranEq));
1582
1583 double tranEd =
1584 (data.icTranEd.c - data.icTranEd.m * (syncXq - transXq) * iq) / (1.0 + data.icTranEd.m * (sq - 1.0));
1585 error = std::max(error, std::abs(data.tranEd - tranEd));
1586
1587 double subEq = (data.icSubEq.c + data.icSubEq.m * (sd * tranEq + (transXd - subXd) * id)) /
1588 (1.0 + data.icSubEq.m * (sd - 1.0));
1589 error = std::max(error, std::abs(data.subEq - subEq));
1590
1591 double subEd = (data.icSubEd.c + data.icSubEd.m * (sq * tranEd - (transXq - subXq) * iq)) /
1592 (1.0 + data.icSubEd.m * (sq - 1.0));
1593 error = std::max(error, std::abs(data.subEd - subEd));
1594
1595 data.tranEq = tranEq;
1596 data.tranEd = tranEd;
1597 data.subEq = subEq;
1598 data.subEd = subEd;
1599 } break;
1600 }
1601
1602 syncGenerator->SetElectricalData(data);
1603 return error;
1604}

◆ CalculateNonIntVariables() [1/2]

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

Definition at line 1455 of file Electromechanical.cpp.

1456{
1457 auto data = indMotor->GetElectricalData();
1458 if (indMotor->IsOnline()) {
1459 double w0 = 2.0 * M_PI * m_systemFreq;
1460 te = (data.tranEr * ir + data.tranEm * im) / w0;
1461 }
1462 else {
1463 te = ir = im = 0.0;
1464 }
1465 data.te = te;
1466 data.ir = ir;
1467 data.im = im;
1468 data.oldTe = te;
1469 data.oldIr = ir;
1470 data.oldIm = im;
1471 indMotor->SetElectricalData(data);
1472
1473 return true;
1474}

◆ 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 1415 of file Electromechanical.cpp.

1422{
1423 auto data = syncGenerator->GetElectricalData();
1424 Bus* bus = nullptr;
1425 if (GetParentBus(syncGenerator, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalData().number]; }
1426
1427 double vd, vq;
1428 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1429
1430 if (data.satFactor != 0.0) {
1431 if (!CalculateSyncMachineSaturation(syncGenerator, id, iq, sd, sq, true, k)) return false;
1432 data.sd = sd;
1433 data.sq = sq;
1434 data.oldSd = sd;
1435 data.oldSq = sq;
1436 }
1437
1438 if (syncGenerator->IsOnline()) {
1439 pe = id * vd + iq * vq + (id * id + iq * iq) * data.armResistance * k;
1440 }
1441 else {
1442 pe = id = iq = 0.0f;
1443 }
1444 data.pe = pe;
1445 data.id = id;
1446 data.iq = iq;
1447 data.oldPe = pe;
1448 data.oldId = id;
1449 data.oldIq = iq;
1450 syncGenerator->SetElectricalData(data);
1451
1452 return true;
1453}

◆ CalculateReferenceSpeed()

void Electromechanical::CalculateReferenceSpeed ( )
protected

Definition at line 1648 of file Electromechanical.cpp.

1649{
1650 if (m_useCOI) {
1651 double sumHW = 0.0;
1652 double sumH = 0.0;
1653 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1654 SyncGenerator* syncGenerator = *it;
1655 if (syncGenerator->IsOnline()) {
1656 auto data = syncGenerator->GetElectricalData();
1657 double k = 1.0; // Power base change factor.
1658 if (data.useMachineBase) {
1659 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1660 k = m_powerSystemBase / oldBase;
1661 }
1662 sumH += data.inertia / k;
1663 sumHW += data.inertia * data.speed / k;
1664 }
1665 }
1666 m_refSpeed = sumHW / sumH;
1667 }
1668 else {
1669 m_refSpeed = 2.0 * M_PI * m_systemFreq;
1670 }
1671}

◆ 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 1673 of file Electromechanical.cpp.

1680{
1681 // [Ref] Arrillaga, J.; Arnold, C. P.. "Computer Modelling of Electrical Power Systems". Pg. 254-260
1682 auto data = syncMachine->GetElectricalData();
1683 auto smDataModel = GetSyncMachineModelData(syncMachine);
1684
1685 Bus* bus = nullptr;
1686 if (GetParentBus(syncMachine, bus)) { data.terminalVoltage = m_vBus[bus->GetElectricalData().number]; }
1687 //else {
1688 //data.terminalVoltage = 0.0;
1689 //return true;
1690 //}
1691 double idCalc = id;
1692 double iqCalc = iq;
1693 double sdCalc = sd;
1694 double sqCalc = sq;
1695
1696 double vd, vq;
1697 ABCtoDQ0(data.terminalVoltage, data.delta, vd, vq);
1698 double deltaVd = smDataModel.ed - vd;
1699 double deltaVq = smDataModel.eq - vq;
1700
1701 double ra = data.armResistance * k;
1702 double xd = smDataModel.xd;
1703 double xq = smDataModel.xq;
1704
1705 double syncXd = data.syncXd * k;
1706 double syncXq = data.syncXq * k;
1707 if (data.model == Machines::SM_MODEL_1) {
1708 syncXq = data.transXd * k;
1709 syncXd = syncXq;
1710 }
1711 else if (data.syncXq == 0.0)
1712 syncXq = data.syncXd * k;
1713
1714 double xp = data.potierReactance * k;
1715 if (xp == 0.0) xp = 0.8 * data.transXd * k;
1716 double satFacd = (data.satFactor - 1.2) / std::pow(1.2, 7);
1717 double satFacq = satFacd * (syncXq / syncXd);
1718
1719 bool exit = false;
1720 int iterations = 0;
1721 while (!exit) {
1722 double oldSd = sdCalc;
1723 double oldSq = sqCalc;
1724
1725 // Saturated reactances.
1726 double xds = (xd - xp) / sdCalc + xp;
1727 double xqs = (xq - xp) / sqCalc + xp;
1728 // dq currents.
1729 double den = 1.0 / (ra * ra + xds * xqs);
1730 iqCalc = den * (ra * deltaVq + xds * deltaVd);
1731 idCalc = den * (-xqs * deltaVq + ra * deltaVd);
1732 // Potier voltages
1733 double epq = vq + ra * iqCalc - xp * idCalc;
1734 double epd = vd + ra * idCalc + xp * iqCalc;
1735 // Saturation factors.
1736 // Gauss
1737 /*sdCalc = 1.0 + satFacd * std::pow(epq, 6);
1738 sqCalc = 1.0 + satFacq * std::pow(epd, 6);*/
1739
1740 // Newton-raphson
1741 double f1 = 1.0 - sdCalc + satFacd * std::pow(epq, 6);
1742 double f2 = 1.0 - sqCalc + satFacq * std::pow(epd, 6);
1743 double dF1dSd =
1744 (6.0 * satFacd * std::pow(epq, 5) * xp * (xd - xp) * deltaVq) / ((sdCalc - 1.0) * xp + xd) - 1.0;
1745 double dF2dSq =
1746 (6.0 * satFacq * std::pow(epd, 5) * xp * (xq - xp) * deltaVd) / ((sqCalc - 1.0) * xp + xq) - 1.0;
1747
1748 sdCalc = sdCalc - f1 / dF1dSd;
1749 sqCalc = sqCalc - f2 / dF2dSq;
1750
1751 double error = std::abs(sdCalc - oldSd) + std::abs(sqCalc - oldSq);
1752 if (error < m_saturationTolerance) exit = true;
1753
1754 iterations++;
1755 if ((iterations >= m_maxIterations) && !exit) {
1756 m_errorMsg =
1757 _("It was not possible to solve the saturation of the synchronous machine \"") + data.name + wxT("\".");
1758 return false;
1759 }
1760 }
1761
1762 sd = sdCalc;
1763 sq = sqCalc;
1764 if (updateCurrents) {
1765 id = idCalc;
1766 iq = iqCalc;
1767 }
1768 return true;
1769}

◆ EventTrigger()

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

Definition at line 521 of file Electromechanical.cpp.

522{
523 return (((eventTime - m_timeStep) < currentTime) && (eventTime >= currentTime));
524}

◆ 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 1951 of file Electromechanical.cpp.

1952{
1953 auto data = motor->GetElectricalData();
1954 auto dataPU = motor->GetPUElectricalData(m_powerSystemBase);
1955 std::complex<double> v = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalData().voltage;
1956 std::complex<double> y0 = (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
1957 // The difference between calculated and defined reactive power
1958 std::complex<double> yq = std::complex<double>(0.0, data.q0 - dataPU.reactivePower) / (std::abs(v) * std::abs(v));
1959 return y0 + yq;
1960}

◆ 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 526 of file Electromechanical.cpp.

527{
528 auto data = generator->GetElectricalData();
529 double k = 1.0; // Power base change factor.
530 if (data.useMachineBase) {
531 double oldBase = generator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
532 k = m_powerSystemBase / oldBase;
533 }
534
535 double ra = data.armResistance * k;
536 auto smModelData = GetSyncMachineModelData(generator);
537 double xd = smModelData.xd;
538 double xq = smModelData.xq;
539 double xdq = 0.5 * (xd + xq);
540 return (std::complex<double>(ra, -xdq) / std::complex<double>(ra * ra + xd * xq, 0.0));
541}

◆ GetSyncMachineModelData()

SyncMachineModelData Electromechanical::GetSyncMachineModelData ( SyncGenerator syncMachine)
protected

Definition at line 1856 of file Electromechanical.cpp.

1857{
1858 SyncMachineModelData smModelData;
1859
1860 auto data = syncMachine->GetElectricalData();
1861 double k = 1.0; // Power base change factor.
1862 if (data.useMachineBase) {
1863 double oldBase = syncMachine->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1864 k = m_powerSystemBase / oldBase;
1865 }
1866
1867 switch (data.model) {
1868 case Machines::SM_MODEL_1: {
1869 smModelData.ed = data.tranEd;
1870 smModelData.eq = data.tranEq;
1871 smModelData.xq = data.transXd * k;
1872 smModelData.xd = smModelData.xq;
1873 } break;
1874 case Machines::SM_MODEL_2: {
1875 smModelData.ed = data.tranEd;
1876 smModelData.eq = data.tranEq;
1877 smModelData.xd = data.transXd * k;
1878 smModelData.xq = data.transXq * k;
1879 if (smModelData.xq == 0.0) {
1880 smModelData.xq = data.syncXq * k;
1881 if (smModelData.xq == 0.0) { smModelData.xq = data.syncXd * k; }
1882 }
1883 } break;
1884 case Machines::SM_MODEL_3: {
1885 smModelData.ed = data.tranEd;
1886 smModelData.eq = data.tranEq;
1887 smModelData.xd = data.transXd * k;
1888 smModelData.xq = data.transXq * k;
1889 if (smModelData.xq == 0.0) smModelData.xq = smModelData.xd;
1890 } break;
1891 case Machines::SM_MODEL_4:
1892 case Machines::SM_MODEL_5: {
1893 smModelData.ed = data.subEd;
1894 smModelData.eq = data.subEq;
1895 smModelData.xd = data.subXd * k;
1896 smModelData.xq = data.subXq * k;
1897 if (smModelData.xd == 0.0) smModelData.xd = smModelData.xq;
1898 if (smModelData.xq == 0.0) smModelData.xq = smModelData.xd;
1899 } break;
1900 }
1901 return smModelData;
1902}
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 196 of file Electromechanical.cpp.

197{
198 for (unsigned int i = 0; i < m_eventTimeList.size(); ++i) {
199 if (!m_eventOccurrenceList[i]) {
200 if (EventTrigger(m_eventTimeList[i], currentTime)) {
201 m_eventOccurrenceList[i] = true;
202 return true;
203 }
204 }
205 }
206 return false;
207}

◆ InitializeDynamicElements()

bool Electromechanical::InitializeDynamicElements ( )
protected

Definition at line 543 of file Electromechanical.cpp.

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

◆ InsertIndMachinesOnYBus()

bool Electromechanical::InsertIndMachinesOnYBus ( )
protected

Definition at line 1962 of file Electromechanical.cpp.

1963{
1964 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1965 IndMotor* motor = *it;
1966 if (!CalculateIndMachinesTransientValues(motor)) return false;
1967 if (motor->IsOnline()) {
1968 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalData().number;
1969 m_yBus[n][n] += GetIndMachineAdmittance(motor);
1970 }
1971 }
1972 return true;
1973}

◆ InsertSyncMachinesOnYBus()

void Electromechanical::InsertSyncMachinesOnYBus ( )
protected

Definition at line 498 of file Electromechanical.cpp.

499{
500 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
501 SyncGenerator* syncGenerator = *it;
502 if (syncGenerator->IsOnline()) {
503 Bus* connBus = static_cast<Bus*>(syncGenerator->GetParentList()[0]);
504 if (connBus->GetElectricalData().number < 0) {
505 syncGenerator->SetOnline(false);
506 //auto data = syncGenerator->GetElectricalData();
507 //data.activePower = 0.0;
508 //data.reactivePower = 0.0;
509 //syncGenerator->SetElectricalData(data);
510 }
511
512 if (syncGenerator->IsOnline()) {
513 auto data = syncGenerator->GetElectricalData();
514 int n = connBus->GetElectricalData().number;
515 m_yBus[n][n] += GetSyncMachineAdmittance(syncGenerator);
516 }
517 }
518 }
519}

◆ PreallocateVectors()

void Electromechanical::PreallocateVectors ( )
protected

Definition at line 1904 of file Electromechanical.cpp.

1905{
1906 int numPoints = static_cast<unsigned int>(m_simTime / m_plotTime);
1907
1908 m_timeVector.reserve(numPoints);
1909 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1910 SyncGenerator* syncGenerator = *it;
1911 auto data = syncGenerator->GetElectricalData();
1912 if (data.plotSyncMachine) {
1913 data.terminalVoltageVector.reserve(numPoints);
1914 data.electricalPowerVector.reserve(numPoints);
1915 data.mechanicalPowerVector.reserve(numPoints);
1916 data.freqVector.reserve(numPoints);
1917 data.fieldVoltageVector.reserve(numPoints);
1918 data.deltaVector.reserve(numPoints);
1919 syncGenerator->SetElectricalData(data);
1920 }
1921 }
1922 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
1923 Bus* bus = *it;
1924 auto data = bus->GetElectricalData();
1925 if (data.plotBus) {
1926 data.stabVoltageVector.reserve(numPoints);
1927 data.stabFreqVector.reserve(numPoints);
1928 bus->SetElectricalData(data);
1929 }
1930 }
1931 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
1932 Load* load = *it;
1933 auto data = load->GetElectricalData();
1934 if (data.plotLoad) {
1935 data.voltageVector.reserve(numPoints);
1936 data.electricalPowerVector.reserve(numPoints);
1937 load->SetElectricalData(data);
1938 }
1939 }
1940 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1941 IndMotor* motor = *it;
1942 auto data = motor->GetElectricalData();
1943 if (data.plotIndMachine) {
1944 data.slipVector.reserve(numPoints);
1945 data.electricalTorqueVector.reserve(numPoints);
1946 motor->SetElectricalData(data);
1947 }
1948 }
1949}

◆ RunStabilityCalculation()

bool Electromechanical::RunStabilityCalculation ( )

Definition at line 71 of file Electromechanical.cpp.

72{
73 wxProgressDialog pbd(_("Running simulation"), _("Initializing..."), 100, m_parent,
74 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_SMOOTH);
75
76 SetSyncMachinesModel();
77
78 // Calculate the admittance matrix with the synchronous machines.
79 if (!GetYBus(m_yBus, m_powerSystemBase, POSITIVE_SEQ, false, true, true)) {
80 m_errorMsg = _("It was not possible to build the admittance matrix.");
81 return false;
82 }
83 InsertSyncMachinesOnYBus();
84 if (!InsertIndMachinesOnYBus()) return false;
85 GetLUDecomposition(m_yBus, m_yBusL, m_yBusU);
86
87 // Get buses voltages.
88 m_vBus.clear();
89 m_vBus.resize(m_yBus.size());
90 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
91 Bus* bus = *it;
92 auto data = bus->GetElectricalData();
93 if (data.number >= 0) m_vBus[data.number] = data.voltage;
94 }
95 m_vBus.shrink_to_fit();
96
97 // Calculate injected currents
98 m_iBus = ComplexMatrixTimesVector(m_yBus, m_vBus);
99 for (unsigned int i = 0; i < m_iBus.size(); ++i) {
100 if (std::abs(m_iBus[i]) < 1e-5) m_iBus[i] = std::complex<double>(0.0, 0.0);
101 }
102
103 if (!InitializeDynamicElements()) return false;
104 PreallocateVectors(); // Reserve the vectors' memory with a estimated size, this can optimize the simulation.
105
106#ifdef _DEBUG
107 for (auto* syncGenerator : m_syncGeneratorList) {
108 auto data = syncGenerator->GetPUElectricalData(m_powerSystemBase);
109 m_debugMessage += "------\n";
110 m_debugMessage += data.name + ":\n";
111 m_debugMessage += "P = " + wxString::FromDouble(data.electricalPower.real(), 5) + " p.u.\n";
112 m_debugMessage += "Q = " + wxString::FromDouble(data.electricalPower.imag(), 5) + " p.u.\n";
113 m_debugMessage += "If0 = " + wxString::FromDouble(data.initialFieldVoltage, 5) + " p.u.\n";
114 m_debugMessage += "If = " + wxString::FromDouble(data.fieldVoltage, 5) + " p.u.\n";
115 m_debugMessage += "delta = " + wxString::FromDouble(data.delta, 5) + " rad?\n";
116 m_debugMessage += "Vf = " + wxString::FromDouble(data.fieldVoltage, 5) + " p.u.\n";
117 m_debugMessage += "Vf0 = " + wxString::FromDouble(data.initialFieldVoltage, 5) + " p.u.\n";
118 m_debugMessage += "Vt = " + wxString::FromDouble(std::abs(data.terminalVoltage), 5) + " p.u.\n";
119 m_debugMessage += "theta = " + wxString::FromDouble(std::arg(data.terminalVoltage), 5) + " rad\n";
120 m_debugMessage += "Pe = " + wxString::FromDouble(data.pe, 5) + " p.u.\n";
121 m_debugMessage += "Pm = " + wxString::FromDouble(data.pm, 5) + " p.u.\n";
122 m_debugMessage += "w = " + wxString::FromDouble(data.speed, 5) + " p.u.\n";
123 m_debugMessage += "Id = " + wxString::FromDouble(data.id, 5) + " p.u.\n";
124 m_debugMessage += "Iq = " + wxString::FromDouble(data.iq, 5) + " p.u.\n";
125 m_debugMessage += "Ed' = " + wxString::FromDouble(data.tranEd, 5) + " p.u.\n";
126 m_debugMessage += "Eq' = " + wxString::FromDouble(data.tranEq, 5) + " p.u.\n";
127 m_debugMessage += "Ed'' = " + wxString::FromDouble(data.subEd, 5) + " p.u.\n";
128 m_debugMessage += "Eq'' = " + wxString::FromDouble(data.subEq, 5) + " p.u.\n";
129 m_debugMessage += "------\n\n";
130 }
131#endif
132
133 double pbdTime = m_plotTime;
134 m_currentTime = 0.0;
135 double currentPlotTime = 0.0;
136 double currentPbdTime = 0.0;
137 while (m_currentTime <= m_simTime) {
138 bool hasEvent = false;
139 if (HasEvent(m_currentTime)) {
140 hasEvent = true;
141 SetEvent(m_currentTime);
142 GetLUDecomposition(m_yBus, m_yBusL, m_yBusU);
143 }
144
145 if (currentPlotTime >= m_plotTime || m_currentTime == 0.0) {
146 m_timeVector.emplace_back(m_currentTime);
147 SaveData();
148 currentPlotTime = 0.0;
149 }
150
151 if (currentPbdTime > pbdTime) {
152 if (!pbd.Update((m_currentTime / m_simTime) * 100, wxString::Format("Time = %.2fs", m_currentTime))) {
153 m_errorMsg = wxString::Format(_("Simulation cancelled at %.2fs."), m_currentTime);
154 pbd.Update(100);
155 return false;
156 }
157 currentPbdTime = 0.0;
158 }
159
160 if (!SolveMachines()) return false;
161
162 CalculateBusesFrequency(hasEvent);
163
164 m_currentTime += m_timeStep;
165 currentPlotTime += m_timeStep;
166 currentPbdTime += m_timeStep;
167 }
168
169 return true;
170}
@ 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 1356 of file Electromechanical.cpp.

1357{
1358 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1359 SyncGenerator* syncGenerator = *it;
1360 auto data = syncGenerator->GetElectricalData();
1361 if (data.plotSyncMachine) { syncGenerator->SavePlotData(); }
1362 }
1363 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
1364 Bus* bus = *it;
1365 auto data = bus->GetElectricalData();
1366 if (data.plotBus) {
1367 data.stabVoltageVector.emplace_back(data.number >= 0 ? m_vBus[data.number] : 0.0);
1368 data.stabFreqVector.emplace_back(data.number >= 0 ? data.stabFreq : 0.0);
1369 bus->SetElectricalData(data);
1370 }
1371 }
1372 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
1373 Load* load = *it;
1374 auto data = load->GetElectricalData();
1375 if (data.plotLoad) {
1376 data.voltageVector.emplace_back(data.voltage);
1377 data.electricalPowerVector.emplace_back(data.electricalPower);
1378 load->SetElectricalData(data);
1379 }
1380 }
1381 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1382 IndMotor* motor = *it;
1383 auto data = motor->GetElectricalData();
1384 if (data.plotIndMachine) {
1385 data.slipVector.emplace_back(data.slip * 100.0);
1386 data.electricalTorqueVector.emplace_back(data.te * 2.0 * M_PI * m_systemFreq);
1387 double tm = (data.as - data.bs * data.slip + data.cs * data.slip * data.slip) * 2.0 * M_PI * m_systemFreq;
1388 data.mechanicalTorqueVector.emplace_back(tm);
1389 double w = (1.0 - data.slip) * 2.0 * M_PI * m_systemFreq;
1390 data.velocityVector.emplace_back(w);
1391 std::complex<double> i1 = std::complex<double>(data.ir, data.im);
1392 data.currentVector.emplace_back(std::abs(i1));
1393 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalData().number;
1394 std::complex<double> v = n >= 0 ? m_vBus[n] : 0.0;
1395 data.terminalVoltageVector.emplace_back(std::abs(v));
1396 std::complex<double> s = v * std::conj(i1);
1397 data.activePowerVector.emplace_back(std::real(s));
1398 data.reactivePowerVector.emplace_back(std::imag(s));
1399 motor->SetElectricalData(data);
1400 }
1401 }
1402 m_iterationsNumVector.emplace_back(m_iterationsNum);
1403}

◆ SetEvent()

void Electromechanical::SetEvent ( double  currentTime)
protected

Definition at line 209 of file Electromechanical.cpp.

210{
211 // Fault
212 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
213 Bus* bus = *it;
214 auto data = bus->GetElectricalData();
215 int n = data.number;
216 if (data.stabHasFault && n >= 0) {
217
218 // Insert fault
219 if (EventTrigger(data.stabFaultTime, currentTime)) {
220 double r, x;
221 r = data.stabFaultResistance;
222 x = data.stabFaultReactance;
223 if (x < 1e-5) x = 1e-5;
224 m_yBus[n][n] += std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
225 }
226
227 // Remove fault
228 else if (EventTrigger(data.stabFaultTime + data.stabFaultLength, currentTime)) {
229 double r, x;
230 r = data.stabFaultResistance;
231 x = data.stabFaultReactance;
232 if (x < 1e-5) x = 1e-5;
233 m_yBus[n][n] -= std::complex<double>(1.0, 0.0) / std::complex<double>(r, x);
234 }
235 }
236 }
237
238 // SyncGenerator switching
239 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
240 SyncGenerator* generator = *it;
241 auto swData = generator->GetSwitchingData();
242 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
243 if (EventTrigger(swData.swTime[i], currentTime)) {
244 // Remove machine (only connected machines)
245 if (swData.swType[i] == SwitchingType::SW_REMOVE && generator->IsOnline()) {
246 generator->SetOnline(false);
247 int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalData().number;
248 if (n >= 0) m_yBus[n][n] -= GetSyncMachineAdmittance(generator);
249 }
250
251 // Insert machine (only disconnected machines)
252 if (swData.swType[i] == SwitchingType::SW_INSERT && !generator->IsOnline() && generator->GetParentList().size() == 1) {
253 if (generator->SetOnline(true)) {
254 int n = static_cast<Bus*>(generator->GetParentList()[0])->GetElectricalData().number;
255 if (n >= 0) m_yBus[n][n] += GetSyncMachineAdmittance(generator);
256 }
257 }
258 }
259 }
260 }
261
262 // Induction motor switching
263 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
264 IndMotor* motor = *it;
265 auto swData = motor->GetSwitchingData();
266 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
267 if (EventTrigger(swData.swTime[i], currentTime)) {
268 // Remove machine (only connected machines)
269 if (swData.swType[i] == SwitchingType::SW_REMOVE && motor->IsOnline() && motor->GetParentList().size() == 1) {
270 auto data = motor->GetElectricalData();
271 motor->SetOnline(false);
272 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalData().number;
273 if (n >= 0) m_yBus[n][n] -= (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
274 }
275
276 // Insert machine (only disconnected machines)
277 if (swData.swType[i] == SwitchingType::SW_INSERT && !motor->IsOnline() && motor->GetParentList().size() == 1) {
278 auto data = motor->GetElectricalData();
279 if (motor->SetOnline(true)) {
280 int n = static_cast<Bus*>(motor->GetParentList()[0])->GetElectricalData().number;
281 if (n >= 0) m_yBus[n][n] += (std::complex<double>(1, 0) / std::complex<double>(data.r1t, data.xt));
282 }
283 }
284 }
285 }
286 }
287
288 // Load switching
289 for (auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) {
290 Load* load = *it;
291 auto swData = load->GetSwitchingData();
292 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
293 if (EventTrigger(swData.swTime[i], currentTime)) {
294 // Remove load (only connected loads)
295 if (swData.swType[i] == SwitchingType::SW_REMOVE && load->IsOnline() && load->GetParentList().size() == 1) {
296 load->SetOnline(false);
297 auto data = load->GetPUElectricalData(m_powerSystemBase);
298 Bus* parentBus = static_cast<Bus*>(load->GetParentList()[0]);
299 int n = parentBus->GetElectricalData().number;
300 std::complex<double> v = parentBus->GetElectricalData().voltage;
301 if (n >= 0) {
302 m_yBus[n][n] -=
303 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
304 }
305 }
306
307 // Insert load (only disconnected load)
308 if (swData.swType[i] == SwitchingType::SW_INSERT && !load->IsOnline() && load->GetParentList().size() == 1) {
309 if (load->SetOnline(true)) {
310 auto data = load->GetPUElectricalData(m_powerSystemBase);
311 Bus* parentBus = static_cast<Bus*>(load->GetParentList()[0]);
312 int n = parentBus->GetElectricalData().number;
313 std::complex<double> v = parentBus->GetElectricalData().voltage;
314 if (n >= 0) {
315 m_yBus[n][n] +=
316 std::complex<double>(data.activePower, -data.reactivePower) / (std::abs(v) * std::abs(v));
317 }
318 }
319 }
320 }
321 }
322 }
323
324 // Line switching
325 for (auto it = m_lineList.begin(), itEnd = m_lineList.end(); it != itEnd; ++it) {
326 Line* line = *it;
327 auto swData = line->GetSwitchingData();
328 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
329 if (EventTrigger(swData.swTime[i], currentTime)) {
330 // Remove line (only connected lines)
331 if (swData.swType[i] == SwitchingType::SW_REMOVE && line->IsOnline()) {
332 line->SetOnline(false);
333 auto data = line->GetElectricalData();
334
335 int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData().number;
336 int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData().number;
337 if (n1 >= 0 && n2 >= 0) {
338 m_yBus[n1][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
339 m_yBus[n2][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
340
341 m_yBus[n1][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
342 m_yBus[n2][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
343
344 m_yBus[n1][n1] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
345 m_yBus[n2][n2] -= std::complex<double>(0.0, data.capSusceptance / 2.0);
346 }
347 }
348
349 // Insert line (only disconnected lines)
350 if (swData.swType[i] == SwitchingType::SW_INSERT && !line->IsOnline() && line->GetParentList().size() == 2) {
351 if (line->SetOnline(true)) {
352 auto data = line->GetElectricalData();
353
354 int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData().number;
355 int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData().number;
356 if (n1 >= 0 && n2 >= 0) {
357 m_yBus[n1][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
358 m_yBus[n2][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance);
359
360 m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
361 m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
362
363 m_yBus[n1][n1] += std::complex<double>(0.0, data.capSusceptance / 2.0);
364 m_yBus[n2][n2] += std::complex<double>(0.0, data.capSusceptance / 2.0);
365 }
366 }
367 }
368 }
369 }
370 }
371
372 // Transformer switching
373 for (auto it = m_transformerList.begin(), itEnd = m_transformerList.end(); it != itEnd; ++it) {
374 Transformer* transformer = *it;
375 auto swData = transformer->GetSwitchingData();
376 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
377 if (EventTrigger(swData.swTime[i], currentTime)) {
378 // Remove transformer (only connected transformers)
379 if (swData.swType[i] == SwitchingType::SW_REMOVE && transformer->IsOnline()) {
380 transformer->SetOnline(false);
381 auto data = transformer->GetElectricalData();
382
383 int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalData().number;
384 int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalData().number;
385 if (n1 >= 0 && n2 >= 0) {
386 if (data.turnsRatio == 1.0 && data.phaseShift == 0.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 else {
394 // Complex turns ratio
395 double radPhaseShift = wxDegToRad(data.phaseShift);
396 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
397 -data.turnsRatio * std::sin(radPhaseShift));
398
399 // Transformer admitance
400 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
401 m_yBus[n1][n1] -= y / std::pow(std::abs(a), 2.0);
402 m_yBus[n1][n2] -= -(y / std::conj(a));
403 m_yBus[n2][n1] -= -(y / a);
404 m_yBus[n2][n2] -= y;
405 }
406 }
407 }
408
409 // Insert transformer (only disconnected transformers)
410 if (swData.swType[i] == SwitchingType::SW_INSERT && !transformer->IsOnline() &&
411 transformer->GetParentList().size() == 2) {
412 if (transformer->SetOnline(true)) {
413 auto data = transformer->GetElectricalData();
414
415 int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalData().number;
416 int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalData().number;
417 if (n1 >= 0 && n2 >= 0) {
418 if (data.turnsRatio == 1.0 && data.phaseShift == 0.0) {
419 m_yBus[n1][n2] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
420 m_yBus[n2][n1] += -1.0 / std::complex<double>(data.resistance, data.indReactance);
421
422 m_yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
423 m_yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance);
424 }
425 else {
426 // Complex turns ratio
427 double radPhaseShift = wxDegToRad(data.phaseShift);
428 std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift),
429 -data.turnsRatio * std::sin(radPhaseShift));
430
431 // Transformer admitance
432 std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance);
433 m_yBus[n1][n1] += y / std::pow(std::abs(a), 2.0);
434 m_yBus[n1][n2] += -(y / std::conj(a));
435 m_yBus[n2][n1] += -(y / a);
436 m_yBus[n2][n2] += y;
437 }
438 }
439 }
440 }
441 }
442 }
443 }
444
445 // Capacitor switching
446 for (auto it = m_capacitorList.begin(), itEnd = m_capacitorList.end(); it != itEnd; ++it) {
447 Capacitor* capacitor = *it;
448 auto swData = capacitor->GetSwitchingData();
449 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
450 if (EventTrigger(swData.swTime[i], currentTime)) {
451 // Remove capacitor (only connected capacitors)
452 if (swData.swType[i] == SwitchingType::SW_REMOVE && capacitor->IsOnline()) {
453 capacitor->SetOnline(false);
454 auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
455 int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalData().number;
456 if (n >= 0) m_yBus[n][n] -= std::complex<double>(0.0, data.reactivePower);
457 }
458
459 // Insert capacitor (only disconnected capacitors)
460 if (swData.swType[i] == SwitchingType::SW_INSERT && !capacitor->IsOnline() && capacitor->GetParentList().size() == 1) {
461 if (capacitor->SetOnline(true)) {
462 auto data = capacitor->GetPUElectricalData(m_powerSystemBase);
463 int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalData().number;
464 if (n >= 0) m_yBus[n][n] += std::complex<double>(0.0, data.reactivePower);
465 }
466 }
467 }
468 }
469 }
470
471 // Inductor switching
472 for (auto it = m_inductorList.begin(), itEnd = m_inductorList.end(); it != itEnd; ++it) {
473 Inductor* inductor = *it;
474 auto swData = inductor->GetSwitchingData();
475 for (unsigned int i = 0; i < swData.swType.size(); ++i) {
476 if (EventTrigger(swData.swTime[i], currentTime)) {
477 // Remove inductor (only connected inductors)
478 if (swData.swType[i] == SwitchingType::SW_REMOVE && inductor->IsOnline()) {
479 inductor->SetOnline(false);
480 auto data = inductor->GetPUElectricalData(m_powerSystemBase);
481 int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalData().number;
482 if (n >= 0) m_yBus[n][n] -= std::complex<double>(0.0, -data.reactivePower);
483 }
484
485 // Insert inductor (only disconnected inductors)
486 if (swData.swType[i] == SwitchingType::SW_INSERT && !inductor->IsOnline() && inductor->GetParentList().size() == 1) {
487 if (inductor->SetOnline(true)) {
488 auto data = inductor->GetPUElectricalData(m_powerSystemBase);
489 int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalData().number;
490 if (n >= 0) m_yBus[n][n] += std::complex<double>(0.0, -data.reactivePower);
491 }
492 }
493 }
494 }
495 }
496}
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 172 of file Electromechanical.cpp.

173{
174 // Fault
175 for (auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) {
176 Bus* bus = *it;
177 auto data = bus->GetElectricalData();
178 if (data.stabHasFault) {
179 m_eventTimeList.emplace_back(data.stabFaultTime);
180 m_eventOccurrenceList.push_back(false);
181 m_eventTimeList.emplace_back(data.stabFaultTime + data.stabFaultLength);
182 m_eventOccurrenceList.push_back(false);
183 }
184 }
185 // Switching
186 for (auto it = m_powerElementList.begin(), itEnd = m_powerElementList.end(); it != itEnd; ++it) {
187 PowerElement* element = *it;
188 SwitchingData swData = element->GetSwitchingData();
189 for (unsigned int i = 0; i < swData.swTime.size(); ++i) {
190 m_eventTimeList.emplace_back(swData.swTime[i]);
191 m_eventOccurrenceList.push_back(false);
192 }
193 }
194}
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 1405 of file Electromechanical.cpp.

1406{
1407 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1408 SyncGenerator* syncGenerator = *it;
1409 auto data = syncGenerator->GetElectricalData();
1410 data.model = GetMachineModel(syncGenerator);
1411 syncGenerator->SetElectricalData(data);
1412 }
1413}
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 1174 of file Electromechanical.cpp.

1175{
1176 // First interation values and extrapolations
1177 // Synchronous machines
1178 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1179 SyncGenerator* syncGenerator = *it;
1180 auto data = syncGenerator->GetElectricalData();
1181
1182 if (syncGenerator->IsOnline()) {
1183 double id, iq, pe, sd, sq;
1184 pe = data.pe;
1185 id = data.id;
1186 iq = data.iq;
1187 sd = data.sd;
1188 sq = data.sq;
1189
1190 double k = 1.0; // Power base change factor.
1191 if (data.useMachineBase) {
1192 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1193 k = m_powerSystemBase / oldBase;
1194 }
1195
1196 // Calculate integration constants.
1197 CalculateIntegrationConstants(syncGenerator, id, iq, k);
1198
1199 if (!CalculateNonIntVariables(syncGenerator, id, iq, sd, sq, pe, k)) return false;
1200 // Extrapolate nonintegrable variables.
1201 id = 2.0 * id - data.oldId;
1202 iq = 2.0 * iq - data.oldIq;
1203 pe = 2.0 * pe - data.oldPe;
1204 sd = 2.0 * sd - data.oldSd;
1205 sq = 2.0 * sq - data.oldSq;
1206
1207 CalculateIntVariables(syncGenerator, id, iq, sd, sq, pe, k);
1208 }
1209 else {
1210 CalculateIntegrationConstants(syncGenerator, 0.0f, 0.0f);
1211 }
1212 }
1213 // Induction machines
1214 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1215 IndMotor* indMotor = *it;
1216 auto data = indMotor->GetElectricalData();
1217
1218 //if(indMotor->IsOnline()) {
1219 double ir, im, te;
1220 te = data.te;
1221 ir = data.ir;
1222 im = data.im;
1223
1224 double k = 1.0; // Power base change factor.
1225 if (data.useMachinePowerAsBase) {
1226 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1227 k = m_powerSystemBase / oldBase;
1228 }
1229
1230 // Calculate integration constants.
1231 CalculateIntegrationConstants(indMotor, ir, im, k);
1232
1233 if (!CalculateNonIntVariables(indMotor, ir, im, te, k)) return false;
1234 // Extrapolate nonintegrable variables.
1235 ir = 2.0 * ir - data.oldIr;
1236 im = 2.0 * im - data.oldIm;
1237 te = 2.0 * te - data.oldTe;
1238
1239 CalculateIntVariables(indMotor, ir, im, te, k);
1240 //} else {
1241 //CalculateIntegrationConstants(indMotor, 0.0f, 0.0f);
1242 //}
1243 }
1244
1245 double error = 1.0;
1246 int iterations = 0;
1247 while (error > m_tolerance) {
1248 error = 0.0;
1249
1250 // Calculate the injected currents.
1251 if (!CalculateInjectedCurrents()) return false;
1252
1253 // Calculate the buses voltages.
1254 m_vBus = LUEvaluate(m_yBusU, m_yBusL, m_iBus);
1255
1256 // Solve synchronous machine equations.
1257 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1258 SyncGenerator* syncGenerator = *it;
1259
1260 auto data = syncGenerator->GetElectricalData();
1261
1262 double id = data.id;
1263 double iq = data.iq;
1264 double pe = data.pe;
1265 double sd = data.sd;
1266 double sq = data.sq;
1267
1268 // Power base change factor.
1269 double k = 1.0;
1270 if (data.useMachineBase) {
1271 double oldBase = syncGenerator->GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1272 k = m_powerSystemBase / oldBase;
1273 }
1274
1275 // Calculate id, iq, dq, sd
1276 if (!CalculateNonIntVariables(syncGenerator, id, iq, sd, sq, pe, k)) return false;
1277
1278 double genError = CalculateIntVariables(syncGenerator, id, iq, sd, sq, pe, k);
1279
1280 if (genError > error) error = genError;
1281 }
1282
1283 // Solve induction machine equation
1284 for (auto it = m_indMotorList.begin(), itEnd = m_indMotorList.end(); it != itEnd; ++it) {
1285 IndMotor* indMotor = *it;
1286
1287 auto data = indMotor->GetElectricalData();
1288
1289 double ir = data.ir;
1290 double im = data.im;
1291 double te = data.te;
1292
1293 // Power base change factor.
1294 double k = 1.0;
1295 if (data.useMachinePowerAsBase) {
1296 double oldBase = indMotor->GetValueFromUnit(data.ratedPower, data.ratedPowerUnit);
1297 k = m_powerSystemBase / oldBase;
1298 }
1299
1300 // Calculate te
1301 if (!CalculateNonIntVariables(indMotor, ir, im, te, k)) return false;
1302
1303 double motorError = CalculateIntVariables(indMotor, ir, im, te, k);
1304
1305 if (motorError > error) error = motorError;
1306 }
1307
1308 ++iterations;
1309
1310 if (iterations > m_maxIterations) {
1311 m_errorMsg = _("Impossible to solve the system machines.\nCheck the system parameters and/or "
1312 "decrease the time step.");
1313 return false;
1314 }
1315 }
1316 m_iterationsNum = iterations;
1317
1318 // Solve controllers.
1319 int ctrlRatio = static_cast<int>(1 / m_ctrlTimeStepMultiplier);
1320 for (auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) {
1321 SyncGenerator* syncGenerator = *it;
1322 auto data = syncGenerator->GetElectricalData();
1323 if (data.useAVR && data.avrSolver) {
1324 data.avrSolver->SetSwitchStatus(syncGenerator->IsOnline());
1325 data.avrSolver->SetCurrentTime(m_currentTime);
1326 data.avrSolver->SetTerminalVoltage(std::abs(data.terminalVoltage));
1327 data.avrSolver->SetDeltaActivePower((data.electricalPower.real() - data.avrSolver->GetActivePower()) /
1328 m_timeStep);
1329 data.avrSolver->SetActivePower(data.electricalPower.real());
1330 data.avrSolver->SetReactivePower(data.electricalPower.imag());
1331 data.avrSolver->SetDeltaVelocity((data.speed - data.avrSolver->GetVelocity()) / m_timeStep);
1332 data.avrSolver->SetVelocity(data.speed);
1333
1334 for (int i = 0; i < ctrlRatio; ++i) data.avrSolver->SolveNextStep();
1335
1336 data.fieldVoltage = data.initialFieldVoltage + data.avrSolver->GetFieldVoltage();
1337 }
1338
1339 if (data.useSpeedGovernor && data.speedGovSolver) {
1340 data.speedGovSolver->SetSwitchStatus(syncGenerator->IsOnline());
1341 data.speedGovSolver->SetCurrentTime(m_currentTime);
1342 data.speedGovSolver->SetVelocity(data.speed);
1343 data.speedGovSolver->SetActivePower(data.electricalPower.real());
1344 data.speedGovSolver->SetReactivePower(data.electricalPower.imag());
1345
1346 for (int i = 0; i < ctrlRatio; ++i) data.speedGovSolver->SolveNextStep();
1347
1348 data.pm = data.speedGovSolver->GetMechanicalPower();
1349 }
1350 syncGenerator->SetElectricalData(data);
1351 }
1352
1353 return true;
1354}
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: