diff options
author | Thales Lima Oliveira <thaleslima.ufu@gmail.com> | 2019-04-26 02:29:47 -0300 |
---|---|---|
committer | Thales Lima Oliveira <thaleslima.ufu@gmail.com> | 2019-04-26 02:29:47 -0300 |
commit | a40d5a405d60b4e429f6f578dcfe3c33ab5ad81a (patch) | |
tree | 0f88240b49798ce5e3d9b96ca2951145a7978343 | |
parent | 4dabf27f998db83e20bc0eca7e18672777f0bf5b (diff) | |
download | PSP.git-a40d5a405d60b4e429f6f578dcfe3c33ab5ad81a.tar.gz PSP.git-a40d5a405d60b4e429f6f578dcfe3c33ab5ad81a.tar.xz PSP.git-a40d5a405d60b4e429f6f578dcfe3c33ab5ad81a.zip |
Frequency response implemented
Need a form
-rw-r--r-- | Project/Bus.cpp | 30 | ||||
-rw-r--r-- | Project/Bus.h | 4 | ||||
-rw-r--r-- | Project/FileHanding.cpp | 23 | ||||
-rw-r--r-- | Project/Load.cpp | 2 | ||||
-rw-r--r-- | Project/Load.h | 2 | ||||
-rw-r--r-- | Project/MainFrame.cpp | 13 | ||||
-rw-r--r-- | Project/PowerElement.h | 17 | ||||
-rw-r--r-- | Project/PowerQuality.cpp | 266 | ||||
-rw-r--r-- | Project/PowerQuality.h | 14 | ||||
-rw-r--r-- | Project/PropertiesData.h | 1 | ||||
-rw-r--r-- | Project/PropertiesForm.wxcp | 663 | ||||
-rw-r--r-- | Project/PropertiesFormBase.cpp | 60 | ||||
-rw-r--r-- | Project/PropertiesFormBase.h | 104 | ||||
-rw-r--r-- | Project/SimulationsSettingsForm.cpp | 2 | ||||
-rw-r--r-- | Project/SyncGenerator.cpp | 2 | ||||
-rw-r--r-- | Project/SyncGenerator.h | 2 | ||||
-rw-r--r-- | Project/Text.cpp | 16 | ||||
-rw-r--r-- | Project/Text.h | 3 | ||||
-rw-r--r-- | Project/TextForm.cpp | 16 | ||||
-rw-r--r-- | Project/Workspace.cpp | 52 | ||||
-rw-r--r-- | Project/Workspace.h | 1 |
21 files changed, 773 insertions, 520 deletions
diff --git a/Project/Bus.cpp b/Project/Bus.cpp index eecc06a..37eac3e 100644 --- a/Project/Bus.cpp +++ b/Project/Bus.cpp @@ -239,23 +239,31 @@ wxString Bus::GetTipText() const } tipText += _("\n\nSsc = ") + wxString::FromDouble(std::abs(m_electricalData.scPower), 5) + _(" p.u."); + tipText += _("\n\nTHD = ") + wxString::FromDouble(std::abs(m_electricalData.thd), 5) + wxT("\%"); return tipText; } -bool Bus::GetPlotData(ElementPlotData& plotData) +bool Bus::GetPlotData(ElementPlotData& plotData, PlotStudy study) { - if(!m_electricalData.plotBus) return false; - plotData.SetName(m_electricalData.name); - plotData.SetCurveType(ElementPlotData::CT_BUS); - - std::vector<double> absVoltage, argVoltage; - for(unsigned int i = 0; i < m_electricalData.stabVoltageVector.size(); ++i) { - absVoltage.push_back(std::abs(m_electricalData.stabVoltageVector[i])); - argVoltage.push_back(wxRadToDeg(std::arg(m_electricalData.stabVoltageVector[i]))); + if(study == STABILITY) { + if(!m_electricalData.plotBus) return false; + plotData.SetName(m_electricalData.name); + plotData.SetCurveType(ElementPlotData::CT_BUS); + + std::vector<double> absVoltage, argVoltage; + for(unsigned int i = 0; i < m_electricalData.stabVoltageVector.size(); ++i) { + absVoltage.push_back(std::abs(m_electricalData.stabVoltageVector[i])); + argVoltage.push_back(wxRadToDeg(std::arg(m_electricalData.stabVoltageVector[i]))); + } + plotData.AddData(absVoltage, _("Voltage")); + plotData.AddData(argVoltage, _("Angle")); + } else if(FREQRESPONSE) { + //if(!m_electricalData.plotBus) return false; + plotData.SetName(m_electricalData.name); + plotData.SetCurveType(ElementPlotData::CT_BUS); + plotData.AddData(m_electricalData.absImpedanceVector, _("Impedance")); } - plotData.AddData(absVoltage, _("Voltage")); - plotData.AddData(argVoltage, _("Angle")); return true; } diff --git a/Project/Bus.h b/Project/Bus.h index 2423be6..e2947ac 100644 --- a/Project/Bus.h +++ b/Project/Bus.h @@ -62,6 +62,8 @@ struct BusElectricalData { std::vector<int> harmonicOrder; std::vector< std::complex<double> > harmonicVoltage; double thd = 0.0; + + std::vector<double> absImpedanceVector; }; /** @@ -92,7 +94,7 @@ class Bus : public PowerElement virtual BusElectricalData GetElectricalData() const { return m_electricalData; } virtual void SetElectricalData(BusElectricalData electricalData) { m_electricalData = electricalData; } virtual bool ShowForm(wxWindow* parent, Element* element); - virtual bool GetPlotData(ElementPlotData& plotData); + virtual bool GetPlotData(ElementPlotData& plotData, PlotStudy study = STABILITY); virtual rapidxml::xml_node<>* SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode); virtual bool OpenElement(rapidxml::xml_node<>* elementNode); diff --git a/Project/FileHanding.cpp b/Project/FileHanding.cpp index 7120063..f5facbf 100644 --- a/Project/FileHanding.cpp +++ b/Project/FileHanding.cpp @@ -232,7 +232,7 @@ void FileHanding::SaveProject(wxFileName path) elementID++; } //} - + //{ HarmCurrent auto harmCurrentNode = XMLParser::AppendNode(doc, elementsNode, "HarmCurrentList"); auto harmCurrentList = allElements.GetHarmCurrentList(); @@ -492,19 +492,20 @@ bool FileHanding::OpenProject(wxFileName path) transfomerNode = transfomerNode->next_sibling("Transfomer"); } //} - - //{ Transformer + + //{ HarmCurrent auto harmCurrentListNode = elementsNode->first_node("HarmCurrentList"); - if(!harmCurrentListNode) return false; - auto harmCurrentNode = harmCurrentListNode->first_node("HarmCurrent"); - while(harmCurrentNode) { - HarmCurrent* harmCurrent = new HarmCurrent(); + if(harmCurrentListNode) { + auto harmCurrentNode = harmCurrentListNode->first_node("HarmCurrent"); + while(harmCurrentNode) { + HarmCurrent* harmCurrent = new HarmCurrent(); - if(!harmCurrent->OpenElement(harmCurrentNode, parentList)) return false; - elementList.push_back(harmCurrent); - harmCurrentList.push_back(harmCurrent); + if(!harmCurrent->OpenElement(harmCurrentNode, parentList)) return false; + elementList.push_back(harmCurrent); + harmCurrentList.push_back(harmCurrent); - harmCurrentNode = harmCurrentNode->next_sibling("HarmCurrent"); + harmCurrentNode = harmCurrentNode->next_sibling("HarmCurrent"); + } } //} m_workspace->SetElementList(elementList); diff --git a/Project/Load.cpp b/Project/Load.cpp index 64c5514..d9f1b2a 100644 --- a/Project/Load.cpp +++ b/Project/Load.cpp @@ -248,7 +248,7 @@ wxString Load::GetTipText() const return tipText; } -bool Load::GetPlotData(ElementPlotData& plotData) +bool Load::GetPlotData(ElementPlotData& plotData, PlotStudy study) { if(!m_electricalData.plotLoad) return false; plotData.SetName(m_electricalData.name); diff --git a/Project/Load.h b/Project/Load.h index 342f377..4fa01ad 100644 --- a/Project/Load.h +++ b/Project/Load.h @@ -87,7 +87,7 @@ class Load : public Shunt LoadElectricalData GetElectricalData() { return m_electricalData; } LoadElectricalData GetPUElectricalData(double systemPowerBase); void SetElectricalData(LoadElectricalData electricalData) { m_electricalData = electricalData; } - virtual bool GetPlotData(ElementPlotData& plotData); + virtual bool GetPlotData(ElementPlotData& plotData, PlotStudy study = STABILITY); virtual rapidxml::xml_node<>* SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode); virtual bool OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList); diff --git a/Project/MainFrame.cpp b/Project/MainFrame.cpp index 3af0545..5e81c89 100644 --- a/Project/MainFrame.cpp +++ b/Project/MainFrame.cpp @@ -22,12 +22,12 @@ #include "DataReport.h" #include "FileHanding.h" #include "GeneralPropertiesForm.h" +#include "HarmCurrent.h" #include "ImportForm.h" #include "IndMotor.h" #include "Inductor.h" #include "Line.h" #include "Load.h" -#include "HarmCurrent.h" #include "MainFrame.h" #include "PropertiesData.h" #include "SimulationsSettingsForm.h" @@ -153,8 +153,9 @@ void MainFrame::CreateAddElementsMenu() _("Adds a shunt capacitor at the circuit")); wxMenuItem* inductorElement = new wxMenuItem(m_addElementsMenu, ID_ADDMENU_INDUCTOR, _("&Inductor\tShift-I"), _("Adds a shunt inductor at the circuit")); - wxMenuItem* harmCurrentElement = new wxMenuItem(m_addElementsMenu, ID_ADDMENU_HARMCURRENT, _("&Harmonic current\tShift-H"), - _("Adds a harmonic current source at the circuit")); + wxMenuItem* harmCurrentElement = + new wxMenuItem(m_addElementsMenu, ID_ADDMENU_HARMCURRENT, _("&Harmonic current\tShift-H"), + _("Adds a harmonic current source at the circuit")); m_addElementsMenu->Append(busElement); m_addElementsMenu->Append(lineElement); @@ -446,8 +447,8 @@ void MainFrame::OnAddElementsClick(wxCommandEvent& event) newElement = true; } break; case ID_ADDMENU_HARMCURRENT: { - HarmCurrent* newHarmCurrent = - new HarmCurrent(wxString::Format(_("Harmonic Current %d"), workspace->GetElementNumber(ID_INDUCTOR))); + HarmCurrent* newHarmCurrent = new HarmCurrent( + wxString::Format(_("Harmonic Current %d"), workspace->GetElementNumber(ID_INDUCTOR))); workspace->IncrementElementNumber(ID_HARMCURRENT); elementList.push_back(newHarmCurrent); statusBarText = _("Insert Harmonic Current Source: Click on a buses, ESC to cancel."); @@ -556,6 +557,8 @@ void MainFrame::OnSimulationSettingsClick(wxRibbonButtonBarEvent& event) } void MainFrame::OnFreqResponseClick(wxRibbonButtonBarEvent& event) { + Workspace* workspace = static_cast<Workspace*>(m_auiNotebook->GetCurrentPage()); + if(workspace) { workspace->RunFrequencyResponse(); } } void MainFrame::OnHarmDistortionsClick(wxRibbonButtonBarEvent& event) { diff --git a/Project/PowerElement.h b/Project/PowerElement.h index d2b13e7..031834e 100644 --- a/Project/PowerElement.h +++ b/Project/PowerElement.h @@ -24,7 +24,7 @@ /** * @enum ElectricalUnit * @brief Electrical units. -*/ + */ enum ElectricalUnit { UNIT_PU = 0, /**< Per unit (p.u.) */ UNIT_V, /**< Volt */ @@ -51,7 +51,7 @@ enum ElectricalUnit { /** * @enum FaultData * @brief Information about fault (type and location). -*/ + */ enum FaultData { FAULT_THREEPHASE = 0, /**< Three-phase fault */ FAULT_2LINE, /**< Line-to-line fault */ @@ -65,7 +65,7 @@ enum FaultData { /** * @enum SwitchingType * @brief Type of switching. -*/ + */ enum SwitchingType { SW_INSERT = 0, /**< Insert element */ SW_REMOVE /**< Remove element */ @@ -74,7 +74,7 @@ enum SwitchingType { /** * @enum PowerFlowDirection * @brief Direction of power flow arrows. -*/ + */ enum PowerFlowDirection { PF_NONE = 0, /**< No direction (no arrows printed) */ PF_TO_BUS, /**< Element to bus */ @@ -83,6 +83,11 @@ enum PowerFlowDirection { PF_BUS2_TO_BUS1 /**< Second bus to first bus (branch elements) */ }; +enum PlotStudy { + STABILITY = 0, /**< Stability studies */ + FREQRESPONSE /**< Frequency resonse (Harmonics) */ +}; + /** * @class SwitchingData * @author Thales Lima Oliveira @@ -195,7 +200,7 @@ class PowerElement : public Element * @param plotData Plot data to be filled. * @return true if the plot data was successfully filled, false otherwise. */ - virtual bool GetPlotData(ElementPlotData& plotData) { return false; } + virtual bool GetPlotData(ElementPlotData& plotData, PlotStudy study = STABILITY) { return false; } /** * @brief Check if the power element have dynamic event. * @return true if the element have dynamic an event, false otherwise. @@ -207,7 +212,7 @@ class PowerElement : public Element */ virtual void SetDynamicEvent(bool dynEvent = true) { m_dynEvent = dynEvent; } virtual double GetValueFromUnit(double value, ElectricalUnit valueUnit); - + virtual void SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode); virtual void SaveSwitchingData(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* electricalNode); virtual bool OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList) { return true; } diff --git a/Project/PowerQuality.cpp b/Project/PowerQuality.cpp index 4efdc49..30969d9 100644 --- a/Project/PowerQuality.cpp +++ b/Project/PowerQuality.cpp @@ -23,133 +23,127 @@ void PowerQuality::CalculateHarmonicYbusList(double systemPowerBase) // Fill all Ybuses for(auto itYbus = m_harmYbusList.begin(), itYbusEnd = m_harmYbusList.end(); itYbus != itYbusEnd; ++itYbus) { HarmonicYbus harmYBus = *itYbus; - // Load - for(auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) { - Load* load = *it; - if(load->IsOnline()) { - int n = static_cast<Bus*>(load->GetParentList()[0])->GetElectricalData().number; - LoadElectricalData data = load->GetPUElectricalData(systemPowerBase); - wxMessageBox(wxString::Format("%f %f", data.activePower, data.reactivePower)); - std::complex<double> yLoad = - std::complex<double>(data.activePower, -data.reactivePower / harmYBus.order); - std::complex<double> v = static_cast<Bus*>(load->GetParentList()[0])->GetElectricalData().voltage; - yLoad /= (std::abs(v) * std::abs(v)); - harmYBus.yBus[n][n] += yLoad; - } + CalculateHarmonicYbus(harmYBus.yBus, systemPowerBase, harmYBus.order); + *itYbus = harmYBus; + } +} + +void PowerQuality::CalculateHarmonicYbus(std::vector<std::vector<std::complex<double> > >& yBus, + double systemPowerBase, + double order) +{ + // Load + for(auto it = m_loadList.begin(), itEnd = m_loadList.end(); it != itEnd; ++it) { + Load* load = *it; + if(load->IsOnline()) { + int n = static_cast<Bus*>(load->GetParentList()[0])->GetElectricalData().number; + LoadElectricalData data = load->GetPUElectricalData(systemPowerBase); + std::complex<double> yLoad = std::complex<double>(data.activePower, -data.reactivePower / order); + std::complex<double> v = static_cast<Bus*>(load->GetParentList()[0])->GetElectricalData().voltage; + yLoad /= (std::abs(v) * std::abs(v)); + yBus[n][n] += yLoad; } + } - // Capacitor - for(auto it = m_capacitorList.begin(), itEnd = m_capacitorList.end(); it != itEnd; ++it) { - Capacitor* capacitor = *it; - if(capacitor->IsOnline()) { - int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalData().number; - CapacitorElectricalData data = capacitor->GetPUElectricalData(systemPowerBase); - harmYBus.yBus[n][n] += std::complex<double>(0.0, data.reactivePower) * harmYBus.order; - } + // Capacitor + for(auto it = m_capacitorList.begin(), itEnd = m_capacitorList.end(); it != itEnd; ++it) { + Capacitor* capacitor = *it; + if(capacitor->IsOnline()) { + int n = static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalData().number; + CapacitorElectricalData data = capacitor->GetPUElectricalData(systemPowerBase); + yBus[n][n] += std::complex<double>(0.0, data.reactivePower) * order; } + } - // Inductor - for(auto it = m_inductorList.begin(), itEnd = m_inductorList.end(); it != itEnd; ++it) { - Inductor* inductor = *it; - if(inductor->IsOnline()) { - int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalData().number; - InductorElectricalData data = inductor->GetPUElectricalData(systemPowerBase); - harmYBus.yBus[n][n] += std::complex<double>(0.0, -data.reactivePower) / harmYBus.order; - } + // Inductor + for(auto it = m_inductorList.begin(), itEnd = m_inductorList.end(); it != itEnd; ++it) { + Inductor* inductor = *it; + if(inductor->IsOnline()) { + int n = static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalData().number; + InductorElectricalData data = inductor->GetPUElectricalData(systemPowerBase); + yBus[n][n] += std::complex<double>(0.0, -data.reactivePower) / order; } + } - // Power line - for(auto it = m_lineList.begin(), itEnd = m_lineList.end(); it != itEnd; ++it) { - Line* line = *it; - if(line->IsOnline()) { - LineElectricalData data = line->GetPUElectricalData(systemPowerBase); + // Power line + for(auto it = m_lineList.begin(), itEnd = m_lineList.end(); it != itEnd; ++it) { + Line* line = *it; + if(line->IsOnline()) { + LineElectricalData data = line->GetPUElectricalData(systemPowerBase); - int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData().number; - int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData().number; + int n1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData().number; + int n2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData().number; - harmYBus.yBus[n1][n2] -= - 1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); - harmYBus.yBus[n2][n1] -= - 1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); + yBus[n1][n2] -= 1.0 / std::complex<double>(data.resistance, data.indReactance * order); + yBus[n2][n1] -= 1.0 / std::complex<double>(data.resistance, data.indReactance * order); - harmYBus.yBus[n1][n1] += - 1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); - harmYBus.yBus[n2][n2] += - 1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); + yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance * order); + yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance * order); - harmYBus.yBus[n1][n1] += std::complex<double>(0.0, (data.capSusceptance * harmYBus.order) / 2.0); - harmYBus.yBus[n2][n2] += std::complex<double>(0.0, (data.capSusceptance * harmYBus.order) / 2.0); - } + yBus[n1][n1] += std::complex<double>(0.0, (data.capSusceptance * order) / 2.0); + yBus[n2][n2] += std::complex<double>(0.0, (data.capSusceptance * order) / 2.0); } + } - // Transformer - for(auto it = m_transformerList.begin(), itEnd = m_transformerList.end(); it != itEnd; ++it) { - Transformer* transformer = *it; + // Transformer + for(auto it = m_transformerList.begin(), itEnd = m_transformerList.end(); it != itEnd; ++it) { + Transformer* transformer = *it; - if(transformer->IsOnline()) { - TransformerElectricalData data = transformer->GetPUElectricalData(systemPowerBase); + if(transformer->IsOnline()) { + TransformerElectricalData data = transformer->GetPUElectricalData(systemPowerBase); - int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalData().number; - int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalData().number; + int n1 = static_cast<Bus*>(transformer->GetParentList()[0])->GetElectricalData().number; + int n2 = static_cast<Bus*>(transformer->GetParentList()[1])->GetElectricalData().number; - // If the transformer have nominal turns ratio (1.0) and no phase shifting, it will be modelled as - // series impedance. - if(data.turnsRatio == 1.0 && data.phaseShift == 0.0) { - harmYBus.yBus[n1][n2] += - -1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); - harmYBus.yBus[n2][n1] += - -1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); + // If the transformer have nominal turns ratio (1.0) and no phase shifting, it will be modelled as + // series impedance. + if(data.turnsRatio == 1.0 && data.phaseShift == 0.0) { + yBus[n1][n2] += -1.0 / std::complex<double>(data.resistance, data.indReactance * order); + yBus[n2][n1] += -1.0 / std::complex<double>(data.resistance, data.indReactance * order); - harmYBus.yBus[n1][n1] += - 1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); - harmYBus.yBus[n2][n2] += - 1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); - } - // If the transformer have no-nominal turn ratio and/or phase shifting, it will be modelled in a - // different way (see references). - //[Ref. 1: Elementos de analise de sistemas de potencia - Stevenson - pg. 232] - //[Ref. 2: - // http://www.ee.washington.edu/research/real/Library/Reports/Tap_Adjustments_in_AC_Load_Flows.pdf] - // [Ref. 3: http://www.columbia.edu/~dano/courses/power/notes/power/andersson1.pdf] - else { - // Complex turns ratio - double radPhaseShift = wxDegToRad(data.phaseShift); - std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift), - -data.turnsRatio * std::sin(radPhaseShift)); - - // Transformer admitance - std::complex<double> y = - 1.0 / std::complex<double>(data.resistance, data.indReactance * harmYBus.order); - - harmYBus.yBus[n1][n1] += y / (std::pow(std::abs(a), 2.0)); - harmYBus.yBus[n1][n2] += -(y / std::conj(a)); - harmYBus.yBus[n2][n1] += -(y / a); - harmYBus.yBus[n2][n2] += y; - } + yBus[n1][n1] += 1.0 / std::complex<double>(data.resistance, data.indReactance * order); + yBus[n2][n2] += 1.0 / std::complex<double>(data.resistance, data.indReactance * order); } - } + // If the transformer have no-nominal turn ratio and/or phase shifting, it will be modelled in a + // different way (see references). + //[Ref. 1: Elementos de analise de sistemas de potencia - Stevenson - pg. 232] + //[Ref. 2: + // http://www.ee.washington.edu/research/real/Library/Reports/Tap_Adjustments_in_AC_Load_Flows.pdf] + // [Ref. 3: http://www.columbia.edu/~dano/courses/power/notes/power/andersson1.pdf] + else { + // Complex turns ratio + double radPhaseShift = wxDegToRad(data.phaseShift); + std::complex<double> a = std::complex<double>(data.turnsRatio * std::cos(radPhaseShift), + -data.turnsRatio * std::sin(radPhaseShift)); + + // Transformer admitance + std::complex<double> y = 1.0 / std::complex<double>(data.resistance, data.indReactance * order); - // Synchronous generator - for(auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) { - SyncGenerator* syncGenerator = *it; - if(syncGenerator->IsOnline()) { - int n = static_cast<Bus*>(syncGenerator->GetParentList()[0])->GetElectricalData().number; - SyncGeneratorElectricalData data = syncGenerator->GetPUElectricalData(systemPowerBase); - harmYBus.yBus[n][n] += - 1.0 / std::complex<double>(data.positiveResistance, data.positiveReactance * harmYBus.order); + yBus[n1][n1] += y / (std::pow(std::abs(a), 2.0)); + yBus[n1][n2] += -(y / std::conj(a)); + yBus[n2][n1] += -(y / a); + yBus[n2][n2] += y; } } - // Synchronous motor - for(auto it = m_syncMotorList.begin(), itEnd = m_syncMotorList.end(); it != itEnd; ++it) { - SyncMotor* syncMotor = *it; - if(syncMotor->IsOnline()) { - int n = static_cast<Bus*>(syncMotor->GetParentList()[0])->GetElectricalData().number; - SyncMotorElectricalData data = syncMotor->GetPUElectricalData(systemPowerBase); - harmYBus.yBus[n][n] += - 1.0 / std::complex<double>(data.positiveResistance, data.positiveReactance * harmYBus.order); - } + } + + // Synchronous generator + for(auto it = m_syncGeneratorList.begin(), itEnd = m_syncGeneratorList.end(); it != itEnd; ++it) { + SyncGenerator* syncGenerator = *it; + if(syncGenerator->IsOnline()) { + int n = static_cast<Bus*>(syncGenerator->GetParentList()[0])->GetElectricalData().number; + SyncGeneratorElectricalData data = syncGenerator->GetPUElectricalData(systemPowerBase); + yBus[n][n] += 1.0 / std::complex<double>(data.positiveResistance, data.positiveReactance * order); + } + } + // Synchronous motor + for(auto it = m_syncMotorList.begin(), itEnd = m_syncMotorList.end(); it != itEnd; ++it) { + SyncMotor* syncMotor = *it; + if(syncMotor->IsOnline()) { + int n = static_cast<Bus*>(syncMotor->GetParentList()[0])->GetElectricalData().number; + SyncMotorElectricalData data = syncMotor->GetPUElectricalData(systemPowerBase); + yBus[n][n] += 1.0 / std::complex<double>(data.positiveResistance, data.positiveReactance * order); } - *itYbus = harmYBus; } } @@ -207,7 +201,7 @@ bool PowerQuality::CalculateDistortions(double systemPowerBase) for(unsigned int i = 0; i < m_harmYbusList.size(); ++i) { vHarmList.push_back(GaussianElimination(m_harmYbusList[i].yBus, iHarmInjList[i])); } - + for(auto it = m_busList.begin(), itEnd = m_busList.end(); it != itEnd; ++it) { Bus* bus = *it; auto data = bus->GetElectricalData(); @@ -219,9 +213,10 @@ bool PowerQuality::CalculateDistortions(double systemPowerBase) data.harmonicVoltage.push_back(vHarmList[i][data.number]); data.harmonicOrder.push_back(static_cast<int>(harmOrders[i])); } - //distortion = std::sqrt(distortion) / std::abs(data.voltage); + // distortion = std::sqrt(distortion) / std::abs(data.voltage); thd = std::sqrt(thd) * 100.0; data.thd = thd; + bus->SetElectricalData(data); } return true; @@ -256,3 +251,58 @@ std::vector<double> PowerQuality::GetHarmonicOrdersList() } return doubleHarmOrder; } + +bool PowerQuality::CalculateFrequencyResponse(double systemFreq, + double initFreq, + double endFreq, + double stepFreq, + double systemPowerBase) +{ + // Clear all previous data + for(unsigned int i = 0; i < m_busList.size(); i++) { + auto data = m_busList[i]->GetElectricalData(); + data.absImpedanceVector.clear(); + data.absImpedanceVector.shrink_to_fit(); + m_busList[i]->SetElectricalData(data); + } + + // Create and fill with zeros the YBus + std::vector<std::vector<std::complex<double> > > yBus; + for(unsigned int i = 0; i < m_busList.size(); i++) { + std::vector<std::complex<double> > line; + for(unsigned int j = 0; j < m_busList.size(); j++) { line.push_back(std::complex<double>(0.0, 0.0)); } + yBus.push_back(line); + } + // Create and fill with zoros the injected current vector + std::vector<std::complex<double> > iInj; + for(unsigned int i = 0; i < m_busList.size(); i++) { iInj.push_back(std::complex<double>(10.0, 0.0)); } + + if(initFreq < 1e-6) initFreq = stepFreq; + double currentFreq = initFreq; + while(currentFreq <= endFreq) { + m_frequencyList.push_back(currentFreq); + + double order = currentFreq / systemFreq; + + // Fill YBus with zeros + for(unsigned int i = 0; i < m_busList.size(); i++) { + for(unsigned int j = 0; j < m_busList.size(); j++) { yBus[i][j] = std::complex<double>(0.0, 0.0); } + } + + CalculateHarmonicYbus(yBus, systemPowerBase, order); + + for(unsigned int i = 0; i < m_busList.size(); i++) { + auto data = m_busList[i]->GetElectricalData(); + iInj[data.number] = std::complex<double>(1.0, 0.0); + + auto zh = GaussianElimination(yBus, iInj); + + data.absImpedanceVector.push_back(std::abs(zh[data.number])); + m_busList[i]->SetElectricalData(data); + iInj[data.number] = std::complex<double>(0.0, 0.0); + } + + currentFreq += stepFreq; + } + return false; +} diff --git a/Project/PowerQuality.h b/Project/PowerQuality.h index ad47884..6c443e3 100644 --- a/Project/PowerQuality.h +++ b/Project/PowerQuality.h @@ -22,16 +22,26 @@ class PowerQuality : public ElectricCalculation }; PowerQuality(); - PowerQuality(std::vector<Element*> elementList); + PowerQuality(std::vector<Element *> elementList); ~PowerQuality(); - + virtual void CalculateHarmonicYbusList(double systemPowerBase = 100e6); + virtual void CalculateHarmonicYbus(std::vector<std::vector<std::complex<double> > > &yBus, + double systemPowerBase, + double order); virtual bool CalculateDistortions(double systemPowerBase = 100e6); + virtual bool CalculateFrequencyResponse(double systemFreq = 60.0, + double initFreq = 0.0, + double endFreq = 1500.0, + double stepFreq = 1.0, + double systemPowerBase = 100e6); virtual std::vector<double> GetHarmonicOrdersList(); + virtual std::vector<double> GetFrequencies() { return m_frequencyList; } protected: std::vector<HarmonicYbus> m_harmYbusList; + std::vector<double> m_frequencyList; }; #endif // POWERQUALITY_H diff --git a/Project/PropertiesData.h b/Project/PropertiesData.h index 127eb59..926b90b 100644 --- a/Project/PropertiesData.h +++ b/Project/PropertiesData.h @@ -31,6 +31,7 @@ struct SimulationData { ElectricalUnit basePowerUnit = UNIT_MVA; bool faultAfterPowerFlow = false; bool scPowerAfterPowerFlow = false; + bool harmDistortionAfterPowerFlow = false; // Power flow PowerFlowMethod powerFlowMethod = GAUSS_SEIDEL; diff --git a/Project/PropertiesForm.wxcp b/Project/PropertiesForm.wxcp index 3962cd0..f4f531a 100644 --- a/Project/PropertiesForm.wxcp +++ b/Project/PropertiesForm.wxcp @@ -1,7 +1,7 @@ { "metadata": { "m_generatedFilesDir": ".", - "m_objCounter": 815, + "m_objCounter": 816, "m_includeFiles": [], "m_bitmapFunction": "wxCDAD0InitBitmapResources", "m_bitmapsFile": "PropertiesFormBitmaps.cpp", @@ -1531,6 +1531,299 @@ }] }] }, { + "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": "boxSizerLvl3_2" + }, { + "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_staticTextFreq" + }, { + "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": "System frequency" + }, { + "type": "string", + "m_label": "Wrap:", + "m_value": "-1" + }], + "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_5" + }, { + "type": "string", + "m_label": "Style:", + "m_value": "" + }, { + "type": "choice", + "m_label": "Orientation:", + "m_selection": 1, + "m_options": ["wxVERTICAL", "wxHORIZONTAL"] + }], + "m_events": [], + "m_children": [{ + "m_type": 4406, + "proportion": 1, + "border": 5, + "gbSpan": "1,1", + "gbPosition": "0,0", + "m_styles": [], + "m_sizerFlags": ["wxLEFT", "wxRIGHT", "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_textCtrlFreq" + }, { + "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": "60,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": 4405, + "proportion": 0, + "border": 5, + "gbSpan": "1,1", + "gbPosition": "0,0", + "m_styles": [], + "m_sizerFlags": ["wxLEFT", "wxRIGHT", "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_staticTextFreqUnit" + }, { + "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": "Hz" + }, { + "type": "string", + "m_label": "Wrap:", + "m_value": "-1" + }], + "m_events": [], + "m_children": [] + }] + }] + }, { "m_type": 4449, "proportion": 0, "border": 5, @@ -1711,6 +2004,81 @@ }], "m_events": [], "m_children": [] + }, { + "m_type": 4415, + "proportion": 0, + "border": 5, + "gbSpan": "1,1", + "gbPosition": "0,0", + "m_styles": [], + "m_sizerFlags": ["wxALL", "wxLEFT", "wxRIGHT", "wxTOP", "wxBOTTOM"], + "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_checkBoxTHDAfterPF" + }, { + "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 harmonic distortion after power flow" + }, { + "type": "bool", + "m_label": "Value:", + "m_value": true + }], + "m_events": [], + "m_children": [] }] }] }] @@ -3660,299 +4028,6 @@ }, { "type": "string", "m_label": "Name:", - "m_value": "boxSizerLvl3_5" - }, { - "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_staticTextFreq" - }, { - "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": "System frequency" - }, { - "type": "string", - "m_label": "Wrap:", - "m_value": "-1" - }], - "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_5" - }, { - "type": "string", - "m_label": "Style:", - "m_value": "" - }, { - "type": "choice", - "m_label": "Orientation:", - "m_selection": 1, - "m_options": ["wxVERTICAL", "wxHORIZONTAL"] - }], - "m_events": [], - "m_children": [{ - "m_type": 4406, - "proportion": 1, - "border": 5, - "gbSpan": "1,1", - "gbPosition": "0,0", - "m_styles": [], - "m_sizerFlags": ["wxLEFT", "wxRIGHT", "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_textCtrlFreq" - }, { - "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": "60,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": 4405, - "proportion": 0, - "border": 5, - "gbSpan": "1,1", - "gbPosition": "0,0", - "m_styles": [], - "m_sizerFlags": ["wxLEFT", "wxRIGHT", "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_staticTextFreqUnit" - }, { - "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": "Hz" - }, { - "type": "string", - "m_label": "Wrap:", - "m_value": "-1" - }], - "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": "boxSizerLvl3_8" }, { "type": "string", diff --git a/Project/PropertiesFormBase.cpp b/Project/PropertiesFormBase.cpp index 62f2c0d..e76b211 100644 --- a/Project/PropertiesFormBase.cpp +++ b/Project/PropertiesFormBase.cpp @@ -190,6 +190,33 @@ SimulationsSettingsFormBase::SimulationsSettingsFormBase(wxWindow* parent, boxSizerLvl4_1->Add(m_choiceBasePower, 0, wxLEFT | wxRIGHT | wxBOTTOM, WXC_FROM_DIP(5)); + wxBoxSizer* boxSizerLvl3_2 = new wxBoxSizer(wxVERTICAL); + + boxSizerLvl2_1->Add(boxSizerLvl3_2, 0, wxEXPAND, WXC_FROM_DIP(5)); + + m_staticTextFreq = new wxStaticText(m_panelGeneral, wxID_ANY, _("System frequency"), wxDefaultPosition, + wxDLG_UNIT(m_panelGeneral, wxSize(-1, -1)), 0); + + boxSizerLvl3_2->Add(m_staticTextFreq, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, WXC_FROM_DIP(5)); + + wxBoxSizer* boxSizerLvl4_5 = new wxBoxSizer(wxHORIZONTAL); + + boxSizerLvl3_2->Add(boxSizerLvl4_5, 0, wxEXPAND, WXC_FROM_DIP(5)); + + m_textCtrlFreq = new wxTextCtrl(m_panelGeneral, wxID_ANY, wxT("60,0"), wxDefaultPosition, + wxDLG_UNIT(m_panelGeneral, wxSize(-1, -1)), 0); +#if wxVERSION_NUMBER >= 3000 + m_textCtrlFreq->SetHint(wxT("")); +#endif + + boxSizerLvl4_5->Add(m_textCtrlFreq, 1, wxLEFT | wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, WXC_FROM_DIP(5)); + + m_staticTextFreqUnit = new wxStaticText(m_panelGeneral, wxID_ANY, _("Hz"), wxDefaultPosition, + wxDLG_UNIT(m_panelGeneral, wxSize(-1, -1)), 0); + + boxSizerLvl4_5->Add(m_staticTextFreqUnit, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, + WXC_FROM_DIP(5)); + wxStaticBoxSizer* staticBoxSizerLvl3_2 = new wxStaticBoxSizer(new wxStaticBox(m_panelGeneral, wxID_ANY, _("Continuous calculation")), wxVERTICAL); @@ -208,6 +235,12 @@ SimulationsSettingsFormBase::SimulationsSettingsFormBase(wxWindow* parent, staticBoxSizerLvl3_2->Add(m_checkBoxSCPowerAfterPF, 0, wxALL, WXC_FROM_DIP(5)); + m_checkBoxTHDAfterPF = new wxCheckBox(m_panelGeneral, wxID_ANY, _("Calculate harmonic distortion after power flow"), + wxDefaultPosition, wxDLG_UNIT(m_panelGeneral, wxSize(-1, -1)), 0); + m_checkBoxTHDAfterPF->SetValue(true); + + staticBoxSizerLvl3_2->Add(m_checkBoxTHDAfterPF, 0, wxALL, WXC_FROM_DIP(5)); + m_panelPF = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDLG_UNIT(m_notebook, wxSize(-1, -1)), wxTAB_TRAVERSAL); m_notebook->AddPage(m_panelPF, _("Power flow"), false); @@ -385,33 +418,6 @@ SimulationsSettingsFormBase::SimulationsSettingsFormBase(wxWindow* parent, boxSizerLvl4_7->Add(m_staticTextSec_2, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, WXC_FROM_DIP(5)); - wxBoxSizer* boxSizerLvl3_5 = new wxBoxSizer(wxVERTICAL); - - gridSizerLvl_2_3->Add(boxSizerLvl3_5, 0, wxEXPAND, WXC_FROM_DIP(5)); - - m_staticTextFreq = new wxStaticText(m_panelStability, wxID_ANY, _("System frequency"), wxDefaultPosition, - wxDLG_UNIT(m_panelStability, wxSize(-1, -1)), 0); - - boxSizerLvl3_5->Add(m_staticTextFreq, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, WXC_FROM_DIP(5)); - - wxBoxSizer* boxSizerLvl4_5 = new wxBoxSizer(wxHORIZONTAL); - - boxSizerLvl3_5->Add(boxSizerLvl4_5, 0, wxEXPAND, WXC_FROM_DIP(5)); - - m_textCtrlFreq = new wxTextCtrl(m_panelStability, wxID_ANY, wxT("60,0"), wxDefaultPosition, - wxDLG_UNIT(m_panelStability, wxSize(-1, -1)), 0); -#if wxVERSION_NUMBER >= 3000 - m_textCtrlFreq->SetHint(wxT("")); -#endif - - boxSizerLvl4_5->Add(m_textCtrlFreq, 1, wxLEFT | wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, WXC_FROM_DIP(5)); - - m_staticTextFreqUnit = new wxStaticText(m_panelStability, wxID_ANY, _("Hz"), wxDefaultPosition, - wxDLG_UNIT(m_panelStability, wxSize(-1, -1)), 0); - - boxSizerLvl4_5->Add(m_staticTextFreqUnit, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, - WXC_FROM_DIP(5)); - wxBoxSizer* boxSizerLvl3_8 = new wxBoxSizer(wxVERTICAL); gridSizerLvl_2_3->Add(boxSizerLvl3_8, 0, wxEXPAND, WXC_FROM_DIP(5)); diff --git a/Project/PropertiesFormBase.h b/Project/PropertiesFormBase.h index f0a579b..d7d4dce 100644 --- a/Project/PropertiesFormBase.h +++ b/Project/PropertiesFormBase.h @@ -7,32 +7,32 @@ #ifndef _PSP_PROJECT_PROPERTIESFORM_BASE_CLASSES_H #define _PSP_PROJECT_PROPERTIESFORM_BASE_CLASSES_H -#include <wx/settings.h> -#include <wx/xrc/xmlres.h> -#include <wx/xrc/xh_bmp.h> +#include <wx/arrstr.h> +#include <wx/artprov.h> +#include <wx/button.h> +#include <wx/checkbox.h> +#include <wx/choice.h> #include <wx/dialog.h> +#include <wx/filepicker.h> +#include <wx/grid.h> +#include <wx/hyperlink.h> #include <wx/iconbndl.h> -#include <wx/artprov.h> -#include <wx/sizer.h> +#include <wx/imaglist.h> #include <wx/notebook.h> #include <wx/panel.h> -#include <wx/imaglist.h> +#include <wx/richtext/richtextctrl.h> +#include <wx/settings.h> +#include <wx/sizer.h> +#include <wx/statbmp.h> +#include <wx/statbox.h> #include <wx/stattext.h> -#include <wx/choice.h> -#include <wx/arrstr.h> -#include <wx/button.h> #include <wx/textctrl.h> -#include <wx/statbox.h> -#include <wx/checkbox.h> -#include <wx/statbmp.h> -#include <wx/grid.h> -#include <wx/richtext/richtextctrl.h> -#include <wx/hyperlink.h> -#include <wx/filepicker.h> +#include <wx/xrc/xh_bmp.h> +#include <wx/xrc/xmlres.h> #if wxVERSION_NUMBER >= 2900 #include <wx/persist.h> -#include <wx/persist/toplevel.h> #include <wx/persist/bookctrl.h> +#include <wx/persist/toplevel.h> #include <wx/persist/treebook.h> #endif @@ -45,10 +45,9 @@ #define WXC_FROM_DIP(x) x #endif - class GeneralPropertiesFormBase : public wxDialog { -protected: + protected: wxNotebook* m_notebook; wxPanel* m_panelGeneral; wxStaticText* m_staticTextLanguage; @@ -58,11 +57,11 @@ protected: wxButton* m_buttonOK; wxButton* m_buttonCancel; -protected: + protected: virtual void OnButtonOKClick(wxCommandEvent& event) { event.Skip(); } virtual void OnButtonCancelClick(wxCommandEvent& event) { event.Skip(); } -public: + public: wxStaticText* GetStaticTextLanguage() { return m_staticTextLanguage; } wxChoice* GetChoiceLanguage() { return m_choiceLanguage; } wxStaticText* GetStaticTextTheme() { return m_staticTextTheme; } @@ -71,21 +70,29 @@ public: wxNotebook* GetNotebook() { return m_notebook; } wxButton* GetButtonOK() { return m_buttonOK; } wxButton* GetButtonCancel() { return m_buttonCancel; } - GeneralPropertiesFormBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("General settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(-1,-1), long style = wxDEFAULT_DIALOG_STYLE); + GeneralPropertiesFormBase(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxString& title = _("General settings"), + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxSize(-1, -1), + long style = wxDEFAULT_DIALOG_STYLE); virtual ~GeneralPropertiesFormBase(); }; - class SimulationsSettingsFormBase : public wxDialog { -protected: + protected: wxNotebook* m_notebook; wxPanel* m_panelGeneral; wxStaticText* m_staticTextBasePower; wxTextCtrl* m_textCtrlbasePower; wxChoice* m_choiceBasePower; + wxStaticText* m_staticTextFreq; + wxTextCtrl* m_textCtrlFreq; + wxStaticText* m_staticTextFreqUnit; wxCheckBox* m_checkBoxFaultAfterPF; wxCheckBox* m_checkBoxSCPowerAfterPF; + wxCheckBox* m_checkBoxTHDAfterPF; wxPanel* m_panelPF; wxStaticText* m_staticTextPFMethod; wxChoice* m_choicePFMethod; @@ -105,9 +112,6 @@ protected: wxStaticText* m_staticTextTSimTime; wxTextCtrl* m_textCtrlSimTime; wxStaticText* m_staticTextSec_2; - wxStaticText* m_staticTextFreq; - wxTextCtrl* m_textCtrlFreq; - wxStaticText* m_staticTextFreqUnit; wxStaticText* m_staticTextTStabTolerance; wxTextCtrl* m_textCtrlStabTolerance; wxStaticText* m_staticTextTStabMaxIterations; @@ -148,18 +152,22 @@ protected: wxButton* m_buttonOK; wxButton* m_buttonCancel; -protected: + protected: virtual void OnPFMethodChoiceSelected(wxCommandEvent& event) { event.Skip(); } virtual void OnCheckboxUseCompLoadClick(wxCommandEvent& event) { event.Skip(); } virtual void OnButtonOKClick(wxCommandEvent& event) { event.Skip(); } virtual void OnButtonCancelClick(wxCommandEvent& event) { event.Skip(); } -public: + public: wxStaticText* GetStaticTextBasePower() { return m_staticTextBasePower; } wxTextCtrl* GetTextCtrlbasePower() { return m_textCtrlbasePower; } wxChoice* GetChoiceBasePower() { return m_choiceBasePower; } + wxStaticText* GetStaticTextFreq() { return m_staticTextFreq; } + wxTextCtrl* GetTextCtrlFreq() { return m_textCtrlFreq; } + wxStaticText* GetStaticTextFreqUnit() { return m_staticTextFreqUnit; } wxCheckBox* GetCheckBoxFaultAfterPF() { return m_checkBoxFaultAfterPF; } wxCheckBox* GetCheckBoxSCPowerAfterPF() { return m_checkBoxSCPowerAfterPF; } + wxCheckBox* GetCheckBoxTHDAfterPF() { return m_checkBoxTHDAfterPF; } wxPanel* GetPanelGeneral() { return m_panelGeneral; } wxStaticText* GetStaticTextPFMethod() { return m_staticTextPFMethod; } wxChoice* GetChoicePFMethod() { return m_choicePFMethod; } @@ -179,9 +187,6 @@ public: wxStaticText* GetStaticTextTSimTime() { return m_staticTextTSimTime; } wxTextCtrl* GetTextCtrlSimTime() { return m_textCtrlSimTime; } wxStaticText* GetStaticTextSec_2() { return m_staticTextSec_2; } - wxStaticText* GetStaticTextFreq() { return m_staticTextFreq; } - wxTextCtrl* GetTextCtrlFreq() { return m_textCtrlFreq; } - wxStaticText* GetStaticTextFreqUnit() { return m_staticTextFreqUnit; } wxStaticText* GetStaticTextTStabTolerance() { return m_staticTextTStabTolerance; } wxTextCtrl* GetTextCtrlStabTolerance() { return m_textCtrlStabTolerance; } wxStaticText* GetStaticTextTStabMaxIterations() { return m_staticTextTStabMaxIterations; } @@ -223,14 +228,18 @@ public: wxNotebook* GetNotebook() { return m_notebook; } wxButton* GetButtonOK() { return m_buttonOK; } wxButton* GetButtonCancel() { return m_buttonCancel; } - SimulationsSettingsFormBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Simulation settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(-1,-1), long style = wxDEFAULT_DIALOG_STYLE); + SimulationsSettingsFormBase(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxString& title = _("Simulation settings"), + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxSize(-1, -1), + long style = wxDEFAULT_DIALOG_STYLE); virtual ~SimulationsSettingsFormBase(); }; - class AboutFormBase : public wxDialog { -protected: + protected: wxNotebook* m_notebook; wxPanel* m_panelLogo; wxStaticBitmap* m_staticBitmapLogo; @@ -244,10 +253,10 @@ protected: wxHyperlinkCtrl* m_hyperLinkPSP; wxButton* m_buttonOK; -protected: + protected: virtual void OnOKButtonClick(wxCommandEvent& event) { event.Skip(); } -public: + public: wxStaticBitmap* GetStaticBitmapLogo() { return m_staticBitmapLogo; } wxPanel* GetPanelLogo() { return m_panelLogo; } wxGrid* GetGridCredits() { return m_gridCredits; } @@ -260,14 +269,18 @@ public: wxStaticText* GetStaticTextHome() { return m_staticTextHome; } wxHyperlinkCtrl* GetHyperLinkPSP() { return m_hyperLinkPSP; } wxButton* GetButtonOK() { return m_buttonOK; } - AboutFormBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About PSP-UFU"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(-1,-1), long style = wxDEFAULT_DIALOG_STYLE); + AboutFormBase(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxString& title = _("About PSP-UFU"), + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxSize(-1, -1), + long style = wxDEFAULT_DIALOG_STYLE); virtual ~AboutFormBase(); }; - class ImportFormBase : public wxDialog { -protected: + protected: wxNotebook* m_notebook; wxPanel* m_panelCEPEL; wxStaticText* m_staticTextBasePWFFile; @@ -286,11 +299,11 @@ protected: wxButton* m_buttonOK; wxButton* m_buttonCancel; -protected: + protected: virtual void OnButtonOKClick(wxCommandEvent& event) { event.Skip(); } virtual void OnButtonCancelClick(wxCommandEvent& event) { event.Skip(); } -public: + public: wxStaticText* GetStaticTextBasePWFFile() { return m_staticTextBasePWFFile; } wxFilePickerCtrl* GetFilePickerANAREDEPWF() { return m_filePickerANAREDEPWF; } wxStaticText* GetStaticTextBaseLSTFile() { return m_staticTextBaseLSTFile; } @@ -308,7 +321,12 @@ public: wxNotebook* GetNotebook() { return m_notebook; } wxButton* GetButtonOK() { return m_buttonOK; } wxButton* GetButtonCancel() { return m_buttonCancel; } - ImportFormBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Import files"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(-1,-1), long style = wxDEFAULT_DIALOG_STYLE); + ImportFormBase(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxString& title = _("Import files"), + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxSize(-1, -1), + long style = wxDEFAULT_DIALOG_STYLE); virtual ~ImportFormBase(); }; diff --git a/Project/SimulationsSettingsForm.cpp b/Project/SimulationsSettingsForm.cpp index b7878d5..2947638 100644 --- a/Project/SimulationsSettingsForm.cpp +++ b/Project/SimulationsSettingsForm.cpp @@ -41,6 +41,7 @@ SimulationsSettingsForm::SimulationsSettingsForm(wxWindow* parent, PropertiesDat } m_checkBoxFaultAfterPF->SetValue(data.faultAfterPowerFlow); m_checkBoxSCPowerAfterPF->SetValue(data.scPowerAfterPowerFlow); + m_checkBoxTHDAfterPF->SetValue(data.harmDistortionAfterPowerFlow); switch(data.powerFlowMethod) { case GAUSS_SEIDEL: { m_choicePFMethod->SetSelection(0); @@ -107,6 +108,7 @@ bool SimulationsSettingsForm::ValidateData() } data.faultAfterPowerFlow = m_checkBoxFaultAfterPF->GetValue(); data.scPowerAfterPowerFlow = m_checkBoxSCPowerAfterPF->GetValue(); + data.harmDistortionAfterPowerFlow = m_checkBoxTHDAfterPF->GetValue(); switch(m_choicePFMethod->GetSelection()) { case 0: { data.powerFlowMethod = GAUSS_SEIDEL; diff --git a/Project/SyncGenerator.cpp b/Project/SyncGenerator.cpp index c988a7e..78082d2 100644 --- a/Project/SyncGenerator.cpp +++ b/Project/SyncGenerator.cpp @@ -223,7 +223,7 @@ wxString SyncGenerator::GetTipText() const return tipText; } -bool SyncGenerator::GetPlotData(ElementPlotData& plotData) +bool SyncGenerator::GetPlotData(ElementPlotData& plotData, PlotStudy study) { if(!m_electricalData.plotSyncMachine) return false; plotData.SetName(m_electricalData.name); diff --git a/Project/SyncGenerator.h b/Project/SyncGenerator.h index 70c4142..1a25a0e 100644 --- a/Project/SyncGenerator.h +++ b/Project/SyncGenerator.h @@ -156,7 +156,7 @@ class SyncGenerator : public Machines virtual void SetElectricalData(SyncGeneratorElectricalData electricalData) { m_electricalData = electricalData; } virtual void SetNominalVoltage(std::vector<double> nominalVoltage, std::vector<ElectricalUnit> nominalVoltageUnit); virtual void SavePlotData(); - virtual bool GetPlotData(ElementPlotData& plotData); + virtual bool GetPlotData(ElementPlotData& plotData, PlotStudy study = STABILITY); virtual rapidxml::xml_node<>* SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode); virtual bool OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList); diff --git a/Project/Text.cpp b/Project/Text.cpp index 0697ac5..f73b425 100644 --- a/Project/Text.cpp +++ b/Project/Text.cpp @@ -270,6 +270,9 @@ void Text::UpdateText(double systemPowerBase) break; } } break; + case DATA_PQ_THD: { + SetText("THD = " + wxString::FromDouble(data.thd, m_decimalPlaces) + "\%"); + } break; default: break; } @@ -913,6 +916,19 @@ void Text::UpdateText(double systemPowerBase) } } } break; + case TYPE_HARMCURRENT: { + HarmCurrent* harmCurrent = static_cast<HarmCurrent*>(m_element); + if(harmCurrent) { + auto data = harmCurrent->GetElectricalData(); + switch(m_dataType) { + case DATA_NAME: { + SetText(data.name); + } break; + default: + break; + } + } + } break; } } diff --git a/Project/Text.h b/Project/Text.h index 56fc821..19e3876 100644 --- a/Project/Text.h +++ b/Project/Text.h @@ -64,7 +64,8 @@ enum DataType { DATA_PF_ACTIVE, DATA_PF_REACTIVE, DATA_PF_LOSSES, - DATA_PF_CURRENT + DATA_PF_CURRENT, + DATA_PQ_THD }; /** diff --git a/Project/TextForm.cpp b/Project/TextForm.cpp index 993593c..30497fd 100644 --- a/Project/TextForm.cpp +++ b/Project/TextForm.cpp @@ -131,6 +131,9 @@ void TextForm::OnTypeChoiceSelected(wxCommandEvent& event) case 5: { m_text->SetDataType(DATA_SC_POWER); } break; + case 6: { + m_text->SetDataType(DATA_PQ_THD); + } break; } } break; case TYPE_SYNC_GENERATOR: { @@ -203,7 +206,7 @@ void TextForm::OnTypeChoiceSelected(wxCommandEvent& event) } DataTypeChoice(); - if(m_text->GetDataType() == DATA_NAME) Preview(); + if(m_text->GetDataType() == DATA_NAME || m_text->GetDataType() == DATA_PQ_THD) Preview(); } bool TextForm::LoadChoices() @@ -305,6 +308,9 @@ bool TextForm::LoadChoices() break; } } break; + case DATA_PQ_THD: { + m_choiceTextType->SetSelection(6); + } break; default: break; } @@ -875,6 +881,7 @@ void TextForm::ElementNumberChoice() arrayString.Add(_("Fault current")); arrayString.Add(_("Fault voltage")); arrayString.Add(_("Short-circuit power")); + arrayString.Add(_("Voltage THD")); } break; case TYPE_SYNC_GENERATOR: { SyncGenerator* syncGenerator = m_allElements.GetSyncGeneratorList()[index]; @@ -966,7 +973,8 @@ void TextForm::DataTypeChoice() wxArrayString arrayString; switch(m_text->GetDataType()) { - case DATA_NAME: { + case DATA_NAME: + case DATA_PQ_THD: { m_choiceTextUnit->Enable(false); return; } break; @@ -1191,7 +1199,9 @@ bool TextForm::ValidateData() if(m_choiceElement->GetSelection() == -1) return false; if(m_choiceName->GetSelection() == -1) return false; if(m_choiceTextType->GetSelection() == -1) return false; - if(m_text->GetDataType() != DATA_NAME && m_choiceTextUnit->GetSelection() == -1) return false; + if(m_text->GetDataType() != DATA_NAME && m_text->GetDataType() != DATA_PQ_THD && + m_choiceTextUnit->GetSelection() == -1) + return false; if(m_text->GetElementType() == TYPE_LINE || m_text->GetElementType() == TYPE_TRANSFORMER) { if(m_text->GetDataType() != DATA_PF_LOSSES && m_text->GetDataType() != DATA_NAME) { if(m_choiceTextFromBus->GetSelection() == -1) return false; diff --git a/Project/Workspace.cpp b/Project/Workspace.cpp index a17faae..6b8d197 100644 --- a/Project/Workspace.cpp +++ b/Project/Workspace.cpp @@ -1491,8 +1491,8 @@ void Workspace::OnMiddleDoubleClick(wxMouseEvent& event) bool Workspace::RunStaticStudies() { - bool pfStatus, faultStatus, scStatus; - pfStatus = faultStatus = scStatus = false; + bool pfStatus, faultStatus, scStatus, harmStatus; + pfStatus = faultStatus = scStatus = harmStatus = false; pfStatus = RunPowerFlow(); @@ -1507,8 +1507,14 @@ bool Workspace::RunStaticStudies() } else { scStatus = true; } + + if(m_properties->GetSimulationPropertiesData().harmDistortionAfterPowerFlow) { + if(pfStatus) harmStatus = RunHarmonicDistortion(); + } else { + harmStatus = true; + } - if(pfStatus && faultStatus && scStatus) return true; + if(pfStatus && faultStatus && scStatus && harmStatus) return true; return false; } @@ -1523,5 +1529,43 @@ bool Workspace::RunHarmonicDistortion() basePower *= 1e3; if(!RunPowerFlow()) return false; PowerQuality pq(GetElementList()); - return pq.CalculateDistortions(basePower); + bool result = pq.CalculateDistortions(basePower); + + UpdateTextElements(); + Redraw(); + + return result; +} + +bool Workspace::RunFrequencyResponse() +{ + auto simProp = m_properties->GetSimulationPropertiesData(); + double basePower = simProp.basePower; + if(simProp.basePowerUnit == UNIT_MVA) + basePower *= 1e6; + else if(simProp.basePowerUnit == UNIT_kVA) + basePower *= 1e3; + PowerQuality pq(GetElementList()); + bool result = pq.CalculateFrequencyResponse(simProp.stabilityFrequency, 0.0, 1500.0, 1.0, basePower); + + wxMessageDialog msgDialog( + this, + wxString::Format(_("Calculations done.\nDo you wish to open the frequency response graphics?")), + _("Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION); + if(msgDialog.ShowModal() == wxID_YES) { + std::vector<ElementPlotData> plotDataList; + for(auto it = m_elementList.begin(), itEnd = m_elementList.end(); it != itEnd; ++it) { + PowerElement* element = *it; + ElementPlotData plotData; + if(element->GetPlotData(plotData, FREQRESPONSE)) plotDataList.push_back(plotData); + } + + ChartView* cView = new ChartView(this, plotDataList, pq.GetFrequencies()); + cView->Show(); + } + + UpdateTextElements(); + Redraw(); + + return result; } diff --git a/Project/Workspace.h b/Project/Workspace.h index 38c5356..afcb1b3 100644 --- a/Project/Workspace.h +++ b/Project/Workspace.h @@ -149,6 +149,7 @@ class Workspace : public WorkspaceBase bool RunStaticStudies(); bool RunStability(); bool RunHarmonicDistortion(); + bool RunFrequencyResponse(); protected: virtual void OnMiddleDoubleClick(wxMouseEvent& event); |