diff options
-rw-r--r-- | Project/ElectricCalculation.cpp | 27 | ||||
-rw-r--r-- | Project/ElementForm.wxcp | 84 | ||||
-rw-r--r-- | Project/ElementFormBase.cpp | 11 | ||||
-rw-r--r-- | Project/ElementFormBase.h | 3 | ||||
-rw-r--r-- | Project/IndMotor.cpp | 61 | ||||
-rw-r--r-- | Project/IndMotor.h | 13 | ||||
-rw-r--r-- | Project/IndMotorForm.cpp | 15 | ||||
-rw-r--r-- | Project/IndMotorForm.h | 3 | ||||
-rw-r--r-- | Project/PowerFlow.cpp | 179 | ||||
-rw-r--r-- | Project/PowerFlow.h | 13 | ||||
-rw-r--r-- | Project/PropertiesData.h | 1 | ||||
-rw-r--r-- | Project/PropertiesForm.wxcp | 192 | ||||
-rw-r--r-- | Project/PropertiesFormBase.cpp | 20 | ||||
-rw-r--r-- | Project/PropertiesFormBase.h | 4 | ||||
-rw-r--r-- | Project/SimulationsSettingsForm.cpp | 8 | ||||
-rw-r--r-- | Project/Workspace.cpp | 12 |
16 files changed, 573 insertions, 73 deletions
diff --git a/Project/ElectricCalculation.cpp b/Project/ElectricCalculation.cpp index 7b03eeb..a2db413 100644 --- a/Project/ElectricCalculation.cpp +++ b/Project/ElectricCalculation.cpp @@ -397,6 +397,33 @@ void ElectricCalculation::UpdateElementsPowerFlow(std::vector<std::complex<doubl } } + // Ind motor + for(unsigned int i = 0; i < m_indMotorList.size(); i++) { + IndMotor* motor = m_indMotorList[i]; + auto data = motor->GetElectricalData(); + if(motor->IsOnline() && data.calcQInPowerFlow) { + double reactivePower = data.qValue * systemPowerBase; + + switch(data.reactivePowerUnit) { + case UNIT_PU: { + reactivePower /= systemPowerBase; + } break; + case UNIT_kVAr: { + reactivePower /= 1e3; + } break; + case UNIT_MVAr: { + reactivePower /= 1e6; + } break; + default: + break; + } + + data.reactivePower = reactivePower; + + motor->SetElectricalData(data); + } + } + // Synchronous machines for(int i = 0; i < (int)m_busList.size(); i++) { Bus* bus = m_busList[i]; diff --git a/Project/ElementForm.wxcp b/Project/ElementForm.wxcp index db8b092..a53c597 100644 --- a/Project/ElementForm.wxcp +++ b/Project/ElementForm.wxcp @@ -1,7 +1,7 @@ { "metadata": { "m_generatedFilesDir": ".", - "m_objCounter": 2190, + "m_objCounter": 2191, "m_includeFiles": [], "m_bitmapFunction": "wxC9EE9InitBitmapResources", "m_bitmapsFile": "ElementFormBitmaps.cpp", @@ -29858,6 +29858,88 @@ }, { "type": "string", "m_label": "Name:", + "m_value": "m_checkBoxComputeQ" + }, { + "type": "multi-string", + "m_label": "Tooltip:", + "m_value": "" + }, { + "type": "colour", + "m_label": "Bg Colour:", + "colour": "<Default>" + }, { + "type": "colour", + "m_label": "Fg Colour:", + "colour": "<Default>" + }, { + "type": "font", + "m_label": "Font:", + "m_value": "" + }, { + "type": "bool", + "m_label": "Hidden", + "m_value": false + }, { + "type": "bool", + "m_label": "Disabled", + "m_value": false + }, { + "type": "bool", + "m_label": "Focused", + "m_value": false + }, { + "type": "string", + "m_label": "Class Name:", + "m_value": "" + }, { + "type": "string", + "m_label": "Include File:", + "m_value": "" + }, { + "type": "string", + "m_label": "Style:", + "m_value": "" + }, { + "type": "string", + "m_label": "Label:", + "m_value": "Calculate reactive power in load flow (using stability data)" + }, { + "type": "bool", + "m_label": "Value:", + "m_value": false + }], + "m_events": [{ + "m_eventName": "wxEVT_COMMAND_CHECKBOX_CLICKED", + "m_eventClass": "wxCommandEvent", + "m_eventHandler": "wxCommandEventHandler", + "m_functionNameAndSignature": "OnCalcQInPFClick(wxCommandEvent& event)", + "m_description": "Process a wxEVT_COMMAND_CHECKBOX_CLICKED event, when the checkbox is clicked.", + "m_noBody": false + }], + "m_children": [] + }, { + "m_type": 4415, + "proportion": 0, + "border": 5, + "gbSpan": "1,1", + "gbPosition": "0,0", + "m_styles": [], + "m_sizerFlags": ["wxALL", "wxLEFT", "wxRIGHT", "wxTOP", "wxBOTTOM", "wxALIGN_CENTER_VERTICAL"], + "m_properties": [{ + "type": "winid", + "m_label": "ID:", + "m_winid": "wxID_ANY" + }, { + "type": "string", + "m_label": "Size:", + "m_value": "-1,-1" + }, { + "type": "string", + "m_label": "Minimum Size:", + "m_value": "-1,-1" + }, { + "type": "string", + "m_label": "Name:", "m_value": "m_checkBoxUseMachinePower" }, { "type": "multi-string", diff --git a/Project/ElementFormBase.cpp b/Project/ElementFormBase.cpp index 6a75ec7..25fc622 100644 --- a/Project/ElementFormBase.cpp +++ b/Project/ElementFormBase.cpp @@ -3187,6 +3187,13 @@ IndMotorFormBase::IndMotorFormBase(wxWindow* parent, boxSizerLvl5_2->Add(m_choiceReactivePower, 0, wxLEFT | wxRIGHT | wxBOTTOM, WXC_FROM_DIP(5)); + m_checkBoxComputeQ = + new wxCheckBox(m_panelGeneral, wxID_ANY, _("Calculate reactive power in load flow (using stability data)"), + wxDefaultPosition, wxDLG_UNIT(m_panelGeneral, wxSize(-1, -1)), 0); + m_checkBoxComputeQ->SetValue(false); + + boxSizerLvl2_1->Add(m_checkBoxComputeQ, 0, wxALL | wxALIGN_CENTER_VERTICAL, WXC_FROM_DIP(5)); + m_checkBoxUseMachinePower = new wxCheckBox(m_panelGeneral, wxID_ANY, _("Use machine rated power as base"), wxDefaultPosition, wxDLG_UNIT(m_panelGeneral, wxSize(-1, -1)), 0); m_checkBoxUseMachinePower->SetValue(false); @@ -3510,6 +3517,8 @@ IndMotorFormBase::IndMotorFormBase(wxWindow* parent, } #endif // Connect events + m_checkBoxComputeQ->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, + wxCommandEventHandler(IndMotorFormBase::OnCalcQInPFClick), NULL, this); m_checkBoxUseKf->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(IndMotorFormBase::OnCheckboxUseCageFactorClick), NULL, this); m_buttonSwitchingButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, @@ -3522,6 +3531,8 @@ IndMotorFormBase::IndMotorFormBase(wxWindow* parent, IndMotorFormBase::~IndMotorFormBase() { + m_checkBoxComputeQ->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, + wxCommandEventHandler(IndMotorFormBase::OnCalcQInPFClick), NULL, this); m_checkBoxUseKf->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(IndMotorFormBase::OnCheckboxUseCageFactorClick), NULL, this); m_buttonSwitchingButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, diff --git a/Project/ElementFormBase.h b/Project/ElementFormBase.h index 734681c..c8fe7f0 100644 --- a/Project/ElementFormBase.h +++ b/Project/ElementFormBase.h @@ -771,6 +771,7 @@ class IndMotorFormBase : public wxDialog wxStaticText* m_staticTextReactivePower; wxTextCtrl* m_textCtrlReactivePower; wxChoice* m_choiceReactivePower; + wxCheckBox* m_checkBoxComputeQ; wxCheckBox* m_checkBoxUseMachinePower; wxPanel* m_panelStability; wxCheckBox* m_checkBoxPlotIndMachine; @@ -806,6 +807,7 @@ class IndMotorFormBase : public wxDialog wxButton* m_ButtonCancel; protected: + virtual void OnCalcQInPFClick(wxCommandEvent& event) { event.Skip(); } virtual void OnCheckboxUseCageFactorClick(wxCommandEvent& event) { event.Skip(); } virtual void OnStabilityButtonClick(wxCommandEvent& event) { event.Skip(); } virtual void OnOKButtonClick(wxCommandEvent& event) { event.Skip(); } @@ -823,6 +825,7 @@ class IndMotorFormBase : public wxDialog wxStaticText* GetStaticTextReactivePower() { return m_staticTextReactivePower; } wxTextCtrl* GetTextCtrlReactivePower() { return m_textCtrlReactivePower; } wxChoice* GetChoiceReactivePower() { return m_choiceReactivePower; } + wxCheckBox* GetCheckBoxComputeQ() { return m_checkBoxComputeQ; } wxCheckBox* GetCheckBoxUseMachinePower() { return m_checkBoxUseMachinePower; } wxPanel* GetPanelGeneral() { return m_panelGeneral; } wxCheckBox* GetCheckBoxPlotIndMachine() { return m_checkBoxPlotIndMachine; } diff --git a/Project/IndMotor.cpp b/Project/IndMotor.cpp index cace164..4df2bb1 100644 --- a/Project/IndMotor.cpp +++ b/Project/IndMotor.cpp @@ -263,3 +263,64 @@ bool IndMotor::GetPlotData(ElementPlotData& plotData, PlotStudy study) plotData.AddData(m_electricalData.slipVector, _("Slip")); return true; } + +void IndMotor::InitPowerFlowMotor(double systemPowerBase, int busNumber) +{ + double k = 1.0; // Power base change factor. + if(m_electricalData.useMachinePowerAsBase) { + double oldBase = GetValueFromUnit(m_electricalData.ratedPower, m_electricalData.ratedPowerUnit); + k = systemPowerBase / oldBase; + } + // Calculate the induction machine transient constants at the machine base + m_electricalData.r1t = m_electricalData.r1 * k; + m_electricalData.r2t = m_electricalData.r2 * k; + m_electricalData.x1t = m_electricalData.x1 * k; + m_electricalData.x2t = m_electricalData.x2 * k; + m_electricalData.xmt = m_electricalData.xm * k; + + m_electricalData.xt = m_electricalData.x1t + + (m_electricalData.x2t * m_electricalData.xmt) / (m_electricalData.x2t + m_electricalData.xmt); + m_electricalData.x0 = m_electricalData.x1t + m_electricalData.xmt; + + double r1 = m_electricalData.r1t; + double r2 = m_electricalData.r2t; + if(m_electricalData.useKf) r2 *= (1.0 + m_electricalData.kf * m_electricalData.r2t); + double x1 = m_electricalData.x1t; + double x2 = m_electricalData.x2t; + double xm = m_electricalData.xmt; + m_electricalData.k1 = x2 + xm; + m_electricalData.k2 = -x1 * m_electricalData.k1 - x2 * xm; + m_electricalData.k3 = xm + x1; + m_electricalData.k4 = r1 * m_electricalData.k1; + + auto puData = GetPUElectricalData(systemPowerBase); + m_electricalData.p0 = puData.activePower; + m_electricalData.busNum = busNumber; +} + +bool IndMotor::CalculateReactivePower(double voltage) +{ + double a = m_electricalData.p0 * + (m_electricalData.r1t * m_electricalData.r1t + m_electricalData.k3 * m_electricalData.k3) - + voltage * voltage * m_electricalData.r1t; + double b = 2.0 * m_electricalData.p0 * + (m_electricalData.r1t * m_electricalData.k2 + m_electricalData.k3 * m_electricalData.k4) - + voltage * voltage * (m_electricalData.k2 + m_electricalData.k1 * m_electricalData.k3); + double c = + m_electricalData.p0 * (m_electricalData.k2 * m_electricalData.k2 + m_electricalData.k4 * m_electricalData.k4) - + voltage * voltage * m_electricalData.k1 * m_electricalData.k4; + double d = (b * b - 4.0 * a * c); + if(d < 0.0) return false; + double r2_s = (-b + std::sqrt(d)) / (2.0 * a); + + double qa = m_electricalData.k1 * (r2_s * m_electricalData.r1t - m_electricalData.x1t * m_electricalData.k1 - + m_electricalData.x2t * m_electricalData.xmt); + double qb = + r2_s * (r2_s * (m_electricalData.xmt + m_electricalData.x1t) + m_electricalData.r1t * m_electricalData.k1); + double qc = r2_s * m_electricalData.r1t - m_electricalData.x1t * m_electricalData.k1 - + m_electricalData.x2t * m_electricalData.xmt; + double qd = r2_s * (m_electricalData.xmt + m_electricalData.x1t) + m_electricalData.r1t * m_electricalData.k1; + m_electricalData.qValue = (-voltage * voltage * (qa - qb)) / (qc * qc + qd * qd); + + return true; +} diff --git a/Project/IndMotor.h b/Project/IndMotor.h index 89bd6e1..0aa6937 100644 --- a/Project/IndMotor.h +++ b/Project/IndMotor.h @@ -32,6 +32,17 @@ struct IndMotorElectricalData { ElectricalUnit reactivePowerUnit = UNIT_MVAr; bool useMachinePowerAsBase = true; + bool calcQInPowerFlow = true; + + // Power flow + double k1 = 0.0; + double k2 = 0.0; + double k3 = 0.0; + double k4 = 0.0; + double p0 = 0.0; + int busNum = 0; + + double qValue = 0.0; // Stability bool plotIndMachine = false; @@ -119,6 +130,8 @@ class IndMotor : public Machines virtual IndMotorElectricalData GetElectricalData() { return m_electricalData; } virtual IndMotorElectricalData GetPUElectricalData(double systemPowerBase); virtual void SetElectricalData(IndMotorElectricalData electricalData) { m_electricalData = electricalData; } + virtual void InitPowerFlowMotor(double systemPowerBase, int busNumber); + virtual bool CalculateReactivePower(double voltage); virtual bool GetPlotData(ElementPlotData& plotData, PlotStudy study = STABILITY); diff --git a/Project/IndMotorForm.cpp b/Project/IndMotorForm.cpp index 6df1684..8517aef 100644 --- a/Project/IndMotorForm.cpp +++ b/Project/IndMotorForm.cpp @@ -78,7 +78,8 @@ IndMotorForm::IndMotorForm(wxWindow* parent, IndMotor* indMotor) : IndMotorFormB default: break; } - + + m_checkBoxComputeQ->SetValue(data.calcQInPowerFlow); m_checkBoxUseMachinePower->SetValue(data.useMachinePowerAsBase); // Stability @@ -99,6 +100,8 @@ IndMotorForm::IndMotorForm(wxWindow* parent, IndMotor* indMotor) : IndMotorFormB m_parent = parent; m_indMotor = indMotor; + + UpdateFields(); } IndMotorForm::~IndMotorForm() {} @@ -172,7 +175,8 @@ bool IndMotorForm::ValidateData() data.reactivePowerUnit = UNIT_MVAr; } break; } - + + data.calcQInPowerFlow = m_checkBoxComputeQ->GetValue(); data.useMachinePowerAsBase = m_checkBoxUseMachinePower->GetValue(); // Stability @@ -223,7 +227,12 @@ bool IndMotorForm::ValidateData() m_indMotor->SetElectricalData(data); return true; } -void IndMotorForm::OnCheckboxUseCageFactorClick(wxCommandEvent& event) +void IndMotorForm::OnCheckboxUseCageFactorClick(wxCommandEvent& event) { UpdateFields(); } +void IndMotorForm::OnCalcQInPFClick(wxCommandEvent& event) { UpdateFields(); } + +void IndMotorForm::UpdateFields() { m_textCtrlKf->Enable(m_checkBoxUseKf->GetValue()); + m_textCtrlReactivePower->Enable(!m_checkBoxComputeQ->GetValue()); + m_choiceReactivePower->Enable(!m_checkBoxComputeQ->GetValue()); } diff --git a/Project/IndMotorForm.h b/Project/IndMotorForm.h index 5a476a6..e24efea 100644 --- a/Project/IndMotorForm.h +++ b/Project/IndMotorForm.h @@ -36,10 +36,13 @@ class IndMotorForm : public IndMotorFormBase virtual bool ValidateData(); protected: + virtual void OnCalcQInPFClick(wxCommandEvent& event); virtual void OnCheckboxUseCageFactorClick(wxCommandEvent& event); virtual void OnCancelButtonClick(wxCommandEvent& event) { EndModal(wxID_CANCEL); }; virtual void OnOKButtonClick(wxCommandEvent& event); virtual void OnStabilityButtonClick(wxCommandEvent& event); + + void UpdateFields(); wxWindow* m_parent = NULL; IndMotor* m_indMotor = NULL; diff --git a/Project/PowerFlow.cpp b/Project/PowerFlow.cpp index 1b2ae13..161a991 100644 --- a/Project/PowerFlow.cpp +++ b/Project/PowerFlow.cpp @@ -155,8 +155,19 @@ bool PowerFlow::InitPowerFlow(std::vector<BusType>& busType, if(indMotor->IsOnline()) { if(bus == indMotor->GetParentList()[0]) { IndMotorElectricalData childData = indMotor->GetPUElectricalData(systemPowerBase); - power[busNumber] += std::complex<double>(-childData.activePower, -childData.reactivePower); - loadPower[busNumber] += std::complex<double>(-childData.activePower, -childData.reactivePower); + double reactivePower = childData.reactivePower; + + if(childData.calcQInPowerFlow) { + indMotor->InitPowerFlowMotor(systemPowerBase, data.number); + if(!indMotor->CalculateReactivePower(std::abs(voltage[childData.busNum]))) { + m_errorMsg = _("It was not possible to solve the induction motors."); + return false; + } + reactivePower = indMotor->GetElectricalData().qValue; + } + + power[busNumber] += std::complex<double>(-childData.activePower, -reactivePower); + loadPower[busNumber] += std::complex<double>(-childData.activePower, -reactivePower); } } } @@ -221,6 +232,12 @@ bool PowerFlow::RunGaussSeidel(double systemPowerBase, return false; } + // Calculate induction motor reactive power + if(!CalculateMotorsReactivePower(voltage, power)) { + m_errorMsg = _("It was not possible to solve the induction motors."); + return false; + } + // Update the old voltage array to current iteration values. for(int i = 0; i < m_numberOfBuses; i++) oldVoltage[i] = voltage[i]; @@ -232,6 +249,7 @@ bool PowerFlow::RunGaussSeidel(double systemPowerBase, iteration++; } + m_iterations = iteration; // Adjust the power array. for(int i = 0; i < m_numberOfBuses; i++) { @@ -245,7 +263,11 @@ bool PowerFlow::RunGaussSeidel(double systemPowerBase, return true; } -bool PowerFlow::RunNewtonRaphson(double systemPowerBase, int maxIteration, double error, double initAngle) +bool PowerFlow::RunNewtonRaphson(double systemPowerBase, + int maxIteration, + double error, + double initAngle, + double inertia) { std::vector<BusType> busType; // Bus type std::vector<std::complex<double> > voltage; // Voltage of buses @@ -273,6 +295,12 @@ bool PowerFlow::RunNewtonRaphson(double systemPowerBase, int maxIteration, doubl return false; } + // Calculate induction motor reactive power + if(!CalculateMotorsReactivePower(voltage, power)) { + m_errorMsg = _("It was not possible to solve the induction motors."); + return false; + } + // Calculate dPdQ array // Fill it with zeros @@ -321,10 +349,11 @@ bool PowerFlow::RunNewtonRaphson(double systemPowerBase, int maxIteration, doubl } } - NewtonRaphson(busType, voltage, power, numPV, numPQ, dPdQ); + NewtonRaphson(busType, voltage, power, numPV, numPQ, dPdQ, inertia); iteration++; } + m_iterations = iteration; // Adjust the power array. for(int i = 0; i < m_numberOfBuses; i++) { @@ -343,7 +372,8 @@ bool PowerFlow::RunGaussNewton(double systemPowerBase, double error, double initAngle, double accFactor, - double gaussTol) + double gaussTol, + double inertia) { std::vector<BusType> busType; // Bus type std::vector<std::complex<double> > voltage; // Voltage of buses @@ -368,6 +398,12 @@ bool PowerFlow::RunGaussNewton(double systemPowerBase, return false; } + // Calculate induction motor reactive power + if(!CalculateMotorsReactivePower(voltage, power)) { + m_errorMsg = _("It was not possible to solve the induction motors."); + return false; + } + // Update the old voltage array to current iteration values. for(int i = 0; i < m_numberOfBuses; i++) oldVoltage[i] = voltage[i]; @@ -394,6 +430,12 @@ bool PowerFlow::RunGaussNewton(double systemPowerBase, return false; } + // Calculate induction motor reactive power + if(!CalculateMotorsReactivePower(voltage, power)) { + m_errorMsg = _("It was not possible to solve the induction motors."); + return false; + } + // Calculate dPdQ array // Fill it with zeros @@ -442,10 +484,11 @@ bool PowerFlow::RunGaussNewton(double systemPowerBase, } } - NewtonRaphson(busType, voltage, power, numPV, numPQ, dPdQ); + NewtonRaphson(busType, voltage, power, numPV, numPQ, dPdQ, inertia); iteration++; } + m_iterations = iteration; // Adjust the power array. for(int i = 0; i < m_numberOfBuses; i++) { @@ -537,11 +580,15 @@ void PowerFlow::NewtonRaphson(std::vector<BusType> busType, std::vector<std::complex<double> > power, int numPV, int numPQ, - std::vector<double> dPdQ) + std::vector<double> dPdQ, + double inertia) { // Jacobian matrix std::vector<std::vector<double> > jacobMatrix = CalculateJacobianMatrix(voltage, busType, numPV, numPQ); + // Apply inertia + for(unsigned int i = 0; i < dPdQ.size(); ++i) { dPdQ[i] = inertia * dPdQ[i]; } + // Calculate DeltaTheta DeltaV array std::vector<double> dTdV = GaussianElimination(jacobMatrix, dPdQ); @@ -556,7 +603,7 @@ void PowerFlow::NewtonRaphson(std::vector<BusType> busType, voltage[i] = std::complex<double>(newV * std::cos(newT), newV * std::sin(newT)); indexDT++; } else { - double newV = std::abs(voltage[i]) + dTdV[indexDV]; + double newV = std::abs(voltage[i]) * (1.0 + dTdV[indexDV]); double newT = std::arg(voltage[i]) + dTdV[indexDT]; voltage[i] = std::complex<double>(newV * std::cos(newT), newV * std::sin(newT)); indexDV++; @@ -577,10 +624,10 @@ std::vector<std::vector<double> > PowerFlow::CalculateJacobianMatrix(std::vector for(int i = 0; i < (2 * numPQ + numPV); i++) jacobMatrix[i].resize(2 * numPQ + numPV); // Calculate the submatrices - //{ H - std::vector<std::vector<double> > sMatrixH; - sMatrixH.resize(numPQ + numPV); // submatrix H size - for(int i = 0; i < (numPQ + numPV); i++) sMatrixH[i].resize(numPQ + numPV); + //{ J11 + std::vector<std::vector<double> > j11; + j11.resize(numPQ + numPV); // submatrix j11 size + for(int i = 0; i < (numPQ + numPV); i++) j11[i].resize(numPQ + numPV); int busNumI = 0; int busNumJ = 0; @@ -593,16 +640,14 @@ std::vector<std::vector<double> > PowerFlow::CalculateJacobianMatrix(std::vector double ti = std::arg(voltage[i]); double vj = std::abs(voltage[j]); double tj = std::arg(voltage[j]); - sMatrixH[busNumI][busNumJ] = - vi * vj * - (m_yBus[i][j].real() * std::sin(ti - tj) - m_yBus[i][j].imag() * std::cos(ti - tj)); - } else // busNumI == busNumJ - { + double yij = std::abs(m_yBus[i][j]); + double tij = std::arg(m_yBus[i][j]); + j11[busNumI][busNumJ] = -vi * vj * yij * std::sin(tij + tj - ti); + } else { // busNumI == busNumJ std::complex<double> sj = std::complex<double>(0.0, 0.0); for(int k = 0; k < m_numberOfBuses; k++) sj += voltage[j] * std::conj(m_yBus[j][k]) * std::conj(voltage[k]); - sMatrixH[busNumI][busNumJ] = - -sj.imag() - m_yBus[j][j].imag() * std::pow(std::abs(voltage[j]), 2.0); + j11[busNumI][busNumJ] = -sj.imag() - m_yBus[j][j].imag() * std::pow(std::abs(voltage[j]), 2.0); } busNumJ++; } @@ -612,10 +657,10 @@ std::vector<std::vector<double> > PowerFlow::CalculateJacobianMatrix(std::vector } } //} - //{ N - std::vector<std::vector<double> > sMatrixN; - sMatrixN.resize(numPQ + numPV); // submatrix N size - for(int i = 0; i < (numPQ + numPV); i++) sMatrixN[i].resize(numPQ); + //{ J12 + std::vector<std::vector<double> > j12; + j12.resize(numPQ + numPV); // submatrix j12 size + for(int i = 0; i < (numPQ + numPV); i++) j12[i].resize(numPQ); busNumI = 0; busNumJ = 0; @@ -629,16 +674,15 @@ std::vector<std::vector<double> > PowerFlow::CalculateJacobianMatrix(std::vector double ti = std::arg(voltage[i]); double vj = std::abs(voltage[j]); double tj = std::arg(voltage[j]); - sMatrixN[busNumI][busNumJ] = - vi * vj * - (m_yBus[i][j].real() * std::cos(ti - tj) + m_yBus[i][j].imag() * std::sin(ti - tj)); - } else // busNumI == busNumJ - { + double yij = std::abs(m_yBus[i][j]); + double tij = std::arg(m_yBus[i][j]); + + j12[busNumI][busNumJ] = vj * vi * yij * std::cos(tij + tj - ti); + } else { // busNumI == busNumJ std::complex<double> sj = std::complex<double>(0.0, 0.0); for(int k = 0; k < m_numberOfBuses; k++) sj += voltage[j] * std::conj(m_yBus[j][k]) * std::conj(voltage[k]); - sMatrixN[busNumI][busNumJ] = - sj.real() + m_yBus[j][j].real() * std::pow(std::abs(voltage[j]), 2.0); + j12[busNumI][busNumJ] = sj.real() + m_yBus[j][j].real() * std::pow(std::abs(voltage[j]), 2.0); } busNumJ++; } @@ -648,10 +692,10 @@ std::vector<std::vector<double> > PowerFlow::CalculateJacobianMatrix(std::vector } } //} - //{ J - std::vector<std::vector<double> > sMatrixJ; - sMatrixJ.resize(numPQ); // submatrix J size - for(int i = 0; i < (numPQ); i++) sMatrixJ[i].resize(numPQ + numPV); + //{ J21 + std::vector<std::vector<double> > j21; + j21.resize(numPQ); // submatrix j21 size + for(int i = 0; i < (numPQ); i++) j21[i].resize(numPQ + numPV); busNumI = 0; busNumJ = 0; @@ -665,16 +709,15 @@ std::vector<std::vector<double> > PowerFlow::CalculateJacobianMatrix(std::vector double ti = std::arg(voltage[i]); double vj = std::abs(voltage[j]); double tj = std::arg(voltage[j]); - sMatrixJ[busNumI][busNumJ] = - -vi * vj * - (m_yBus[i][j].real() * std::cos(ti - tj) + m_yBus[i][j].imag() * std::sin(ti - tj)); - } else // busNumI == busNumJ - { + double yij = std::abs(m_yBus[i][j]); + double tij = std::arg(m_yBus[i][j]); + + j21[busNumI][busNumJ] = -vi * vj * yij * std::cos(tij + tj - ti); + } else { // busNumI == busNumJ std::complex<double> sj = std::complex<double>(0.0, 0.0); for(int k = 0; k < m_numberOfBuses; k++) sj += voltage[j] * std::conj(m_yBus[j][k]) * std::conj(voltage[k]); - sMatrixJ[busNumI][busNumJ] = - sj.real() - m_yBus[j][j].real() * std::pow(std::abs(voltage[j]), 2.0); + j21[busNumI][busNumJ] = sj.real() - m_yBus[j][j].real() * std::pow(std::abs(voltage[j]), 2.0); } busNumJ++; } @@ -684,34 +727,31 @@ std::vector<std::vector<double> > PowerFlow::CalculateJacobianMatrix(std::vector } } //} - //{ L - std::vector<std::vector<double> > sMatrixL; - sMatrixL.resize(numPQ); // submatrix L size - for(int i = 0; i < (numPQ); i++) sMatrixL[i].resize(numPQ); + //{ J22 + std::vector<std::vector<double> > j22; + j22.resize(numPQ); // submatrix j22 size + for(int i = 0; i < (numPQ); i++) j22[i].resize(numPQ); busNumI = 0; busNumJ = 0; for(int i = 0; i < m_numberOfBuses; i++) { - if(busType[i] == BUS_PQ) // nćo deve ser referźncia e nćo deve ser PV, ou seja é PQ - { + if(busType[i] == BUS_PQ) { for(int j = 0; j < m_numberOfBuses; j++) { - if(busType[j] == BUS_PQ) // nćo deve ser referźncia e nćo deve ser PV, ou seja é PQ - { + if(busType[j] == BUS_PQ) { if(busNumI != busNumJ) { double vi = std::abs(voltage[i]); double ti = std::arg(voltage[i]); double vj = std::abs(voltage[j]); double tj = std::arg(voltage[j]); - sMatrixL[busNumI][busNumJ] = - vi * vj * - (m_yBus[i][j].real() * std::sin(ti - tj) - m_yBus[i][j].imag() * std::cos(ti - tj)); - } else // busNumI == busNumJ - { + double yij = std::abs(m_yBus[i][j]); + double tij = std::arg(m_yBus[i][j]); + + j22[busNumI][busNumJ] = -vj * vi * yij * std::sin(tij + tj - ti); + } else { // busNumI == busNumJ std::complex<double> sj = std::complex<double>(0.0, 0.0); for(int k = 0; k < m_numberOfBuses; k++) sj += voltage[j] * std::conj(m_yBus[j][k]) * std::conj(voltage[k]); - sMatrixL[busNumI][busNumJ] = - -sj.imag() - m_yBus[j][j].imag() * std::pow(std::abs(voltage[j]), 2.0); + j22[busNumI][busNumJ] = sj.imag() - m_yBus[j][j].imag() * std::pow(std::abs(voltage[j]), 2.0); } busNumJ++; } @@ -727,15 +767,14 @@ std::vector<std::vector<double> > PowerFlow::CalculateJacobianMatrix(std::vector for(int j = 0; j < (2 * numPQ + numPV); j++) { if(i < (numPQ + numPV)) { if(j < (numPQ + numPV)) - jacobMatrix[i][j] = sMatrixH[i][j]; + jacobMatrix[i][j] = j11[i][j]; else // j >= numPQ + numPV - jacobMatrix[i][j] = sMatrixN[i][j - (numPQ + numPV)]; - } else // i >= numPQ + numPV - { + jacobMatrix[i][j] = j12[i][j - (numPQ + numPV)]; + } else { // i >= numPQ + numPV if(j < (numPQ + numPV)) - jacobMatrix[i][j] = sMatrixJ[i - (numPQ + numPV)][j]; + jacobMatrix[i][j] = j21[i - (numPQ + numPV)][j]; else // j >= numPQ + numPV - jacobMatrix[i][j] = sMatrixL[i - (numPQ + numPV)][j - (numPQ + numPV)]; + jacobMatrix[i][j] = j22[i - (numPQ + numPV)][j - (numPQ + numPV)]; } } } @@ -770,3 +809,19 @@ bool PowerFlow::CheckReactiveLimits(std::vector<BusType>& busType, } return limitReach; } + +bool PowerFlow::CalculateMotorsReactivePower(std::vector<std::complex<double> > voltage, + std::vector<std::complex<double> >& power) +{ + for(unsigned int i = 0; i < m_indMotorList.size(); ++i) { + IndMotor* motor = m_indMotorList[i]; + auto data = motor->GetElectricalData(); + if(motor->IsOnline() && data.calcQInPowerFlow) { + double oldQ = data.qValue; + if(!motor->CalculateReactivePower(std::abs(voltage[data.busNum]))) return false; + double dQ = oldQ - motor->GetElectricalData().qValue; + power[data.busNum] += std::complex<double>(0.0, dQ); + } + } + return true; +} diff --git a/Project/PowerFlow.h b/Project/PowerFlow.h index 2f3419a..e8945c8 100644 --- a/Project/PowerFlow.h +++ b/Project/PowerFlow.h @@ -51,15 +51,18 @@ class PowerFlow : public ElectricCalculation virtual bool RunNewtonRaphson(double systemPowerBase = 100e6, int maxIteration = 5000, double error = 1e-6, - double initAngle = 0.0); + double initAngle = 0.0, + double inertia = 1.0); virtual bool RunGaussNewton(double systemPowerBase = 100e6, int maxIteration = 5000, double error = 1e-6, double initAngle = 0.0, double accFactor = 1.0, - double gaussTol = 10); + double gaussTol = 1e-2, + double inertia = 1.0); virtual wxString GetErrorMessage() { return m_errorMsg; } + virtual int GetIterations() { return m_iterations; } protected: void GetNumPVPQ(std::vector<BusType> busType, int &numPQ, int &numPV); @@ -82,11 +85,15 @@ class PowerFlow : public ElectricCalculation std::vector<std::complex<double> > power, int numPV, int numPQ, - std::vector<double> dPdQ); + std::vector<double> dPdQ, + double inertia); + bool CalculateMotorsReactivePower(std::vector<std::complex<double> > voltage, + std::vector<std::complex<double> > &power); std::vector<std::vector<std::complex<double> > > m_yBus; wxString m_errorMsg = ""; int m_numberOfBuses = 0; + int m_iterations = 0; }; #endif // POWERFLOW_H diff --git a/Project/PropertiesData.h b/Project/PropertiesData.h index e6c156a..588cc00 100644 --- a/Project/PropertiesData.h +++ b/Project/PropertiesData.h @@ -39,6 +39,7 @@ struct SimulationData { double powerFlowTolerance = 1e-7; int powerFlowMaxIterations = 5000; double initAngle = 0.0; + double newtonInertia = 1.0; double gaussTolerance = 1e-2; // Stability diff --git a/Project/PropertiesForm.wxcp b/Project/PropertiesForm.wxcp index e579bf3..63265ec 100644 --- a/Project/PropertiesForm.wxcp +++ b/Project/PropertiesForm.wxcp @@ -1,7 +1,7 @@ { "metadata": { "m_generatedFilesDir": ".", - "m_objCounter": 970, + "m_objCounter": 976, "m_includeFiles": [], "m_bitmapFunction": "wxCDAD0InitBitmapResources", "m_bitmapsFile": "PropertiesFormBitmaps.cpp", @@ -3292,6 +3292,196 @@ }, { "type": "string", "m_label": "Name:", + "m_value": "boxSizerLvl4_22" + }, { + "type": "string", + "m_label": "Style:", + "m_value": "" + }, { + "type": "choice", + "m_label": "Orientation:", + "m_selection": 0, + "m_options": ["wxVERTICAL", "wxHORIZONTAL"] + }], + "m_events": [], + "m_children": [{ + "m_type": 4405, + "proportion": 0, + "border": 5, + "gbSpan": "1,1", + "gbPosition": "0,0", + "m_styles": [], + "m_sizerFlags": ["wxLEFT", "wxRIGHT", "wxTOP", "wxALIGN_CENTER_VERTICAL"], + "m_properties": [{ + "type": "winid", + "m_label": "ID:", + "m_winid": "wxID_ANY" + }, { + "type": "string", + "m_label": "Size:", + "m_value": "-1,-1" + }, { + "type": "string", + "m_label": "Minimum Size:", + "m_value": "-1,-1" + }, { + "type": "string", + "m_label": "Name:", + "m_value": "m_staticTextPFNewtonInertia" + }, { + "type": "multi-string", + "m_label": "Tooltip:", + "m_value": "" + }, { + "type": "colour", + "m_label": "Bg Colour:", + "colour": "<Default>" + }, { + "type": "colour", + "m_label": "Fg Colour:", + "colour": "<Default>" + }, { + "type": "font", + "m_label": "Font:", + "m_value": "" + }, { + "type": "bool", + "m_label": "Hidden", + "m_value": false + }, { + "type": "bool", + "m_label": "Disabled", + "m_value": false + }, { + "type": "bool", + "m_label": "Focused", + "m_value": false + }, { + "type": "string", + "m_label": "Class Name:", + "m_value": "" + }, { + "type": "string", + "m_label": "Include File:", + "m_value": "" + }, { + "type": "string", + "m_label": "Style:", + "m_value": "" + }, { + "type": "multi-string", + "m_label": "Label:", + "m_value": "Newton inertia" + }, { + "type": "string", + "m_label": "Wrap:", + "m_value": "-1" + }], + "m_events": [], + "m_children": [] + }, { + "m_type": 4406, + "proportion": 0, + "border": 5, + "gbSpan": "1,1", + "gbPosition": "0,0", + "m_styles": [], + "m_sizerFlags": ["wxLEFT", "wxRIGHT", "wxBOTTOM", "wxEXPAND", "wxALIGN_CENTER_VERTICAL"], + "m_properties": [{ + "type": "winid", + "m_label": "ID:", + "m_winid": "wxID_ANY" + }, { + "type": "string", + "m_label": "Size:", + "m_value": "-1,-1" + }, { + "type": "string", + "m_label": "Minimum Size:", + "m_value": "20,-1" + }, { + "type": "string", + "m_label": "Name:", + "m_value": "m_textCtrlPFNewtonInertia" + }, { + "type": "multi-string", + "m_label": "Tooltip:", + "m_value": "" + }, { + "type": "colour", + "m_label": "Bg Colour:", + "colour": "<Default>" + }, { + "type": "colour", + "m_label": "Fg Colour:", + "colour": "<Default>" + }, { + "type": "font", + "m_label": "Font:", + "m_value": "" + }, { + "type": "bool", + "m_label": "Hidden", + "m_value": false + }, { + "type": "bool", + "m_label": "Disabled", + "m_value": false + }, { + "type": "bool", + "m_label": "Focused", + "m_value": false + }, { + "type": "string", + "m_label": "Class Name:", + "m_value": "" + }, { + "type": "string", + "m_label": "Include File:", + "m_value": "" + }, { + "type": "string", + "m_label": "Style:", + "m_value": "" + }, { + "type": "string", + "m_label": "Value:", + "m_value": "1,0" + }, { + "type": "string", + "m_label": "Text Hint", + "m_value": "" + }, { + "type": "string", + "m_label": "Max Length:", + "m_value": "0" + }, { + "type": "bool", + "m_label": "Auto Complete Directories:", + "m_value": false + }, { + "type": "bool", + "m_label": "Auto Complete Files:", + "m_value": false + }], + "m_events": [], + "m_children": [] + }] + }, { + "m_type": 4401, + "proportion": 0, + "border": 5, + "gbSpan": "1,1", + "gbPosition": "0,0", + "m_styles": [], + "m_sizerFlags": ["wxEXPAND"], + "m_properties": [{ + "type": "string", + "m_label": "Minimum Size:", + "m_value": "-1,-1" + }, { + "type": "string", + "m_label": "Name:", "m_value": "boxSizerLvl4_21" }, { "type": "string", diff --git a/Project/PropertiesFormBase.cpp b/Project/PropertiesFormBase.cpp index c2fb1ad..f3cddf0 100644 --- a/Project/PropertiesFormBase.cpp +++ b/Project/PropertiesFormBase.cpp @@ -356,6 +356,26 @@ SimulationsSettingsFormBase::SimulationsSettingsFormBase(wxWindow* parent, boxSizerLvl5_17->Add(m_staticTextDeg_1, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, WXC_FROM_DIP(5)); + wxBoxSizer* boxSizerLvl4_22 = new wxBoxSizer(wxVERTICAL); + + gridSizerLvl_3_4->Add(boxSizerLvl4_22, 0, wxEXPAND, WXC_FROM_DIP(5)); + + m_staticTextPFNewtonInertia = new wxStaticText(m_panelPF, wxID_ANY, _("Newton inertia"), wxDefaultPosition, + wxDLG_UNIT(m_panelPF, wxSize(-1, -1)), 0); + + boxSizerLvl4_22->Add(m_staticTextPFNewtonInertia, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, + WXC_FROM_DIP(5)); + + m_textCtrlPFNewtonInertia = + new wxTextCtrl(m_panelPF, wxID_ANY, wxT("1,0"), wxDefaultPosition, wxDLG_UNIT(m_panelPF, wxSize(-1, -1)), 0); +#if wxVERSION_NUMBER >= 3000 + m_textCtrlPFNewtonInertia->SetHint(wxT("")); +#endif + + boxSizerLvl4_22->Add(m_textCtrlPFNewtonInertia, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND | wxALIGN_CENTER_VERTICAL, + WXC_FROM_DIP(5)); + m_textCtrlPFNewtonInertia->SetMinSize(wxSize(20, -1)); + wxBoxSizer* boxSizerLvl4_21 = new wxBoxSizer(wxVERTICAL); gridSizerLvl_3_4->Add(boxSizerLvl4_21, 0, wxEXPAND, WXC_FROM_DIP(5)); diff --git a/Project/PropertiesFormBase.h b/Project/PropertiesFormBase.h index 6b9cd82..dc7439b 100644 --- a/Project/PropertiesFormBase.h +++ b/Project/PropertiesFormBase.h @@ -105,6 +105,8 @@ class SimulationsSettingsFormBase : public wxDialog wxStaticText* m_staticTextPFSlackBusAngle; wxTextCtrl* m_textCtrlPFSlackBusAngle; wxStaticText* m_staticTextDeg_1; + wxStaticText* m_staticTextPFNewtonInertia; + wxTextCtrl* m_textCtrlPFNewtonInertia; wxStaticText* m_staticTextPFGaussTolerance; wxTextCtrl* m_textCtrlPFGaussTolerance; wxPanel* m_panelStability; @@ -182,6 +184,8 @@ class SimulationsSettingsFormBase : public wxDialog wxStaticText* GetStaticTextPFSlackBusAngle() { return m_staticTextPFSlackBusAngle; } wxTextCtrl* GetTextCtrlPFSlackBusAngle() { return m_textCtrlPFSlackBusAngle; } wxStaticText* GetStaticTextDeg_1() { return m_staticTextDeg_1; } + wxStaticText* GetStaticTextPFNewtonInertia() { return m_staticTextPFNewtonInertia; } + wxTextCtrl* GetTextCtrlPFNewtonInertia() { return m_textCtrlPFNewtonInertia; } wxStaticText* GetStaticTextPFGaussTolerance() { return m_staticTextPFGaussTolerance; } wxTextCtrl* GetTextCtrlPFGaussTolerance() { return m_textCtrlPFGaussTolerance; } wxPanel* GetPanelPF() { return m_panelPF; } diff --git a/Project/SimulationsSettingsForm.cpp b/Project/SimulationsSettingsForm.cpp index a9890a8..5b3cb1c 100644 --- a/Project/SimulationsSettingsForm.cpp +++ b/Project/SimulationsSettingsForm.cpp @@ -60,6 +60,7 @@ SimulationsSettingsForm::SimulationsSettingsForm(wxWindow* parent, PropertiesDat m_textCtrlPFTolerance->SetValue(wxString::Format("%g", data.powerFlowTolerance)); m_textCtrlPFMaxIterations->SetValue(wxString::Format("%d", data.powerFlowMaxIterations)); m_textCtrlPFSlackBusAngle->SetValue(Element::StringFromDouble(data.initAngle)); + m_textCtrlPFNewtonInertia->SetValue(Element::StringFromDouble(data.newtonInertia)); m_textCtrlPFGaussTolerance->SetValue(wxString::Format("%g", data.gaussTolerance)); m_textCtrlTimeStep->SetValue(wxString::Format("%g", data.timeStep)); m_textCtrlSimTime->SetValue(Element::StringFromDouble(data.stabilitySimulationTime)); @@ -136,6 +137,9 @@ bool SimulationsSettingsForm::ValidateData() if(!Element::DoubleFromString(this, m_textCtrlPFSlackBusAngle->GetValue(), data.initAngle, _("Value entered incorrectly in the field \"Slack bus angle\"."))) return false; + if(!Element::DoubleFromString(this, m_textCtrlPFNewtonInertia->GetValue(), data.newtonInertia, + _("Value entered incorrectly in the field \"Newton inertia (Power flow)\"."))) + return false; if(!Element::DoubleFromString(this, m_textCtrlPFGaussTolerance->GetValue(), data.gaussTolerance, _("Value entered incorrectly in the field \"Gauss tolerance (Power flow)\"."))) return false; @@ -240,4 +244,8 @@ void SimulationsSettingsForm::UpdatePFFieldStatus() m_textCtrlPFGaussTolerance->Enable(); else m_textCtrlPFGaussTolerance->Enable(false); + if(m_choicePFMethod->GetSelection() == 1 || m_choicePFMethod->GetSelection() == 2) + m_textCtrlPFNewtonInertia->Enable(); + else + m_textCtrlPFNewtonInertia->Enable(false); } diff --git a/Project/Workspace.cpp b/Project/Workspace.cpp index b057bdc..82989b5 100644 --- a/Project/Workspace.cpp +++ b/Project/Workspace.cpp @@ -1135,6 +1135,7 @@ bool Workspace::RunPowerFlow() basePower *= 1e3; PowerFlow pf(GetElementList()); bool result = false; + wxStopWatch sw; switch(simProp.powerFlowMethod) { case GAUSS_SEIDEL: { result = pf.RunGaussSeidel(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance, @@ -1142,16 +1143,21 @@ bool Workspace::RunPowerFlow() } break; case NEWTON_RAPHSON: { result = pf.RunNewtonRaphson(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance, - simProp.initAngle); + simProp.initAngle, simProp.newtonInertia); } break; case GAUSS_NEWTON: { - result = pf.RunGaussNewton(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance, - simProp.initAngle, simProp.accFator, simProp.gaussTolerance); + result = + pf.RunGaussNewton(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance, + simProp.initAngle, simProp.accFator, simProp.gaussTolerance, simProp.newtonInertia); } break; } + sw.Pause(); if(!result) { wxMessageDialog msgDialog(this, pf.GetErrorMessage(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR); msgDialog.ShowModal(); + } else { + m_statusBar->SetStatusText( + wxString::Format(_("Power flow converge with %d iterations (%ld ms)"), pf.GetIterations(), sw.Time())); } UpdateTextElements(); |