From eb11954f4ac815f091b75bc7e9b8850f3bbdd5d2 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Thu, 9 Sep 2021 12:55:37 -0600 Subject: [PATCH 01/26] Create v1 of custom combine cases widget and add to ui --- CMakeLists.txt | 3 +- deploy/runtime/ui/Generic System Plant.txt | 59 ++++++++- src/combinecases.cpp | 145 +++++++++++++++++++++ src/combinecases.h | 70 ++++++++++ src/invoke.cpp | 32 +++++ 5 files changed, 306 insertions(+), 3 deletions(-) create mode 100644 src/combinecases.cpp create mode 100644 src/combinecases.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1467388432..22953f3bd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,8 @@ set(SAM_SRC src/shadingfactors.cpp src/materials.cpp src/codegenerator.cpp - src/pythonhandler.cpp src/pythonhandler.h) + src/pythonhandler.cpp src/pythonhandler.h + src/combinecases.cpp src/combinecases.h) ##################################################################################################################### diff --git a/deploy/runtime/ui/Generic System Plant.txt b/deploy/runtime/ui/Generic System Plant.txt index 7d4c96035e..f85166ed8f 100644 --- a/deploy/runtime/ui/Generic System Plant.txt +++ b/deploy/runtime/ui/Generic System Plant.txt @@ -1,7 +1,7 @@ Generic System Plant 788 527 -19 +20 GroupBox 1 @@ -118,6 +118,36 @@ Tool Tip TabOrder 3 7 +Button + +1 +8 +Name +5 +15 +btn_selectcases +X +3 +66 +Y +3 +93 +Width +3 +90 +Height +3 +24 +Tool Tip +5 +0 +Caption +5 +15 +Select Cases... +TabOrder +3 +4294967295 Numeric 1 @@ -1152,7 +1182,7 @@ equations{ 'ui_step_minutes' } = define() { } }; -1032 +1466 on_load{"Generic System Plant"}=define(){ on_change{'spec_mode'}(); on_change{'system_capacity'}(); @@ -1167,6 +1197,31 @@ on_change{'system_capacity'}=define(){ check_inputs(); }; +on_change{'btn_selectcases'} = define() { + if ( value('spec_mode') == 0 ) // change to 2 eventually + { + combine_cases(); + } +}; + +// display window for combine cases +function combine_cases() +{ + // msgbox(list_cases()); // crashes SAM + hash = combinecasesquery(); + // meta data + if ( hash != '' ) + { + x=1; + //fn = hash{'file'}; + //dn = hash{'folder'}; + //af = hash{'addfolder'}; + //if ( fn != '' ) + //{ + // x = 1; + //} + } +} function check_inputs() { mode = value('spec_mode'); diff --git a/src/combinecases.cpp b/src/combinecases.cpp new file mode 100644 index 0000000000..cf419aa2ed --- /dev/null +++ b/src/combinecases.cpp @@ -0,0 +1,145 @@ +/** +BSD-3-Clause +Copyright 2019 Alliance for Sustainable Energy, LLC +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met : +1. Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER, CONTRIBUTORS, UNITED STATES GOVERNMENT OR UNITED STATES +DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "combinecases.h" +#include "main.h" + +enum { + ID_chlCases, + ID_chkOverwriteCapital, + ID_spndDegradation +}; + +BEGIN_EVENT_TABLE( CombineCasesDialog, wxDialog ) + EVT_CHECKLISTBOX(ID_chlCases, CombineCasesDialog::OnEvt) + EVT_CHECKBOX(ID_chkOverwriteCapital, CombineCasesDialog::OnEvt) + //EVT_SPINCTRLDOUBLE(ID_spndDegradation, CombineCasesDialog::OnEvt) +END_EVENT_TABLE() + +CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) + : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + // Text at top of window + wxString msg = "Select open cases, simulate those cases and combine their generation profiles into a single profile to be used with this generic case."; + + // Case selection + m_chlCases = new wxCheckListBox(this, ID_chlCases, wxDefaultPosition, wxSize(800, 200)); // populate with active cases + wxBoxSizer* szcases = new wxBoxSizer(wxVERTICAL); + szcases->Add(new wxStaticText(this, wxID_ANY, "1. Select cases:"), 0, wxALL, 2); + szcases->Add(m_chlCases, 10, wxALL | wxEXPAND, 1); + + // Overwrite capital checkbox + m_chkOverwriteCapital = new wxCheckBox(this, ID_chkOverwriteCapital, "Overwrite Capital Expenses"); + + // Annual AC degradation + m_spndDegradation = new wxSpinCtrlDouble(this, ID_spndDegradation, "Annual AC Degradation"); + + // Combine all into main vertical sizer + wxBoxSizer* szmain = new wxBoxSizer(wxVERTICAL); + szmain->Add(new wxStaticText(this, wxID_ANY, msg), 0, wxALL | wxEXPAND, 10); + szmain->Add(szcases, 10, wxALL | wxEXPAND, 1); + szmain->Add(m_chkOverwriteCapital); + szmain->Add(m_spndDegradation); + szmain->Add(CreateButtonSizer(wxHELP | wxOK | wxCANCEL), 0, wxALL | wxEXPAND, 10); + + SetSizer(szmain); + Fit(); + m_chlCases->SetFocus(); + + // for testing: + //wxConfig& config = SamApp::Settings(); + //MainWindow* main_window = SamApp::Window(); // MainWindow.m_caseTabList MainWindow.m_project.m_cases + //int x = 1; +} + +void CombineCasesDialog::OnEvt(wxCommandEvent& e) +{ + switch (e.GetId()) + { + case wxID_HELP: + SamApp::ShowHelp("combine_cases"); + break; + case ID_chlCases: + { + for (size_t i = 0; i < m_cases.size(); i++) + { + if (m_cases[i].display == m_chlCases->GetString(e.GetInt())) + { + m_cases[i].is_selected = m_chlCases->IsChecked(e.GetInt()); + } + } + } + break; + case wxID_OK: + { + wxArrayInt arychecked; + for (size_t i = 0; i < m_cases.size(); i++) + { + if (m_cases[i].is_selected) + { + arychecked.push_back((int)i); + } + } + if (arychecked.Count() >= 2) + { + bool overwrite_capital = m_chkOverwriteCapital->IsChecked(); + double degradation = m_spndDegradation->GetValue(); + + + EndModal(wxID_OK); + } + else if (arychecked.Count() == 1) + { + wxMessageBox("Not enough cases selected.\n\nChoose at least two cases to combine.", "Combine Cases Message", wxOK, this); + } + else + { + wxMessageBox("No cases selected.\n\nChoose at least two cases to combine.", "Combine Cases Message", wxOK, this); + } + } + break; + } +} + +// Remake checklist widget with info in cases vector +void CombineCasesDialog::RefreshList(size_t first_item) +{ + m_chlCases->Freeze(); + m_chlCases->Clear(); + for (size_t i = 0; i < m_cases.size(); i++) + { + int ndx = m_chlCases->Append(m_cases[i].display); + if (m_cases[i].is_selected) { + m_chlCases->Check(ndx, true); + } + else { + m_chlCases->Check(ndx, false); + } + } + m_chlCases->Thaw(); + m_chlCases->SetFirstItem(first_item); +} diff --git a/src/combinecases.h b/src/combinecases.h new file mode 100644 index 0000000000..bdefc04f38 --- /dev/null +++ b/src/combinecases.h @@ -0,0 +1,70 @@ +/** +BSD-3-Clause +Copyright 2019 Alliance for Sustainable Energy, LLC +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met : +1. Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER, CONTRIBUTORS, UNITED STATES GOVERNMENT OR UNITED STATES +DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __CombineCasesDialog_h +#define __CombineCasesDialog_h + +#include + + +class wxCheckListBox; +class wxCheckbox; +class wxSpinCtrlDouble; + +class CombineCasesDialog : public wxDialog +{ + +public: + CombineCasesDialog(wxWindow* parent, const wxString& title); + + struct CaseInfo + { + wxString name; // dataset - e.g. psm + wxString displayName; + wxString display; + wxString attributes; // limit column and file size to SAM specific per NSRDB + bool is_visible; + bool is_selected; + CaseInfo(wxString& _n, wxString& _dn, wxString& _a) + : name(_n), displayName(_dn), attributes(_a) + { + display = name; + is_visible = true; + is_selected = false; + } + }; + +private: + void OnEvt(wxCommandEvent &); + + void RefreshList(size_t first_item); + + std::vector m_cases; + wxCheckListBox* m_chlCases; + wxCheckBox* m_chkOverwriteCapital; + wxSpinCtrlDouble* m_spndDegradation; + + DECLARE_EVENT_TABLE() +}; + + +#endif diff --git a/src/invoke.cpp b/src/invoke.cpp index 17a5a57d3c..faeab4be0f 100644 --- a/src/invoke.cpp +++ b/src/invoke.cpp @@ -73,6 +73,7 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "stochastic.h" #include "codegencallback.h" #include "nsrdb.h" +#include "combinecases.h" #include "wavetoolkit.h" #include "graph.h" @@ -2206,6 +2207,36 @@ void fcall_nsrdbquery(lk::invoke_t &cxt) cxt.result().hash_item("addfolder").assign(addfolder); } +void fcall_combinecasesquery(lk::invoke_t& cxt) +{ + LK_DOC("combinecasesquery", "Creates the Combine Cases dialog box, lists all open cases, simulates selected cases and returns a combined generation profile", "(none) : string"); + CombineCasesDialog dlgCombineCases(SamApp::Window(), "Combine Cases"); + dlgCombineCases.CenterOnParent(); + int code = dlgCombineCases.ShowModal(); //shows the dialog and makes it so you can't interact with other parts until window is closed + + //Return an empty string if the window was dismissed + if (code == wxID_CANCEL) + { + cxt.result().assign(wxEmptyString); + return; + } + + // Just for testing: + cxt.result().assign(wxEmptyString); + return; + ////Get selected filename + //wxString foldername = dlgNSRDB.GetWeatherFolder(); + //wxString filename = dlgNSRDB.GetWeatherFile(); + //wxString addfolder = dlgNSRDB.GetAddFolder(); + + //cxt.result().empty_hash(); + + //// meta data + //cxt.result().hash_item("file").assign(filename); + //cxt.result().hash_item("folder").assign(foldername); + //cxt.result().hash_item("addfolder").assign(addfolder); +} + void fcall_wavetoolkit(lk::invoke_t& cxt) { LK_DOC("wavetoolkit", "Creates the Wave data download dialog box, lists all avaialble resource files, downloads multiple solar resource files, and returns local file name for weather file", "(none) : string"); @@ -5601,6 +5632,7 @@ lk::fcall_t* invoke_uicallback_funcs() fcall_current_at_voltage_sandia, fcall_windtoolkit, fcall_nsrdbquery, + fcall_combinecasesquery, fcall_wavetoolkit, fcall_openeiutilityrateform, fcall_group_read, From 1a36acc61c6d6f9e41d7dfe8fe202c17c2efa070 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Thu, 9 Sep 2021 22:34:15 -0600 Subject: [PATCH 02/26] Run cases from widget --- src/combinecases.cpp | 64 ++++++++++++++++++++++++++++++++------------ src/combinecases.h | 11 ++++---- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index cf419aa2ed..9d71b98bae 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -25,8 +25,10 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include "casewin.h" #include "combinecases.h" #include "main.h" +#include "script.h" enum { ID_chlCases, @@ -38,16 +40,21 @@ BEGIN_EVENT_TABLE( CombineCasesDialog, wxDialog ) EVT_CHECKLISTBOX(ID_chlCases, CombineCasesDialog::OnEvt) EVT_CHECKBOX(ID_chkOverwriteCapital, CombineCasesDialog::OnEvt) //EVT_SPINCTRLDOUBLE(ID_spndDegradation, CombineCasesDialog::OnEvt) + EVT_BUTTON(wxID_OK, CombineCasesDialog::OnEvt) + EVT_BUTTON(wxID_HELP, CombineCasesDialog::OnEvt) END_EVENT_TABLE() CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { + // Text at top of window wxString msg = "Select open cases, simulate those cases and combine their generation profiles into a single profile to be used with this generic case."; // Case selection m_chlCases = new wxCheckListBox(this, ID_chlCases, wxDefaultPosition, wxSize(800, 200)); // populate with active cases + this->GetOpenCases(); + this->RefreshList(0.); wxBoxSizer* szcases = new wxBoxSizer(wxVERTICAL); szcases->Add(new wxStaticText(this, wxID_ANY, "1. Select cases:"), 0, wxALL, 2); szcases->Add(m_chlCases, 10, wxALL | wxEXPAND, 1); @@ -69,11 +76,6 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) SetSizer(szmain); Fit(); m_chlCases->SetFocus(); - - // for testing: - //wxConfig& config = SamApp::Settings(); - //MainWindow* main_window = SamApp::Window(); // MainWindow.m_caseTabList MainWindow.m_project.m_cases - //int x = 1; } void CombineCasesDialog::OnEvt(wxCommandEvent& e) @@ -87,7 +89,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) { for (size_t i = 0; i < m_cases.size(); i++) { - if (m_cases[i].display == m_chlCases->GetString(e.GetInt())) + if (m_cases[i].display_name == m_chlCases->GetString(e.GetInt())) { m_cases[i].is_selected = m_chlCases->IsChecked(e.GetInt()); } @@ -97,27 +99,46 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) case wxID_OK: { wxArrayInt arychecked; - for (size_t i = 0; i < m_cases.size(); i++) - { - if (m_cases[i].is_selected) - { + // Find which cases are selected + for (size_t i = 0; i < m_cases.size(); i++) { + if (m_cases[i].is_selected) { arychecked.push_back((int)i); } } - if (arychecked.Count() >= 2) - { + if (arychecked.Count() >= 1) { bool overwrite_capital = m_chkOverwriteCapital->IsChecked(); double degradation = m_spndDegradation->GetValue(); + // Simulate each case + for (size_t i = 0; i < arychecked.Count(); i++) { + // Switch to case to make current case + SamApp::Window()->SwitchToCaseWindow(m_cases[arychecked[i]].name); + + // Simulate current case + Case* c = SamApp::Window()->GetCurrentCase(); + Simulation& bcsim = c->BaseCase(); + bcsim.Clear(); + bool result = bcsim.Invoke(); + + // Get outputs and notices + double annual_energy = bcsim.GetOutput("annual_energy")->Value(); + matrix_t gen = bcsim.GetOutput("gen")->Matrix(); + //wxArrayString messages = c->BaseCase().GetAllMessages(); + + // Update UI + CaseWindow* cw = SamApp::Window()->GetCaseWindow(c); + cw->UpdateResults(); + + // Set array + int x = 1; + } EndModal(wxID_OK); } - else if (arychecked.Count() == 1) - { + else if (arychecked.Count() == 1) { wxMessageBox("Not enough cases selected.\n\nChoose at least two cases to combine.", "Combine Cases Message", wxOK, this); } - else - { + else { wxMessageBox("No cases selected.\n\nChoose at least two cases to combine.", "Combine Cases Message", wxOK, this); } } @@ -132,7 +153,7 @@ void CombineCasesDialog::RefreshList(size_t first_item) m_chlCases->Clear(); for (size_t i = 0; i < m_cases.size(); i++) { - int ndx = m_chlCases->Append(m_cases[i].display); + int ndx = m_chlCases->Append(m_cases[i].display_name); if (m_cases[i].is_selected) { m_chlCases->Check(ndx, true); } @@ -143,3 +164,12 @@ void CombineCasesDialog::RefreshList(size_t first_item) m_chlCases->Thaw(); m_chlCases->SetFirstItem(first_item); } + +void CombineCasesDialog::GetOpenCases() +{ + m_cases.clear(); + wxArrayString names = SamApp::Window()->Project().GetCaseNames(); + for (size_t i = 0; i < names.size(); i++) { + m_cases.push_back(CaseInfo(names[i], names[i])); + } +} \ No newline at end of file diff --git a/src/combinecases.h b/src/combinecases.h index bdefc04f38..8accd3e300 100644 --- a/src/combinecases.h +++ b/src/combinecases.h @@ -39,15 +39,12 @@ class CombineCasesDialog : public wxDialog struct CaseInfo { wxString name; // dataset - e.g. psm - wxString displayName; - wxString display; - wxString attributes; // limit column and file size to SAM specific per NSRDB + wxString display_name; bool is_visible; bool is_selected; - CaseInfo(wxString& _n, wxString& _dn, wxString& _a) - : name(_n), displayName(_dn), attributes(_a) + CaseInfo(wxString& _n, wxString& _dn) + : name(_n), display_name(_dn) { - display = name; is_visible = true; is_selected = false; } @@ -58,6 +55,8 @@ class CombineCasesDialog : public wxDialog void RefreshList(size_t first_item); + void GetOpenCases(); + std::vector m_cases; wxCheckListBox* m_chlCases; wxCheckBox* m_chkOverwriteCapital; From e57a41f2b2593fac8c177ebda9c67805f2b01bec Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Tue, 14 Sep 2021 14:37:27 -0600 Subject: [PATCH 03/26] Add FindActiveObject code that is currently failing --- src/combinecases.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 9d71b98bae..3c58937403 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -111,12 +111,29 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // Simulate each case for (size_t i = 0; i < arychecked.Count(); i++) { - // Switch to case to make current case + // Switch to case SamApp::Window()->SwitchToCaseWindow(m_cases[arychecked[i]].name); + Case* current_case = SamApp::Window()->GetCurrentCase(); + CaseWindow* case_window = SamApp::Window()->GetCaseWindow(current_case); + //cw->UpdateConfiguration(); + Simulation& bcsim = current_case->BaseCase(); + + // Grab inputs needed for combined case in generic model + double degradation = bcsim.GetInput("degradation")->Value(); // 'SchedNumeric', toggle 'UseSchedule' + ActiveInputPage* aip = 0; + wxUIObject* degradation_obj = case_window->FindActiveObject("degradation", &aip); + assert(degradation_obj && aip); + assert(degradation_obj->HasProperty("UseSchedule")); + bool use_schedule = degradation_obj->Property("UseSchedule").GetBoolean(); + + + // Set Annual AC degradation rate to the combine cases input value + // save whether Value or Sched, and save Value value + + + // Simulate current case - Case* c = SamApp::Window()->GetCurrentCase(); - Simulation& bcsim = c->BaseCase(); bcsim.Clear(); bool result = bcsim.Invoke(); @@ -126,10 +143,12 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) //wxArrayString messages = c->BaseCase().GetAllMessages(); // Update UI - CaseWindow* cw = SamApp::Window()->GetCaseWindow(c); - cw->UpdateResults(); + case_window->UpdateResults(); + //c->SetProperty + // Maybe just return the value instead of setting the table value directly? // Set array + degradation_obj->Property("UseSchedule").Set(true); int x = 1; } From c91b912bdf10518c68b5a98fa98b5d9ad3ccaa3c Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Wed, 15 Sep 2021 09:05:49 -0600 Subject: [PATCH 04/26] Mildly clean up code --- src/combinecases.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 3c58937403..d21c447f91 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -119,12 +119,15 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) Simulation& bcsim = current_case->BaseCase(); // Grab inputs needed for combined case in generic model - double degradation = bcsim.GetInput("degradation")->Value(); // 'SchedNumeric', toggle 'UseSchedule' + // -degradation value and + double degradation_orig = bcsim.GetInput("degradation")->Value(); // 'SchedNumeric', toggle 'UseSchedule' ActiveInputPage* aip = 0; + case_window->SwitchToInputPage("Lifetime and Degradation"); wxUIObject* degradation_obj = case_window->FindActiveObject("degradation", &aip); assert(degradation_obj && aip); assert(degradation_obj->HasProperty("UseSchedule")); - bool use_schedule = degradation_obj->Property("UseSchedule").GetBoolean(); + bool use_schedule_orig = degradation_obj->Property("UseSchedule").GetBoolean(); + // Set Annual AC degradation rate to the combine cases input value @@ -132,23 +135,23 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) - // Simulate current case bcsim.Clear(); bool result = bcsim.Invoke(); // Get outputs and notices - double annual_energy = bcsim.GetOutput("annual_energy")->Value(); - matrix_t gen = bcsim.GetOutput("gen")->Matrix(); + //double annual_energy = bcsim.GetOutput("annual_energy")->Value(); + //matrix_t gen = bcsim.GetOutput("gen")->Matrix(); //wxArrayString messages = c->BaseCase().GetAllMessages(); - // Update UI + // Reinstate original values + degradation_obj->Property("UseSchedule").Set(use_schedule_orig); + + // Update UI with results case_window->UpdateResults(); - //c->SetProperty // Maybe just return the value instead of setting the table value directly? // Set array - degradation_obj->Property("UseSchedule").Set(true); int x = 1; } From cf19136868c0ba3586a12992e6bf82bd7294b7c7 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Wed, 15 Sep 2021 11:10:18 -0600 Subject: [PATCH 05/26] Add FindObject UI function --- src/casewin.cpp | 15 +++++++++++++++ src/casewin.h | 3 ++- src/widgets.cpp | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/casewin.cpp b/src/casewin.cpp index 9f5eb99242..34109877d1 100644 --- a/src/casewin.cpp +++ b/src/casewin.cpp @@ -656,6 +656,21 @@ void CaseWindow::OnCommand( wxCommandEvent &evt ) } } +wxUIObject* CaseWindow::FindObject(const wxString& name, ActiveInputPage** ipage) +{ + auto aPage = GetInputPages(); + for (auto &page : aPage) + { + SwitchToInputPage(page); + if (wxUIObject* obj = FindActiveObject(name, ipage)) + return obj; + } + + if (ipage) *ipage = 0; + return 0; +} + + wxUIObject *CaseWindow::FindActiveObject( const wxString &name, ActiveInputPage **ipage ) { for( size_t i=0;iAdd( new wxStaticText( this, wxID_ANY, "Number of values:"), 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + tools->Add( new wxStaticText( this, wxID_ANY, "Number of values:"), 0, wxALL, 5 ); tools->Add( m_numVals = new wxNumericCtrl( this, ID_numValueCount, 50, wxNUMERIC_INTEGER ), 0, wxALL, 3 ); } From 97428ab6ec436f8f43d71ab6b1b56e824149c6ce Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Wed, 15 Sep 2021 11:11:16 -0600 Subject: [PATCH 06/26] Implement FindObject in combinecases --- src/combinecases.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index d21c447f91..434f0fd5c1 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -122,10 +122,11 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // -degradation value and double degradation_orig = bcsim.GetInput("degradation")->Value(); // 'SchedNumeric', toggle 'UseSchedule' ActiveInputPage* aip = 0; - case_window->SwitchToInputPage("Lifetime and Degradation"); - wxUIObject* degradation_obj = case_window->FindActiveObject("degradation", &aip); - assert(degradation_obj && aip); - assert(degradation_obj->HasProperty("UseSchedule")); + // ** this works only if page that contains the control is selected in the "current_case" ** + // case_window->SwitchToInputPage("Lifetime and Degradation"); + // wxUIObject* degradation_obj = case_window->FindActiveObject("degradation", &aip); + wxUIObject* degradation_obj = case_window->FindObject("degradation", &aip); + assert(degradation_obj && aip && degradation_obj->HasProperty("UseSchedule")); bool use_schedule_orig = degradation_obj->Property("UseSchedule").GetBoolean(); @@ -152,6 +153,11 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // Maybe just return the value instead of setting the table value directly? // Set array + //if (auto pvv = current_case->Values().Get("degradation")) { + // double v[] = { 1.0, 2.0, 3.0, 4.0 }; + // pvv->Set(v, 4); + // current_case->VariableChanged("degradation"); // triggers UI update + //} int x = 1; } From c2a3878440f595e086f219ff8dc2185d3311f94d Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Wed, 15 Sep 2021 12:19:12 -0600 Subject: [PATCH 07/26] Refine UI look of Power Plant page with new Select cases button --- deploy/runtime/ui/Generic System Plant.txt | 74 ++++++++++++---------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/deploy/runtime/ui/Generic System Plant.txt b/deploy/runtime/ui/Generic System Plant.txt index f85166ed8f..84406a08d2 100644 --- a/deploy/runtime/ui/Generic System Plant.txt +++ b/deploy/runtime/ui/Generic System Plant.txt @@ -1,6 +1,6 @@ Generic System Plant -788 -527 +787 +547 20 GroupBox @@ -21,7 +21,7 @@ Width 780 Height 3 -389 +410 Tool Tip 5 0 @@ -45,7 +45,7 @@ X 6 Y 3 -393 +414 Width 3 780 @@ -105,7 +105,7 @@ X 357 Y 3 -423 +444 Width 3 294 @@ -128,23 +128,23 @@ Name btn_selectcases X 3 -66 +411 Y 3 -93 +99 Width 3 -90 +143 Height 3 -24 +21 Tool Tip 5 0 Caption 5 15 -Select Cases... +Select cases... TabOrder 3 4294967295 @@ -161,7 +161,7 @@ X 432 Y 3 -297 +318 Width 3 120 @@ -223,7 +223,7 @@ X 453 Y 3 -363 +384 Width 3 100 @@ -285,7 +285,7 @@ X 453 Y 3 -207 +228 Width 3 100 @@ -347,7 +347,7 @@ X 411 Y 3 -102 +123 Width 3 143 @@ -382,7 +382,7 @@ X 432 Y 3 -249 +270 Width 3 120 @@ -444,7 +444,7 @@ X 432 Y 3 -273 +294 Width 3 120 @@ -506,7 +506,7 @@ X 453 Y 3 -339 +360 Width 3 100 @@ -568,7 +568,7 @@ X 48 Y 3 -129 +150 Width 3 636 @@ -615,7 +615,7 @@ X 48 Y 3 -321 +342 Width 3 600 @@ -654,7 +654,7 @@ X 48 Y 3 -228 +249 Width 3 600 @@ -693,7 +693,7 @@ X 48 Y 3 -189 +210 Width 3 600 @@ -732,7 +732,7 @@ X 21 Y 3 -423 +444 Width 3 243 @@ -783,10 +783,10 @@ Y 30 Width 3 -536 +413 Height 3 -44 +65 Tool Tip 5 0 @@ -795,9 +795,10 @@ Selection 0 Items 6 -2 +3 Constant generation profile from nameplate capacity and capacity factor Import hourly or subhourly generation profile from file +Generate production profiles from open cases ShowCaptions 2 1 @@ -820,7 +821,7 @@ X 453 Y 3 -78 +99 Width 3 100 @@ -882,7 +883,7 @@ X 453 Y 3 -102 +123 Width 3 100 @@ -1062,8 +1063,8 @@ spec_mode Generic system plant output specification mode Generic System Plant -127 -Constant generation profile from nameplate capacity and capacity factor|Import hourly or subhourly generation profile from file +172 +Constant generation profile from nameplate capacity and capacity factor|Import hourly or subhourly generation profile from file|Generate production profiles from open cases 1 1 1 @@ -1182,7 +1183,7 @@ equations{ 'ui_step_minutes' } = define() { } }; -1466 +1816 on_load{"Generic System Plant"}=define(){ on_change{'spec_mode'}(); on_change{'system_capacity'}(); @@ -1198,8 +1199,7 @@ on_change{'system_capacity'}=define(){ }; on_change{'btn_selectcases'} = define() { - if ( value('spec_mode') == 0 ) // change to 2 eventually - { + if ( value('spec_mode') == 2 ) { combine_cases(); } }; @@ -1225,13 +1225,19 @@ function combine_cases() function check_inputs() { mode = value('spec_mode'); + show('system_capacity', + mode == 0 || mode == 1); show('user_capacity_factor', mode == 0 ); show('energy_output_array', - mode == 1); + mode == 1 || mode == 2); + show('btn_selectcases', + mode == 2); color = 'black'; if ( mode == 1 ) - txt = 'Click "Edit data" to import generation data from a file. After importing the data, type a value for the nameplate capacity, which SAM uses to calculate the capacity factor and dollar amounts from any costs or incentives expressed $/W. Use the calculated values below to help you choose a nameplate capacity value. See Help for details.'; + txt = 'Click "Edit array..." to import generation data from a file. After importing the data, type a value for the nameplate capacity, which SAM uses to calculate the capacity factor and dollar amounts from any costs or incentives expressed $/W. Use the calculated values below to help you choose a nameplate capacity value. See Help for details.'; + elseif ( mode == 2 ) + txt = 'Click "Select cases..." to select open cases, simulate those cases and combine their generation profiles into a single profile to be used with this generic case. Click "Edit array..." to view or edit the combined simulated productions.'; else txt = ''; /* if ( value('system_capacity')*0.9 > value('first_year_output_peak' ) ) { From 01a4314bff3e6951329b818422f3b50bf8e353f4 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Wed, 15 Sep 2021 18:10:42 -0600 Subject: [PATCH 08/26] Refine combine cases dialog window --- src/combinecases.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 434f0fd5c1..c1ff8e639a 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -47,12 +47,12 @@ END_EVENT_TABLE() CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { - // Text at top of window - wxString msg = "Select open cases, simulate those cases and combine their generation profiles into a single profile to be used with this generic case."; + wxString msg = "Select open cases, simulate those cases and combine their generation\n"; + msg += "profiles into a single profile to be used with this generic case."; // Case selection - m_chlCases = new wxCheckListBox(this, ID_chlCases, wxDefaultPosition, wxSize(800, 200)); // populate with active cases + m_chlCases = new wxCheckListBox(this, ID_chlCases, wxDefaultPosition, wxSize(400, 200)); // populate with active cases this->GetOpenCases(); this->RefreshList(0.); wxBoxSizer* szcases = new wxBoxSizer(wxVERTICAL); @@ -63,14 +63,19 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) m_chkOverwriteCapital = new wxCheckBox(this, ID_chkOverwriteCapital, "Overwrite Capital Expenses"); // Annual AC degradation - m_spndDegradation = new wxSpinCtrlDouble(this, ID_spndDegradation, "Annual AC Degradation"); + m_spndDegradation = new wxSpinCtrlDouble(this, ID_spndDegradation, "Annual AC Degradation", wxDefaultPosition, wxSize(54, 22), + wxSP_ARROW_KEYS, 0, 100, 0.0, 0.1, "wxspndDegradation"); + wxString degradation_label = "%/year Annual AC degradation rate"; + wxBoxSizer* szdegradation = new wxBoxSizer(wxHORIZONTAL); + szdegradation->Add(m_spndDegradation, 0, wxALL, 1); + szdegradation->Add(new wxStaticText(this, wxID_ANY, degradation_label), 0, wxALL | wxALIGN_CENTER, 1); // Combine all into main vertical sizer wxBoxSizer* szmain = new wxBoxSizer(wxVERTICAL); szmain->Add(new wxStaticText(this, wxID_ANY, msg), 0, wxALL | wxEXPAND, 10); szmain->Add(szcases, 10, wxALL | wxEXPAND, 1); - szmain->Add(m_chkOverwriteCapital); - szmain->Add(m_spndDegradation); + szmain->Add(m_chkOverwriteCapital, 0, wxALL, 5); + szmain->Add(szdegradation, 0, wxALL, 1); szmain->Add(CreateButtonSizer(wxHELP | wxOK | wxCANCEL), 0, wxALL | wxEXPAND, 10); SetSizer(szmain); From e096b2cae84a4696f0040bf580868bd85e5d0661 Mon Sep 17 00:00:00 2001 From: Steven Janzou Date: Thu, 16 Sep 2021 04:06:31 -0600 Subject: [PATCH 09/26] Fix issue with wxUISchedNumeric always returning false for "UseSchedule" property --- src/uiobjects.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/uiobjects.cpp b/src/uiobjects.cpp index dab5127849..462c5e39ba 100644 --- a/src/uiobjects.cpp +++ b/src/uiobjects.cpp @@ -114,7 +114,8 @@ class wxUISchedNumericObject : public wxUIObject virtual void OnNativeEvent() { - /* nothing to do here ... */ + if (AFSchedNumeric* sn = GetNative()) + Property("UseSchedule").Set(sn->UseSchedule()); } }; From 89e59df9229df8b9699fc629933b2932781e9166 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Thu, 16 Sep 2021 08:40:59 -0600 Subject: [PATCH 10/26] Exclude generic case from ui case list --- src/combinecases.cpp | 19 ++++++++++++------- src/combinecases.h | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index c1ff8e639a..373837be62 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -51,7 +51,9 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) wxString msg = "Select open cases, simulate those cases and combine their generation\n"; msg += "profiles into a single profile to be used with this generic case."; - // Case selection + // Case selection list + Case* current_case = SamApp::Window()->GetCurrentCase(); + m_generic_case_name = SamApp::Window()->Project().GetCaseName(current_case); m_chlCases = new wxCheckListBox(this, ID_chlCases, wxDefaultPosition, wxSize(400, 200)); // populate with active cases this->GetOpenCases(); this->RefreshList(0.); @@ -186,12 +188,15 @@ void CombineCasesDialog::RefreshList(size_t first_item) m_chlCases->Clear(); for (size_t i = 0; i < m_cases.size(); i++) { - int ndx = m_chlCases->Append(m_cases[i].display_name); - if (m_cases[i].is_selected) { - m_chlCases->Check(ndx, true); - } - else { - m_chlCases->Check(ndx, false); + // Exclude generic case from displaying in case list + if (m_cases[i].display_name != m_generic_case_name) { + int ndx = m_chlCases->Append(m_cases[i].display_name); + if (m_cases[i].is_selected) { + m_chlCases->Check(ndx, true); + } + else { + m_chlCases->Check(ndx, false); + } } } m_chlCases->Thaw(); diff --git a/src/combinecases.h b/src/combinecases.h index 8accd3e300..bc0b2191e5 100644 --- a/src/combinecases.h +++ b/src/combinecases.h @@ -57,6 +57,7 @@ class CombineCasesDialog : public wxDialog void GetOpenCases(); + wxString m_generic_case_name; std::vector m_cases; wxCheckListBox* m_chlCases; wxCheckBox* m_chkOverwriteCapital; From 9f50118bdaaac99cdfb492d4063280ef4ddb1682 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Tue, 21 Sep 2021 21:36:17 -0600 Subject: [PATCH 11/26] Get most everything working minus carrying over costs --- deploy/runtime/ui/Generic System Plant.txt | 16 +- src/combinecases.cpp | 360 ++++++++++++++++++--- src/combinecases.h | 8 + src/invoke.cpp | 20 +- 4 files changed, 338 insertions(+), 66 deletions(-) diff --git a/deploy/runtime/ui/Generic System Plant.txt b/deploy/runtime/ui/Generic System Plant.txt index 84406a08d2..f8ca60c986 100644 --- a/deploy/runtime/ui/Generic System Plant.txt +++ b/deploy/runtime/ui/Generic System Plant.txt @@ -1183,7 +1183,7 @@ equations{ 'ui_step_minutes' } = define() { } }; -1816 +1738 on_load{"Generic System Plant"}=define(){ on_change{'spec_mode'}(); on_change{'system_capacity'}(); @@ -1207,19 +1207,15 @@ on_change{'btn_selectcases'} = define() { // display window for combine cases function combine_cases() { - // msgbox(list_cases()); // crashes SAM hash = combinecasesquery(); // meta data if ( hash != '' ) { - x=1; - //fn = hash{'file'}; - //dn = hash{'folder'}; - //af = hash{'addfolder'}; - //if ( fn != '' ) - //{ - // x = 1; - //} + result = hash{'result_code'}; // 0=success, 1=error + if ( result == 0 ) + { + x=1; + } } } diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 373837be62..945183bf5b 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -47,13 +47,19 @@ END_EVENT_TABLE() CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { + // Initializations + m_result_code = -1; + // Text at top of window wxString msg = "Select open cases, simulate those cases and combine their generation\n"; - msg += "profiles into a single profile to be used with this generic case."; + msg += "profiles into a single profile to be used with this generic case.\n\n"; + msg += "SAM will switch to each case in the project and run a simulation.\n"; + msg += "Depending on the configuration, SAM may appear to be temporarily unresponsive."; // Case selection list - Case* current_case = SamApp::Window()->GetCurrentCase(); - m_generic_case_name = SamApp::Window()->Project().GetCaseName(current_case); + m_generic_case = SamApp::Window()->GetCurrentCase(); + m_generic_case_name = SamApp::Window()->Project().GetCaseName(m_generic_case); + m_generic_case_window = SamApp::Window()->GetCaseWindow(m_generic_case); m_chlCases = new wxCheckListBox(this, ID_chlCases, wxDefaultPosition, wxSize(400, 200)); // populate with active cases this->GetOpenCases(); this->RefreshList(0.); @@ -62,9 +68,15 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) szcases->Add(m_chlCases, 10, wxALL | wxEXPAND, 1); // Overwrite capital checkbox - m_chkOverwriteCapital = new wxCheckBox(this, ID_chkOverwriteCapital, "Overwrite Capital Expenses"); + m_chkOverwriteCapital = new wxCheckBox(this, ID_chkOverwriteCapital, "Overwrite capital expenses"); // Annual AC degradation + // Due to complexity of AC and DC degradation and lifetime and single year simulations, require user to provide an + // AC degradation rate for the combined project and ignore degradation rate inputs of individual system cases. + // TODO: maybe add note: 'The Year 1 hourly generation profile is used for each system. Please' + // '\n provide an annual AC degradation rate to use for the combined project,' + // '\n or enter a zero to ignore degradation.'; + // TODO: make this a SchedNumeric instead so a schedule could be used m_spndDegradation = new wxSpinCtrlDouble(this, ID_spndDegradation, "Annual AC Degradation", wxDefaultPosition, wxSize(54, 22), wxSP_ARROW_KEYS, 0, 100, 0.0, 0.1, "wxspndDegradation"); wxString degradation_label = "%/year Annual AC degradation rate"; @@ -94,10 +106,8 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) break; case ID_chlCases: { - for (size_t i = 0; i < m_cases.size(); i++) - { - if (m_cases[i].display_name == m_chlCases->GetString(e.GetInt())) - { + for (size_t i = 0; i < m_cases.size(); i++) { + if (m_cases[i].display_name == m_chlCases->GetString(e.GetInt())) { m_cases[i].is_selected = m_chlCases->IsChecked(e.GetInt()); } } @@ -105,76 +115,334 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) break; case wxID_OK: { + // See which cases are selected wxArrayInt arychecked; - // Find which cases are selected for (size_t i = 0; i < m_cases.size(); i++) { if (m_cases[i].is_selected) { arychecked.push_back((int)i); } } - if (arychecked.Count() >= 1) { + + if (arychecked.Count() >= 2) { + + // Get analysis period and inflation from generic case + // TODO: Move some of this to constructor? + wxString technology_name = m_generic_case->GetTechnology(); + wxString financial_name = m_generic_case->GetFinancing(); + double analysis_period = std::numeric_limits::quiet_NaN(); + double inflation = std::numeric_limits::quiet_NaN(); + if (financial_name == "LCOE Calculator") { + analysis_period = m_generic_case->Values().Get("c_lifetime")->Value(); + inflation = m_generic_case->Values().Get("c_inflation")->Value(); + } + else if (financial_name != "None") { + analysis_period = m_generic_case->Values().Get("analysis_period")->Value(); + inflation = m_generic_case->Values().Get("inflation_rate")->Value(); + } + + // Allocate and initialize variables to run through the cases to combine + double nameplate = 0.; + matrix_t hourly_energy(1, 8760, 0.); + double annual_energy = 0.; + double total_installed_cost = 0.; + std::vector om_fixed(analysis_period, 0.); + bool is_notices = false; + + // Get dialog inputs bool overwrite_capital = m_chkOverwriteCapital->IsChecked(); - double degradation = m_spndDegradation->GetValue(); + double degradation = m_spndDegradation->GetValue(); // TODO: change this to a SchedNumeric - // Simulate each case + // Run each simulation for (size_t i = 0; i < arychecked.Count(); i++) { // Switch to case SamApp::Window()->SwitchToCaseWindow(m_cases[arychecked[i]].name); Case* current_case = SamApp::Window()->GetCurrentCase(); CaseWindow* case_window = SamApp::Window()->GetCaseWindow(current_case); - //cw->UpdateConfiguration(); + //case_page_orig = ? Simulation& bcsim = current_case->BaseCase(); + wxString technology_name = current_case->GetTechnology(); + wxString financial_name = current_case->GetFinancing(); - // Grab inputs needed for combined case in generic model - // -degradation value and - double degradation_orig = bcsim.GetInput("degradation")->Value(); // 'SchedNumeric', toggle 'UseSchedule' + // Set degradation, saving original value and value/sched property + // TODO: grab more than just the numeric if it's a schedule + VarValue* degradation_vv = current_case->Values().Get("degradation"); + VarValue degradation_orig(*degradation_vv); + degradation_vv->Set(new double[1]{degradation}, 1); ActiveInputPage* aip = 0; - // ** this works only if page that contains the control is selected in the "current_case" ** // case_window->SwitchToInputPage("Lifetime and Degradation"); - // wxUIObject* degradation_obj = case_window->FindActiveObject("degradation", &aip); + // wxUIObject* degradation_obj = case_window->FindActiveObject("degradation", &aip); // works only if page containing control is active wxUIObject* degradation_obj = case_window->FindObject("degradation", &aip); assert(degradation_obj && aip && degradation_obj->HasProperty("UseSchedule")); - bool use_schedule_orig = degradation_obj->Property("UseSchedule").GetBoolean(); + bool degradation_orig_usesched = degradation_obj->Property("UseSchedule").GetBoolean(); + degradation_obj->Property("UseSchedule").Set(false); + // Deal with inflation + VarValue* inflation_vv = nullptr; + double inflation_orig = std::numeric_limits::quiet_NaN(); + if (financial_name != "None") { + if (financial_name == "LCOE Calculator") { + inflation_vv = current_case->Values().Get("c_inflation"); + } + else { + inflation_vv = current_case->Values().Get("inflation_rate"); + } + inflation_orig = inflation_vv->Value(); + inflation_vv->Set(inflation); + } + // Simulate + bcsim.Clear(); + bool ok = bcsim.Invoke(); - // Set Annual AC degradation rate to the combine cases input value - // save whether Value or Sched, and save Value value + // check that the case ran + if (!ok) { + m_result_code = 1; + m_generic_case_window->UpdateResults(); + case_window->SwitchToPage("results:notices"); + wxArrayString messages = current_case->BaseCase().GetAllMessages(); + wxMessageBox("Error in " + technology_name + "\n\n" + + technology_name + " returned the following error:\n\n + " + + messages.Last(), + "Combine Cases Message", wxOK, this); + EndModal(wxID_OK); + return; + } + // optionally display messages returned from the simulation + wxArrayString messages = current_case->BaseCase().GetAllMessages(); + if (!messages.IsEmpty()) { + is_notices = true; + } + // Add the performance parameters of this case + double nameplate_this = current_case->Values().Get("system_capacity")->Value(); + nameplate += nameplate_this; + matrix_t hourly_energy_this = bcsim.GetOutput("gen")->Matrix(); // TODO: what is returned here if it doesn't generate hourly data? - // Simulate current case - bcsim.Clear(); - bool result = bcsim.Invoke(); + // need annual energy + double annual_energy_this = std::numeric_limits::quiet_NaN(); + if (technology_name == "Geothermal Power") { + annual_energy_this = bcsim.GetOutput("first_year_output")->Value(); + } + else { + annual_energy_this = bcsim.GetOutput("annual_energy")->Value(); + } + annual_energy += annual_energy_this; + + // for lifetime simulation, truncate results to first 8760 values + double analysis_period_this = std::numeric_limits::quiet_NaN(); + if (financial_name == "LCOE Calculator") { + analysis_period_this = current_case->Values().Get("c_lifetime")->Value(); + } + else if (financial_name != "None") { + analysis_period_this = current_case->Values().Get("analysis_period")->Value(); + } + + // determine hourly generation profile of current case + bool constant_generation = false; + bool lifetime = false; + if (hourly_energy_this.ncells() <= 1) { + constant_generation = true; // TODO: Report this somewhere?: "Model does not generate hourly generation data. Calculating constant generation profile from annual energy." + } + else if (hourly_energy_this.ncells() == 8760 * analysis_period_this) { + lifetime = true; // TODO: Report this somewhere?: "Model runs the simulation over the analysis period. Only Year 1 data will be combined with other cases." + } + else if (hourly_energy_this > 8760 * analysis_period_this) { + m_result_code = 1; + m_generic_case_window->UpdateResults(); + SamApp::Window()->SwitchToCaseWindow(m_generic_case_name); + m_generic_case_window->SwitchToInputPage("Power Plant"); + wxMessageBox("Subhourly simulations unsupported\n\n" + "The subhourly simulation for case " + technology_name + " is not supported.", + "Combine Cases Message", wxOK, this); + return; + } + + for (int i = 0; i < 8760; i++) { + if (constant_generation) { + hourly_energy[i] += annual_energy_this / 8760; + } + else { + hourly_energy[i] += hourly_energy_this[i]; + } + } + + // Add the financial parameters of this case, if applicable + double total_installed_cost_this = std::numeric_limits::quiet_NaN(); + std::vector om_total_this(analysis_period, std::numeric_limits::quiet_NaN()); + if (financial_name == "LCOE Calculator") { + total_installed_cost_this = current_case->Values().Get("capital_cost")->Value(); + total_installed_cost += total_installed_cost_this; + double om_fixed_this = current_case->Values().Get("fixed_operating_cost")->Value(); + double om_variable = current_case->Values().Get("variable_operating_cost")->Value() * annual_energy; + std::fill(om_total_this.begin(), om_total_this.end(), om_fixed_this + om_variable); + for (int i = 0; i < analysis_period; i++) { + om_fixed[i] += om_total_this[i]; + } + } + else if (financial_name == "None" || financial_name == "Third Party") { + total_installed_cost_this = 0.; + std::fill(om_total_this.begin(), om_total_this.end(), 0.); + } + else if (financial_name != "None" && analysis_period > std::numeric_limits::epsilon()) { + total_installed_cost_this = current_case->Values().Get("total_installed_cost")->Value(); + total_installed_cost += total_installed_cost_this; + // O&M costs are taken from the cash flows of each system. All types of O&M costs + // are entered in as fixed O&M costs in the financial case. This accounts for several things: + // (a) the escalation of O&M costs + // (b) the O&M costs by capacity and by generation, appropriately weighted by the system size/generation, + // (c) allows for inclusion of fuel costs that are found in some technologies but not others (fuel costs) + // TODO: go through whole LK script and ensure all important comments are being carried over + matrix_t om_fixed_this = bcsim.GetOutput("cf_om_fixed_expense")->Matrix(); // O&M fixed + matrix_t om_capacity = bcsim.GetOutput("cf_om_capacity_expense")->Matrix(); // O&M capacity based + matrix_t om_production = bcsim.GetOutput("cf_om_production_expense")->Matrix(); // O&M production based + matrix_t cf_om_fuel = bcsim.GetOutput("cf_om_fuel_expense")->Matrix(); // O&M fuel + matrix_t cf_opt_fuel_1 = bcsim.GetOutput("cf_om_opt_fuel_1_expense")->Matrix(); // O&M biomass + matrix_t cf_opt_fuel_2 = bcsim.GetOutput("cf_om_opt_fuel_2_expense")->Matrix(); // O&M coal + + // Battery costs + matrix_t om_fixed_this_1(1, analysis_period + 1, 0.); + matrix_t om_capacity_1(1, analysis_period + 1, 0.); + matrix_t om_production_1(1, analysis_period + 1, 0.); + matrix_t cf_batt_repl(1, analysis_period + 1, 0.); + if (bcsim.GetOutput("cf_om_fixed1_expense")) { + om_fixed_this_1 = bcsim.GetOutput("cf_om_fixed1_expense")->Matrix(); // battery fixed + om_capacity_1 = bcsim.GetOutput("cf_om_capacity1_expense")->Matrix(); // battery capacity based + om_production_1 = bcsim.GetOutput("cf_om_production1_expense")->Matrix(); // battery production based + cf_batt_repl = bcsim.GetOutput("cf_battery_replacement_cost")->Matrix(); // battery replacement + } + + // Fuel cell costs + matrix_t om_fixed_this_2(1, analysis_period + 1, 0.); + matrix_t om_capacity_2(1, analysis_period + 1, 0.); + matrix_t om_production_2(1, analysis_period + 1, 0.); + matrix_t cf_fuelcell_repl(1, analysis_period + 1, 0.); + if (bcsim.GetOutput("cf_om_fixed2_expense")) { + om_fixed_this_2 = bcsim.GetOutput("cf_om_fixed2_expense")->Matrix(); // fuel cell fixed + om_capacity_2 = bcsim.GetOutput("cf_om_capacity2_expense")->Matrix(); // fuel cell capacity based + om_production_2 = bcsim.GetOutput("cf_om_production2_expense")->Matrix(); // fuel cell production based + cf_fuelcell_repl = bcsim.GetOutput("cf_fuelcell_replacement_cost")->Matrix(); // fuel cell replacement + } - // Get outputs and notices - //double annual_energy = bcsim.GetOutput("annual_energy")->Value(); - //matrix_t gen = bcsim.GetOutput("gen")->Matrix(); - //wxArrayString messages = c->BaseCase().GetAllMessages(); + //in cash flows, the first entry in the array is "Year 0", so must call j+1 in loop + for (int i = 0; i < analysis_period; i++) { + // for display in System Costs page note + om_total_this[i] = om_fixed_this[i + 1] + om_capacity[i + 1] + om_production[i + 1] + + om_fixed_this_1[i + 1] + om_capacity_1[i + 1] + om_production_1[i + 1] + + om_fixed_this_2[i + 1] + om_capacity_2[i + 1] + om_production_2[i + 1] + + cf_om_fuel[i + 1] + cf_opt_fuel_1[i + 1] + cf_opt_fuel_2[i + 1] + + cf_batt_repl[i + 1] + cf_fuelcell_repl[i + 1]; - // Reinstate original values - degradation_obj->Property("UseSchedule").Set(use_schedule_orig); + om_fixed[i] += om_total_this[i]; + } + } + + // put inflation rate back and reinstate other original values + if (financial_name != "None") { + inflation_vv->Set(inflation_orig); + } + degradation_obj->Property("UseSchedule").Set(degradation_orig_usesched); + degradation_vv->Set(new double[1]{degradation_orig.Value()}, 1); // Update UI with results case_window->UpdateResults(); - // Maybe just return the value instead of setting the table value directly? - - // Set array - //if (auto pvv = current_case->Values().Get("degradation")) { - // double v[] = { 1.0, 2.0, 3.0, 4.0 }; - // pvv->Set(v, 4); - // current_case->VariableChanged("degradation"); // triggers UI update - //} - int x = 1; + //case_window->SwitchToInputPage("case_page_orig"); // TODO: switch to original page (how to get current page?) + case_window->SwitchToPage("results"); + } + + //For user benefit, change the fixed O&M schedule back to a single value if all entries are the same + bool constant1 = true; + if (financial_name != "None") { + for (int i = 1; i < analysis_period; i++) { //don't start at zero because comparing j-1 + if (om_fixed[i] != om_fixed[i - 1]) { + constant1 = false; + } + } + if (constant1) { + om_fixed.resize(1); + } + } + + // Set the generic system performance parameters + m_generic_case->Values().Get("system_capacity")->Set(nameplate); + m_generic_case->Values().Get("spec_mode")->Set(2); // specify the third radio button + //m_generic_case->Values().Get("derate")->Set(0); + //m_generic_case->Values().Get("heat_rate")->Set(0); + m_generic_case->Values().Get("energy_output_array")->Set(hourly_energy.data(), hourly_energy.ncells()); + m_generic_case->VariableChanged("energy_output_array"); // triggers UI update + + if (financial_name == "LCOE Calculator") { + if (!constant1) { + m_result_code = 1; + m_generic_case_window->UpdateResults(); + SamApp::Window()->SwitchToCaseWindow(m_generic_case_name); + m_generic_case_window->SwitchToInputPage("Power Plant"); + wxMessageBox("LCOE calculator error\n\n" + "Single annualized fixed operating costs must be used.\n\n" + "Check O&M inputs in case " + technology_name, + "Combine Cases Message", wxOK, this); + return; + } + else { + m_generic_case->Values().Get("fixed_operating_cost")->Set(om_fixed); + } + } + + if (financial_name != "None") { + //set cost parameters + m_generic_case->Values().Get("fixed_plant_input")->Set(total_installed_cost); + m_generic_case->Values().Get("genericsys.cost.per_watt")->Set(0); + m_generic_case->Values().Get("genericsys.cost.contingency_percent")->Set(0); + m_generic_case->Values().Get("genericsys.cost.epc.percent")->Set(0); + m_generic_case->Values().Get("genericsys.cost.epc.fixed")->Set(0); + m_generic_case->Values().Get("genericsys.cost.plm.percent")->Set(0); + m_generic_case->Values().Get("genericsys.cost.plm.fixed")->Set(0); + m_generic_case->Values().Get("genericsys.cost.sales_tax.percent")->Set(0); + + //set O&M parameters- all are zero except for fixed (see explanation above) + m_generic_case->Values().Get("om_fixed")->Set(om_fixed); + m_generic_case->Values().Get("om_capacity")->Set(new double[1]{0}, 1); + m_generic_case->Values().Get("om_production")->Set(new double[1]{0}, 1); + //O&M escalation rates are also zeroed because they are accounted for in the fixed O&M costs + m_generic_case->Values().Get("om_fixed_escal")->Set(0); + m_generic_case->Values().Get("om_capacity_escal")->Set(0); + m_generic_case->Values().Get("om_production_escal")->Set(0); + + if (m_generic_case->Values().Get("om_fuel_cost")) { + m_generic_case->Values().Get("om_fuel_cost")->Set(new double[1]{0}, 1); + m_generic_case->Values().Get("om_fuel_cost_escal")->Set(0); + } + + if (m_generic_case->Values().Get("om_replacement_cost1")) { + m_generic_case->Values().Get("om_replacement_cost1")->Set(new double[1]{0}, 1); + m_generic_case->Values().Get("om_replacement_cost_escal")->Set(0); + } } + // Update UI with results + m_result_code = 0; // 0=success + m_generic_case_window->UpdateResults(); + SamApp::Window()->SwitchToCaseWindow(m_generic_case_name); + m_generic_case_window->SwitchToInputPage("Power Plant"); + ActiveInputPage* aip = 0; + //wxUIObject* energy_output_array = m_generic_case_window->FindActiveObject("energy_output_array", &aip); // works only if page containing control is active + if (is_notices) { + wxMessageBox("Notices\n\n" + "At least one of the models generated notices.\n\n" + "View these messages or warnings on the Notices pane of the Results page.", + "Combine Cases Message", wxOK, this); + } EndModal(wxID_OK); } else if (arychecked.Count() == 1) { - wxMessageBox("Not enough cases selected.\n\nChoose at least two cases to combine.", "Combine Cases Message", wxOK, this); + wxMessageBox("Not enough cases selected.\n\n" + "Choose at least two cases to combine.", + "Combine Cases Message", wxOK, this); } else { - wxMessageBox("No cases selected.\n\nChoose at least two cases to combine.", "Combine Cases Message", wxOK, this); + wxMessageBox("No cases selected.\n\n" + "Choose at least two cases to combine.", + "Combine Cases Message", wxOK, this); } } break; @@ -189,6 +457,7 @@ void CombineCasesDialog::RefreshList(size_t first_item) for (size_t i = 0; i < m_cases.size(); i++) { // Exclude generic case from displaying in case list + // TODO: If excluding geothermal, do here. if (m_cases[i].display_name != m_generic_case_name) { int ndx = m_chlCases->Append(m_cases[i].display_name); if (m_cases[i].is_selected) { @@ -210,4 +479,11 @@ void CombineCasesDialog::GetOpenCases() for (size_t i = 0; i < names.size(); i++) { m_cases.push_back(CaseInfo(names[i], names[i])); } -} \ No newline at end of file +} + +std::vector CombineCasesDialog::matrix_to_vector(matrix_t &matrix) +{ + std::vector vector; + std::copy(matrix.data(), matrix.data() + matrix.ncells(), back_inserter(vector)); + return vector; +} diff --git a/src/combinecases.h b/src/combinecases.h index bc0b2191e5..8ad28e7cac 100644 --- a/src/combinecases.h +++ b/src/combinecases.h @@ -35,6 +35,9 @@ class CombineCasesDialog : public wxDialog public: CombineCasesDialog(wxWindow* parent, const wxString& title); + int GetResultCode() { + return m_result_code; + }; struct CaseInfo { @@ -57,7 +60,12 @@ class CombineCasesDialog : public wxDialog void GetOpenCases(); + std::vector matrix_to_vector(matrix_t& matrix); + + int m_result_code; + Case* m_generic_case; wxString m_generic_case_name; + CaseWindow* m_generic_case_window; std::vector m_cases; wxCheckListBox* m_chlCases; wxCheckBox* m_chkOverwriteCapital; diff --git a/src/invoke.cpp b/src/invoke.cpp index faeab4be0f..6eab395aa3 100644 --- a/src/invoke.cpp +++ b/src/invoke.cpp @@ -2221,20 +2221,12 @@ void fcall_combinecasesquery(lk::invoke_t& cxt) return; } - // Just for testing: - cxt.result().assign(wxEmptyString); - return; - ////Get selected filename - //wxString foldername = dlgNSRDB.GetWeatherFolder(); - //wxString filename = dlgNSRDB.GetWeatherFile(); - //wxString addfolder = dlgNSRDB.GetAddFolder(); - - //cxt.result().empty_hash(); - - //// meta data - //cxt.result().hash_item("file").assign(filename); - //cxt.result().hash_item("folder").assign(foldername); - //cxt.result().hash_item("addfolder").assign(addfolder); + int result = dlgCombineCases.GetResultCode(); // 0 = success, 1 = error + + cxt.result().empty_hash(); + + // meta data + cxt.result().hash_item("result_code").assign(result); } void fcall_wavetoolkit(lk::invoke_t& cxt) From 80f8c0138c5617a68b4734a78dc0df8193589d24 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Wed, 22 Sep 2021 17:06:11 -0600 Subject: [PATCH 12/26] Add non-working start of GetInputPage function --- src/casewin.cpp | 8 ++++++++ src/casewin.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/casewin.cpp b/src/casewin.cpp index 34109877d1..3ae3946bea 100644 --- a/src/casewin.cpp +++ b/src/casewin.cpp @@ -829,6 +829,14 @@ void CaseWindow::DetachCurrentInputPage() m_currentActivePages.clear(); } +wxString CaseWindow::GetInputPage() +{ + wxArrayString input_pages = GetInputPages(); + wxString input_page = input_pages[m_pageFlipper->GetSelection()]; + // do checks + return input_page; +} + wxArrayString CaseWindow::GetInputPages() { wxArrayString list; diff --git a/src/casewin.h b/src/casewin.h index 64e56b33f7..aa8b361e15 100644 --- a/src/casewin.h +++ b/src/casewin.h @@ -64,6 +64,7 @@ class CaseWindow : public wxSplitterWindow, CaseEventListener void UpdateConfiguration(); bool SwitchToInputPage( const wxString &name ); + wxString GetInputPage(); wxArrayString GetInputPages(); wxUIObject* FindActiveObject(const wxString& name, ActiveInputPage** page = 0); wxUIObject* FindObject(const wxString& name, ActiveInputPage** page = 0); From 91856fef1ee8dee17a0a7f1f8537be8fa84162c7 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Wed, 22 Sep 2021 17:07:19 -0600 Subject: [PATCH 13/26] Open data array dialog window at end and other small changes --- src/combinecases.cpp | 47 +++++++++++++++++++++++++++++++++----------- src/combinecases.h | 3 ++- src/invoke.cpp | 2 +- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 945183bf5b..5185bd47b3 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -44,11 +44,20 @@ BEGIN_EVENT_TABLE( CombineCasesDialog, wxDialog ) EVT_BUTTON(wxID_HELP, CombineCasesDialog::OnEvt) END_EVENT_TABLE() -CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) +CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title, lk::invoke_t& cxt) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { // Initializations m_result_code = -1; + m_generic_case = SamApp::Window()->GetCurrentCase(); + m_generic_case_name = SamApp::Window()->Project().GetCaseName(m_generic_case); + m_generic_case_window = SamApp::Window()->GetCaseWindow(m_generic_case); + if (m_generic_case->Values().Get("system_use_lifetime_output")->Boolean()) { + m_generic_degradation = m_generic_case->Values().Get("generic_degradation")->Array(); + } + else { + m_generic_degradation = m_generic_case->Values().Get("degradation")->Array(); + } // Text at top of window wxString msg = "Select open cases, simulate those cases and combine their generation\n"; @@ -57,9 +66,6 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) msg += "Depending on the configuration, SAM may appear to be temporarily unresponsive."; // Case selection list - m_generic_case = SamApp::Window()->GetCurrentCase(); - m_generic_case_name = SamApp::Window()->Project().GetCaseName(m_generic_case); - m_generic_case_window = SamApp::Window()->GetCaseWindow(m_generic_case); m_chlCases = new wxCheckListBox(this, ID_chlCases, wxDefaultPosition, wxSize(400, 200)); // populate with active cases this->GetOpenCases(); this->RefreshList(0.); @@ -68,7 +74,7 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) szcases->Add(m_chlCases, 10, wxALL | wxEXPAND, 1); // Overwrite capital checkbox - m_chkOverwriteCapital = new wxCheckBox(this, ID_chkOverwriteCapital, "Overwrite capital expenses"); + m_chkOverwriteCapital = new wxCheckBox(this, ID_chkOverwriteCapital, "Overwrite Installation Costs with combined cases costs"); // Annual AC degradation // Due to complexity of AC and DC degradation and lifetime and single year simulations, require user to provide an @@ -78,7 +84,7 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title) // '\n or enter a zero to ignore degradation.'; // TODO: make this a SchedNumeric instead so a schedule could be used m_spndDegradation = new wxSpinCtrlDouble(this, ID_spndDegradation, "Annual AC Degradation", wxDefaultPosition, wxSize(54, 22), - wxSP_ARROW_KEYS, 0, 100, 0.0, 0.1, "wxspndDegradation"); + wxSP_ARROW_KEYS, 0, 100, m_generic_degradation[0], 0.1, "wxspndDegradation"); wxString degradation_label = "%/year Annual AC degradation rate"; wxBoxSizer* szdegradation = new wxBoxSizer(wxHORIZONTAL); szdegradation->Add(m_spndDegradation, 0, wxALL, 1); @@ -158,23 +164,32 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) SamApp::Window()->SwitchToCaseWindow(m_cases[arychecked[i]].name); Case* current_case = SamApp::Window()->GetCurrentCase(); CaseWindow* case_window = SamApp::Window()->GetCaseWindow(current_case); - //case_page_orig = ? + wxString case_page_orig = case_window->GetInputPage(); Simulation& bcsim = current_case->BaseCase(); wxString technology_name = current_case->GetTechnology(); wxString financial_name = current_case->GetFinancing(); // Set degradation, saving original value and value/sched property // TODO: grab more than just the numeric if it's a schedule - VarValue* degradation_vv = current_case->Values().Get("degradation"); - VarValue degradation_orig(*degradation_vv); - degradation_vv->Set(new double[1]{degradation}, 1); - ActiveInputPage* aip = 0; // case_window->SwitchToInputPage("Lifetime and Degradation"); // wxUIObject* degradation_obj = case_window->FindActiveObject("degradation", &aip); // works only if page containing control is active + ActiveInputPage* aip = 0; wxUIObject* degradation_obj = case_window->FindObject("degradation", &aip); assert(degradation_obj && aip && degradation_obj->HasProperty("UseSchedule")); bool degradation_orig_usesched = degradation_obj->Property("UseSchedule").GetBoolean(); - degradation_obj->Property("UseSchedule").Set(false); + if (degradation_orig_usesched) { + degradation_obj->Property("UseSchedule").Set(false); + //aip->Refresh(); // no effect + //wxWindow* win; + //if (win = degradation_obj->GetNative()); + // win->Refresh(); // no effect + //current_case->VariableChanged("degradation"); // no effect + //case_window->Refresh(); // no effect + } + VarValue* degradation_vv = current_case->Values().Get("degradation"); + std::vector degradation_orig_vec = degradation_vv->Array(); + VarValue degradation_orig(*degradation_vv); // redundant here + degradation_vv->Set(new double[1]{degradation}, 1); // Deal with inflation VarValue* inflation_vv = nullptr; @@ -433,6 +448,14 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) "Combine Cases Message", wxOK, this); } EndModal(wxID_OK); + + // open up data array dialog + wxUIObject* energy_output_array = m_generic_case_window->FindObject("energy_output_array", &aip); + // do assert for non-null + if (AFDataArrayButton* btn_energy_output_array = energy_output_array->GetNative()) { + // see inputpage.cpp line 421 + btn_energy_output_array->OnPressed(e); + } } else if (arychecked.Count() == 1) { wxMessageBox("Not enough cases selected.\n\n" diff --git a/src/combinecases.h b/src/combinecases.h index 8ad28e7cac..3f2c66fccb 100644 --- a/src/combinecases.h +++ b/src/combinecases.h @@ -34,7 +34,7 @@ class CombineCasesDialog : public wxDialog { public: - CombineCasesDialog(wxWindow* parent, const wxString& title); + CombineCasesDialog(wxWindow* parent, const wxString& title, lk::invoke_t& cxt); int GetResultCode() { return m_result_code; }; @@ -66,6 +66,7 @@ class CombineCasesDialog : public wxDialog Case* m_generic_case; wxString m_generic_case_name; CaseWindow* m_generic_case_window; + std::vector m_generic_degradation; std::vector m_cases; wxCheckListBox* m_chlCases; wxCheckBox* m_chkOverwriteCapital; diff --git a/src/invoke.cpp b/src/invoke.cpp index 6eab395aa3..6329e4d63b 100644 --- a/src/invoke.cpp +++ b/src/invoke.cpp @@ -2210,7 +2210,7 @@ void fcall_nsrdbquery(lk::invoke_t &cxt) void fcall_combinecasesquery(lk::invoke_t& cxt) { LK_DOC("combinecasesquery", "Creates the Combine Cases dialog box, lists all open cases, simulates selected cases and returns a combined generation profile", "(none) : string"); - CombineCasesDialog dlgCombineCases(SamApp::Window(), "Combine Cases"); + CombineCasesDialog dlgCombineCases(SamApp::Window(), "Combine Cases", cxt); dlgCombineCases.CenterOnParent(); int code = dlgCombineCases.ShowModal(); //shows the dialog and makes it so you can't interact with other parts until window is closed From bc918f44835c5041f2ef14e036f2586c4de3f8fe Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Thu, 23 Sep 2021 09:26:54 -0600 Subject: [PATCH 14/26] Add SwitchToInputPage capability --- src/casewin.cpp | 3 +-- src/combinecases.cpp | 12 +++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/casewin.cpp b/src/casewin.cpp index 3ae3946bea..5ab6af30d9 100644 --- a/src/casewin.cpp +++ b/src/casewin.cpp @@ -831,8 +831,7 @@ void CaseWindow::DetachCurrentInputPage() wxString CaseWindow::GetInputPage() { - wxArrayString input_pages = GetInputPages(); - wxString input_page = input_pages[m_pageFlipper->GetSelection()]; + wxString input_page = m_inputPageList->GetStringSelection(); // do checks return input_page; } diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 5185bd47b3..5514a6c5e4 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -179,12 +179,6 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) bool degradation_orig_usesched = degradation_obj->Property("UseSchedule").GetBoolean(); if (degradation_orig_usesched) { degradation_obj->Property("UseSchedule").Set(false); - //aip->Refresh(); // no effect - //wxWindow* win; - //if (win = degradation_obj->GetNative()); - // win->Refresh(); // no effect - //current_case->VariableChanged("degradation"); // no effect - //case_window->Refresh(); // no effect } VarValue* degradation_vv = current_case->Values().Get("degradation"); std::vector degradation_orig_vec = degradation_vv->Array(); @@ -361,7 +355,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // Update UI with results case_window->UpdateResults(); - //case_window->SwitchToInputPage("case_page_orig"); // TODO: switch to original page (how to get current page?) + case_window->SwitchToInputPage(case_page_orig); case_window->SwitchToPage("results"); } @@ -398,12 +392,12 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) "Combine Cases Message", wxOK, this); return; } - else { + else if (overwrite_capital) { m_generic_case->Values().Get("fixed_operating_cost")->Set(om_fixed); } } - if (financial_name != "None") { + if (financial_name != "None" && overwrite_capital) { //set cost parameters m_generic_case->Values().Get("fixed_plant_input")->Set(total_installed_cost); m_generic_case->Values().Get("genericsys.cost.per_watt")->Set(0); From a3b007829e20d404f9ae432a06aa96d2c2f0ae4f Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Thu, 23 Sep 2021 13:49:32 -0600 Subject: [PATCH 15/26] Clean up some odds and ends --- src/combinecases.cpp | 63 ++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 5514a6c5e4..b592186426 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -63,7 +63,7 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title, wxString msg = "Select open cases, simulate those cases and combine their generation\n"; msg += "profiles into a single profile to be used with this generic case.\n\n"; msg += "SAM will switch to each case in the project and run a simulation.\n"; - msg += "Depending on the configuration, SAM may appear to be temporarily unresponsive."; + msg += "Depending on the configuration, SAM may be temporarily unresponsive."; // Case selection list m_chlCases = new wxCheckListBox(this, ID_chlCases, wxDefaultPosition, wxSize(400, 200)); // populate with active cases @@ -74,18 +74,15 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title, szcases->Add(m_chlCases, 10, wxALL | wxEXPAND, 1); // Overwrite capital checkbox - m_chkOverwriteCapital = new wxCheckBox(this, ID_chkOverwriteCapital, "Overwrite Installation Costs with combined cases costs"); + m_chkOverwriteCapital = new wxCheckBox(this, ID_chkOverwriteCapital, "Overwrite Installation and Operating Costs with combined cases costs"); // Annual AC degradation // Due to complexity of AC and DC degradation and lifetime and single year simulations, require user to provide an // AC degradation rate for the combined project and ignore degradation rate inputs of individual system cases. - // TODO: maybe add note: 'The Year 1 hourly generation profile is used for each system. Please' - // '\n provide an annual AC degradation rate to use for the combined project,' - // '\n or enter a zero to ignore degradation.'; // TODO: make this a SchedNumeric instead so a schedule could be used m_spndDegradation = new wxSpinCtrlDouble(this, ID_spndDegradation, "Annual AC Degradation", wxDefaultPosition, wxSize(54, 22), wxSP_ARROW_KEYS, 0, 100, m_generic_degradation[0], 0.1, "wxspndDegradation"); - wxString degradation_label = "%/year Annual AC degradation rate"; + wxString degradation_label = "%/year Annual AC degradation rate for all cases"; wxBoxSizer* szdegradation = new wxBoxSizer(wxHORIZONTAL); szdegradation->Add(m_spndDegradation, 0, wxALL, 1); szdegradation->Add(new wxStaticText(this, wxID_ANY, degradation_label), 0, wxALL | wxALIGN_CENTER, 1); @@ -335,7 +332,6 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) //in cash flows, the first entry in the array is "Year 0", so must call j+1 in loop for (int i = 0; i < analysis_period; i++) { - // for display in System Costs page note om_total_this[i] = om_fixed_this[i + 1] + om_capacity[i + 1] + om_production[i + 1] + om_fixed_this_1[i + 1] + om_capacity_1[i + 1] + om_production_1[i + 1] + om_fixed_this_2[i + 1] + om_capacity_2[i + 1] + om_production_2[i + 1] @@ -375,8 +371,8 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // Set the generic system performance parameters m_generic_case->Values().Get("system_capacity")->Set(nameplate); m_generic_case->Values().Get("spec_mode")->Set(2); // specify the third radio button - //m_generic_case->Values().Get("derate")->Set(0); - //m_generic_case->Values().Get("heat_rate")->Set(0); + m_generic_case->Values().Get("derate")->Set(0); + m_generic_case->Values().Get("heat_rate")->Set(0); m_generic_case->Values().Get("energy_output_array")->Set(hourly_energy.data(), hourly_energy.ncells()); m_generic_case->VariableChanged("energy_output_array"); // triggers UI update @@ -397,44 +393,44 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) } } + // Set installation and operating costs if (financial_name != "None" && overwrite_capital) { - //set cost parameters + // Installation Costs m_generic_case->Values().Get("fixed_plant_input")->Set(total_installed_cost); - m_generic_case->Values().Get("genericsys.cost.per_watt")->Set(0); - m_generic_case->Values().Get("genericsys.cost.contingency_percent")->Set(0); - m_generic_case->Values().Get("genericsys.cost.epc.percent")->Set(0); - m_generic_case->Values().Get("genericsys.cost.epc.fixed")->Set(0); - m_generic_case->Values().Get("genericsys.cost.plm.percent")->Set(0); - m_generic_case->Values().Get("genericsys.cost.plm.fixed")->Set(0); - m_generic_case->Values().Get("genericsys.cost.sales_tax.percent")->Set(0); - - //set O&M parameters- all are zero except for fixed (see explanation above) + m_generic_case->Values().Get("genericsys.cost.per_watt")->Set(0.); + m_generic_case->Values().Get("genericsys.cost.contingency_percent")->Set(0.); + m_generic_case->Values().Get("genericsys.cost.epc.percent")->Set(0.); + m_generic_case->Values().Get("genericsys.cost.epc.fixed")->Set(0.); + m_generic_case->Values().Get("genericsys.cost.plm.percent")->Set(0.); + m_generic_case->Values().Get("genericsys.cost.plm.fixed")->Set(0.); + m_generic_case->Values().Get("genericsys.cost.sales_tax.percent")->Set(0.); + + // Operating Costs - all zero except fixed (see explanation above) m_generic_case->Values().Get("om_fixed")->Set(om_fixed); - m_generic_case->Values().Get("om_capacity")->Set(new double[1]{0}, 1); - m_generic_case->Values().Get("om_production")->Set(new double[1]{0}, 1); + m_generic_case->Values().Get("om_capacity")->Set(new double[1]{0.}, 1); + m_generic_case->Values().Get("om_production")->Set(new double[1]{0.}, 1); //O&M escalation rates are also zeroed because they are accounted for in the fixed O&M costs - m_generic_case->Values().Get("om_fixed_escal")->Set(0); - m_generic_case->Values().Get("om_capacity_escal")->Set(0); - m_generic_case->Values().Get("om_production_escal")->Set(0); + m_generic_case->Values().Get("om_fixed_escal")->Set(0.); + m_generic_case->Values().Get("om_capacity_escal")->Set(0.); + m_generic_case->Values().Get("om_production_escal")->Set(0.); if (m_generic_case->Values().Get("om_fuel_cost")) { - m_generic_case->Values().Get("om_fuel_cost")->Set(new double[1]{0}, 1); - m_generic_case->Values().Get("om_fuel_cost_escal")->Set(0); + m_generic_case->Values().Get("om_fuel_cost")->Set(new double[1]{0.}, 1); + m_generic_case->Values().Get("om_fuel_cost_escal")->Set(0.); } if (m_generic_case->Values().Get("om_replacement_cost1")) { - m_generic_case->Values().Get("om_replacement_cost1")->Set(new double[1]{0}, 1); - m_generic_case->Values().Get("om_replacement_cost_escal")->Set(0); + m_generic_case->Values().Get("om_replacement_cost1")->Set(new double[1]{0.}, 1); + m_generic_case->Values().Get("om_replacement_cost_escal")->Set(0.); } } // Update UI with results m_result_code = 0; // 0=success - m_generic_case_window->UpdateResults(); SamApp::Window()->SwitchToCaseWindow(m_generic_case_name); + int result = m_generic_case->RecalculateAll(); + m_generic_case_window->UpdateResults(); m_generic_case_window->SwitchToInputPage("Power Plant"); - ActiveInputPage* aip = 0; - //wxUIObject* energy_output_array = m_generic_case_window->FindActiveObject("energy_output_array", &aip); // works only if page containing control is active if (is_notices) { wxMessageBox("Notices\n\n" "At least one of the models generated notices.\n\n" @@ -443,11 +439,10 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) } EndModal(wxID_OK); - // open up data array dialog + // 'Press' Edit array... button to show energy output array + ActiveInputPage* aip = 0; wxUIObject* energy_output_array = m_generic_case_window->FindObject("energy_output_array", &aip); - // do assert for non-null if (AFDataArrayButton* btn_energy_output_array = energy_output_array->GetNative()) { - // see inputpage.cpp line 421 btn_energy_output_array->OnPressed(e); } } From 3877290109aa4a669759c0d6d488b02b239268bc Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Thu, 23 Sep 2021 15:51:02 -0600 Subject: [PATCH 16/26] Change combine cases degradation widget to a SchedNumeric --- src/combinecases.cpp | 55 +++++++++++++++++++++----------------------- src/combinecases.h | 1 + 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index b592186426..413df7c9e6 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -79,12 +79,18 @@ CombineCasesDialog::CombineCasesDialog(wxWindow* parent, const wxString& title, // Annual AC degradation // Due to complexity of AC and DC degradation and lifetime and single year simulations, require user to provide an // AC degradation rate for the combined project and ignore degradation rate inputs of individual system cases. - // TODO: make this a SchedNumeric instead so a schedule could be used - m_spndDegradation = new wxSpinCtrlDouble(this, ID_spndDegradation, "Annual AC Degradation", wxDefaultPosition, wxSize(54, 22), - wxSP_ARROW_KEYS, 0, 100, m_generic_degradation[0], 0.1, "wxspndDegradation"); + m_schnDegradation = new AFSchedNumeric(this, ID_spndDegradation, wxDefaultPosition, wxSize(64, 22)); + if (m_generic_degradation.size() == 1) { + m_schnDegradation->UseSchedule(false); + m_schnDegradation->SetValue(m_generic_degradation[0]); + } + else { + m_schnDegradation->UseSchedule(true); + m_schnDegradation->SetSchedule(m_generic_degradation); + } wxString degradation_label = "%/year Annual AC degradation rate for all cases"; wxBoxSizer* szdegradation = new wxBoxSizer(wxHORIZONTAL); - szdegradation->Add(m_spndDegradation, 0, wxALL, 1); + szdegradation->Add(m_schnDegradation, 0, wxLEFT, 2); szdegradation->Add(new wxStaticText(this, wxID_ANY, degradation_label), 0, wxALL | wxALIGN_CENTER, 1); // Combine all into main vertical sizer @@ -151,10 +157,6 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) std::vector om_fixed(analysis_period, 0.); bool is_notices = false; - // Get dialog inputs - bool overwrite_capital = m_chkOverwriteCapital->IsChecked(); - double degradation = m_spndDegradation->GetValue(); // TODO: change this to a SchedNumeric - // Run each simulation for (size_t i = 0; i < arychecked.Count(); i++) { // Switch to case @@ -166,21 +168,15 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) wxString technology_name = current_case->GetTechnology(); wxString financial_name = current_case->GetFinancing(); - // Set degradation, saving original value and value/sched property - // TODO: grab more than just the numeric if it's a schedule - // case_window->SwitchToInputPage("Lifetime and Degradation"); - // wxUIObject* degradation_obj = case_window->FindActiveObject("degradation", &aip); // works only if page containing control is active - ActiveInputPage* aip = 0; - wxUIObject* degradation_obj = case_window->FindObject("degradation", &aip); - assert(degradation_obj && aip && degradation_obj->HasProperty("UseSchedule")); - bool degradation_orig_usesched = degradation_obj->Property("UseSchedule").GetBoolean(); - if (degradation_orig_usesched) { - degradation_obj->Property("UseSchedule").Set(false); - } + // Set degradation, saving original value VarValue* degradation_vv = current_case->Values().Get("degradation"); - std::vector degradation_orig_vec = degradation_vv->Array(); - VarValue degradation_orig(*degradation_vv); // redundant here - degradation_vv->Set(new double[1]{degradation}, 1); + std::vector degradation_orig = degradation_vv->Array(); + if (m_schnDegradation->UseSchedule()) { + degradation_vv->Set(m_schnDegradation->GetSchedule()); + } + else { + degradation_vv->Set(new double[1]{m_schnDegradation->GetValue()}, 1); + } // Deal with inflation VarValue* inflation_vv = nullptr; @@ -225,7 +221,9 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) nameplate += nameplate_this; matrix_t hourly_energy_this = bcsim.GetOutput("gen")->Matrix(); // TODO: what is returned here if it doesn't generate hourly data? - // need annual energy + // need annual energy to calculate variable O&M cost for LCOE calculator + // and for constant generation profile for Marine Energy + // Note that Wind with Weibull distribution as input reports gen calculated as annual_energy / 8760 double annual_energy_this = std::numeric_limits::quiet_NaN(); if (technology_name == "Geothermal Power") { annual_energy_this = bcsim.GetOutput("first_year_output")->Value(); @@ -298,7 +296,6 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // (a) the escalation of O&M costs // (b) the O&M costs by capacity and by generation, appropriately weighted by the system size/generation, // (c) allows for inclusion of fuel costs that are found in some technologies but not others (fuel costs) - // TODO: go through whole LK script and ensure all important comments are being carried over matrix_t om_fixed_this = bcsim.GetOutput("cf_om_fixed_expense")->Matrix(); // O&M fixed matrix_t om_capacity = bcsim.GetOutput("cf_om_capacity_expense")->Matrix(); // O&M capacity based matrix_t om_production = bcsim.GetOutput("cf_om_production_expense")->Matrix(); // O&M production based @@ -342,12 +339,11 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) } } - // put inflation rate back and reinstate other original values + // put inflation rate and degradation back if (financial_name != "None") { inflation_vv->Set(inflation_orig); } - degradation_obj->Property("UseSchedule").Set(degradation_orig_usesched); - degradation_vv->Set(new double[1]{degradation_orig.Value()}, 1); + degradation_vv->Set(degradation_orig); // Update UI with results case_window->UpdateResults(); @@ -371,11 +367,12 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // Set the generic system performance parameters m_generic_case->Values().Get("system_capacity")->Set(nameplate); m_generic_case->Values().Get("spec_mode")->Set(2); // specify the third radio button - m_generic_case->Values().Get("derate")->Set(0); - m_generic_case->Values().Get("heat_rate")->Set(0); + m_generic_case->Values().Get("derate")->Set(0); // no additional losses- losses were computed in the individual models + m_generic_case->Values().Get("heat_rate")->Set(0); // no fuel costs- accounted for in O&M fuel costs from subsystem cash flows m_generic_case->Values().Get("energy_output_array")->Set(hourly_energy.data(), hourly_energy.ncells()); m_generic_case->VariableChanged("energy_output_array"); // triggers UI update + bool overwrite_capital = m_chkOverwriteCapital->IsChecked(); if (financial_name == "LCOE Calculator") { if (!constant1) { m_result_code = 1; diff --git a/src/combinecases.h b/src/combinecases.h index 3f2c66fccb..0004eb14f8 100644 --- a/src/combinecases.h +++ b/src/combinecases.h @@ -71,6 +71,7 @@ class CombineCasesDialog : public wxDialog wxCheckListBox* m_chlCases; wxCheckBox* m_chkOverwriteCapital; wxSpinCtrlDouble* m_spndDegradation; + AFSchedNumeric* m_schnDegradation; DECLARE_EVENT_TABLE() }; From 47620b0555862d0b9243dc56bec36b6699b1c669 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Fri, 24 Sep 2021 17:11:34 -0600 Subject: [PATCH 17/26] Restrict degradation queries to certain financial models --- src/combinecases.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 413df7c9e6..cbc5ffd4f7 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -169,13 +169,17 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) wxString financial_name = current_case->GetFinancing(); // Set degradation, saving original value - VarValue* degradation_vv = current_case->Values().Get("degradation"); - std::vector degradation_orig = degradation_vv->Array(); - if (m_schnDegradation->UseSchedule()) { - degradation_vv->Set(m_schnDegradation->GetSchedule()); - } - else { - degradation_vv->Set(new double[1]{m_schnDegradation->GetValue()}, 1); + VarValue* degradation_vv = nullptr; + std::vector degradation_orig = { std::numeric_limits::quiet_NaN() }; + if (financial_name != "None" && financial_name != "LCOE Calculator") { + degradation_vv = current_case->Values().Get("degradation"); + degradation_orig = degradation_vv->Array(); + if (m_schnDegradation->UseSchedule()) { + degradation_vv->Set(m_schnDegradation->GetSchedule()); + } + else { + degradation_vv->Set(new double[1]{ m_schnDegradation->GetValue() }, 1); + } } // Deal with inflation @@ -339,11 +343,13 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) } } - // put inflation rate and degradation back + // put degradation and inflation rate back + if (financial_name != "None" && financial_name != "LCOE Calculator") { + degradation_vv->Set(degradation_orig); + } if (financial_name != "None") { inflation_vv->Set(inflation_orig); } - degradation_vv->Set(degradation_orig); // Update UI with results case_window->UpdateResults(); From 71ee73c0ad8a23ee3743c8d3c8d0d134dc24fa45 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Fri, 24 Sep 2021 21:23:05 -0600 Subject: [PATCH 18/26] Fix constant generation case --- src/combinecases.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index cbc5ffd4f7..4cb1e5f32c 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -223,7 +223,10 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // Add the performance parameters of this case double nameplate_this = current_case->Values().Get("system_capacity")->Value(); nameplate += nameplate_this; - matrix_t hourly_energy_this = bcsim.GetOutput("gen")->Matrix(); // TODO: what is returned here if it doesn't generate hourly data? + matrix_t hourly_energy_this(1, 1, std::numeric_limits::quiet_NaN()); + if (bcsim.GetOutput("gen")) { + hourly_energy_this = bcsim.GetOutput("gen")->Matrix(); + } // need annual energy to calculate variable O&M cost for LCOE calculator // and for constant generation profile for Marine Energy From 9060bc1a5a9ea82401308131ea5494214339448b Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Fri, 24 Sep 2021 21:55:53 -0600 Subject: [PATCH 19/26] Fix battery replacement cost --- src/combinecases.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 4cb1e5f32c..1a645e3738 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -319,6 +319,8 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) om_fixed_this_1 = bcsim.GetOutput("cf_om_fixed1_expense")->Matrix(); // battery fixed om_capacity_1 = bcsim.GetOutput("cf_om_capacity1_expense")->Matrix(); // battery capacity based om_production_1 = bcsim.GetOutput("cf_om_production1_expense")->Matrix(); // battery production based + } + if (bcsim.GetOutput("cf_battery_replacement_cost")) { cf_batt_repl = bcsim.GetOutput("cf_battery_replacement_cost")->Matrix(); // battery replacement } From b673cc8d56b8ecde44e71114e22f5d2f74c1a8f7 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Fri, 24 Sep 2021 22:51:33 -0600 Subject: [PATCH 20/26] Fix lcoh handling --- src/combinecases.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 1a645e3738..7729a4b597 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -140,7 +140,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) wxString financial_name = m_generic_case->GetFinancing(); double analysis_period = std::numeric_limits::quiet_NaN(); double inflation = std::numeric_limits::quiet_NaN(); - if (financial_name == "LCOE Calculator") { + if (financial_name == "LCOE Calculator" || financial_name == "LCOH Calculator") { analysis_period = m_generic_case->Values().Get("c_lifetime")->Value(); inflation = m_generic_case->Values().Get("c_inflation")->Value(); } @@ -171,7 +171,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // Set degradation, saving original value VarValue* degradation_vv = nullptr; std::vector degradation_orig = { std::numeric_limits::quiet_NaN() }; - if (financial_name != "None" && financial_name != "LCOE Calculator") { + if (financial_name != "None" && financial_name != "LCOE Calculator" && financial_name != "LCOH Calculator") { degradation_vv = current_case->Values().Get("degradation"); degradation_orig = degradation_vv->Array(); if (m_schnDegradation->UseSchedule()) { @@ -186,7 +186,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) VarValue* inflation_vv = nullptr; double inflation_orig = std::numeric_limits::quiet_NaN(); if (financial_name != "None") { - if (financial_name == "LCOE Calculator") { + if (financial_name == "LCOE Calculator" || financial_name == "LCOH Calculator") { inflation_vv = current_case->Values().Get("c_inflation"); } else { @@ -242,7 +242,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // for lifetime simulation, truncate results to first 8760 values double analysis_period_this = std::numeric_limits::quiet_NaN(); - if (financial_name == "LCOE Calculator") { + if (financial_name == "LCOE Calculator" || financial_name == "LCOH Calculator") { analysis_period_this = current_case->Values().Get("c_lifetime")->Value(); } else if (financial_name != "None") { @@ -281,7 +281,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) // Add the financial parameters of this case, if applicable double total_installed_cost_this = std::numeric_limits::quiet_NaN(); std::vector om_total_this(analysis_period, std::numeric_limits::quiet_NaN()); - if (financial_name == "LCOE Calculator") { + if (financial_name == "LCOE Calculator" || financial_name == "LCOH Calculator") { total_installed_cost_this = current_case->Values().Get("capital_cost")->Value(); total_installed_cost += total_installed_cost_this; double om_fixed_this = current_case->Values().Get("fixed_operating_cost")->Value(); @@ -349,7 +349,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) } // put degradation and inflation rate back - if (financial_name != "None" && financial_name != "LCOE Calculator") { + if (financial_name != "None" && financial_name != "LCOE Calculator" && financial_name != "LCOH Calculator") { degradation_vv->Set(degradation_orig); } if (financial_name != "None") { @@ -384,7 +384,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) m_generic_case->VariableChanged("energy_output_array"); // triggers UI update bool overwrite_capital = m_chkOverwriteCapital->IsChecked(); - if (financial_name == "LCOE Calculator") { + if (financial_name == "LCOE Calculator" || financial_name == "LCOH Calculator") { if (!constant1) { m_result_code = 1; m_generic_case_window->UpdateResults(); From d8753dc9bd4fb34553f2643ff0e5dbc70eacf0f6 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Fri, 24 Sep 2021 23:03:13 -0600 Subject: [PATCH 21/26] Ensure notices messagebox stays on top --- src/combinecases.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 7729a4b597..b12db4b1c6 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -443,7 +443,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) wxMessageBox("Notices\n\n" "At least one of the models generated notices.\n\n" "View these messages or warnings on the Notices pane of the Results page.", - "Combine Cases Message", wxOK, this); + "Combine Cases Message", wxOK | wxSTAY_ON_TOP, this); } EndModal(wxID_OK); From de2cf58988a3b3335b6a787ac4907040e2d53221 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Fri, 24 Sep 2021 23:25:28 -0600 Subject: [PATCH 22/26] Remove combine cases macro --- deploy/runtime/macros/Combine Cases.lk | 564 ------------------------ deploy/runtime/macros/combine_cases.JPG | Bin 23697 -> 0 bytes 2 files changed, 564 deletions(-) delete mode 100644 deploy/runtime/macros/Combine Cases.lk delete mode 100644 deploy/runtime/macros/combine_cases.JPG diff --git a/deploy/runtime/macros/Combine Cases.lk b/deploy/runtime/macros/Combine Cases.lk deleted file mode 100644 index 228326d1d4..0000000000 --- a/deploy/runtime/macros/Combine Cases.lk +++ /dev/null @@ -1,564 +0,0 @@ -/*@ -

This macro adds the hourly power generation profiles of two or more cases and uses the resulting profile as input to a Generic System case that represents the combined project. You can use it to model a project's cash flow when the generation profile is from different sources.

-

To learn about cases, see the "Manage Cases" topic in Help under Reference in the table of contents.

-

Some examples of analysis scenarios this macro can model include:

-
    -
  • A project that combines two or more types of renewable energy systems (wind/PV, PV/CSP, PV/Wind/Biopower, etc.).
  • -
  • A system that uses different types or sizes of equipment, such as a PV system with different module or inverter types, or a wind farm with different sizes of turbines.
  • -
  • A PV system with more than four subarrays.
  • -
-

The following diagram shows an example of how the macro works to combine two cases. It works the same way for more than two cases:

-

-
Instructions:
-
    -
  1. Create a case for each system to combine. -
      -
    • If you want to specify costs for each system, include a financial model for each case.
    • -
    • The macro calculates a total project installation cost and operating cost by adding the costs of the individual cases and using those as the costs for the combined project case.
    • -
    • It uses the financial parameters, incentives, and other financial model inputs from the combined project case and ignores those inputs for the system cases.
    • -
    -
  2. -
  3. For each case, design the system and assign costs as appropriate. You may want to run a simulation for each case to make sure the results are as you expect.
  4. -
  5. Create a special Generic System case with the financial model for the combined project.
  6. -
  7. Specify inputs for the Generic System case's Financial Parameters, Incentives and other finanical model input pages. -
      -
    • The macro automatically populates inputs on the Generic System case's Power Plant and System Costs input pages, so you do can ignore the inputs on those pages.
    • -
    -
  8. Use the "Choose an option" list to the right to choose the cases to include in the combined project.
  9. -
      -
    • Combine all cases to combine all of the cases except the Generic System case you created for the combined project.
    • Combine only the cases listed below to choose the cases to combine by typing a comma-separated list of case names, including the Generic System for the combined project. (Separate each case name by a comma with no spaces.) -
    -
  10. Click Run macro above and follow the prompts.
  11. -
-

When the macro finishes, it displays the case for the combined project. Review the information in the page notes for the Power Plant and System Costs page, and then click Simulate to generate results for the combined project.

-

If you make changes to the combined project financial model inputs, you can run a simulation to generate new results. You only need to rerun this macro if you change the system design for any of the system cases.

-

This macro only uses power generation data for Year 1. It prompts you for a degradation rate to apply to the combined system output.

- -@*/ -//Macro user interface widgets -//@ name=mode;type=combo;label=Choose an option;value=1) Combine all cases,2) Combine only the cases listed below;sel=0 -//@ name=cases;type=text;label=List cases for Option 2 (comma-separated);value= - -//Check that this macro was run from within a case -if ( typeof(macro) == 'unknown' ) { - msgbox('This macro must be run from within a case.'); - exit; - /*macro = {}; - macro.mode = '1) Combine all cases'; - macro.cases = '';*/ -} - -function num_to_str( x , d ) -{ - // only format x if it is a number - if ( typeof(x) == 'number' ) - { - // The sprintf() function in this step converts a number (for d = 0) like 409.857 to 41 instead of 410. - // This if-else condition is a workaround that bug. - if( ( d == 0) && (mod( round(x) , 10) <= 0) ) - { - x = round(x); - str = sprintf('%,',x); - } - else - { str = sprintf('%.'+to_string(d)+',',x); } - - arr = split(str,'.'); - if ( #arr > 1 ) - { num_decs = strlen(to_string(arr[1])); } - else - { num_decs = 0; } - // if number has one or more decimal places less than the desired number - if ( #arr > 1 && num_decs < d) - { - for (i=0; i 0 ) - { - str += '.'; - for (i=0; i 1) //more than one generic case -{ - financial = choose_from_list(generic, 'Choose which generic system to use as the financial configuration for the combined system.', 'Select financial case', 0); -} -else //exactly one generic case -{ - financial = generic[0]; -} -outln('Case for combined project: ' + financial); - -//Verify the configuration before running it -msg = 'Review Configuration\nThis macro will combine these cases:\n'; -for (i=0; i 8760 * analysis_period_this) - { - outln('The ' + config[0] + ' model runs sub-hourly simulations, which this script does not support. Exiting script.'); - msgbox('Subhourly simulations not supported!\nThis macro is not currently enabled to work with sub-hourly simulations. Change the ' + cases[i] + ' case to hourly simulations modify the macro code to run sub-hourly.'); - exit; - } - - for (j=0; j<8760; j++) - { - if ( constant_generation ) - { - hourly_energy[j] += annual_energy_this / 8760; - } - else - { - hourly_energy[j] += hourly_energy_this[j]; - } - } - - //Add the financial parameters of this case, if applicable - if (config[1] == 'LCOE Calculator') //o&m is different for this one - { - total_installed_cost_this = get('capital_cost'); // to display on System Costs page note - total_installed_cost += get('capital_cost'); - om_fixed_this = get('fixed_operating_cost'); - om_variable = get('variable_operating_cost') * annual_energy; - for(j=0; j8-f2D5s-E^H@7pkXS*YBY;R?64gl!y zP=8|-#2|CXeuwtY``W0;-|kU`xB(z4>i7HK*#%b8PXzyF7ymcf(ce1%jlkas{Efih z2>gw}?+Ea4ar29Caf@*A-{IyJ;pZ3O<_G>&8URQG&VV`iwF8U+dw>meT*&`+X9Yfi ztOWFZ;L<*Gb#)Zs*Z|z)a!|wsh5qZuo)vn0uM*bL&U?*(azk}_>PC2 zt-XtghdAwT(nY}gpUIrG5@OC~79y&Tr2iua_$2b#t>a6R|M1;5RWd z<7YQ9<~3vI<~B2CH#XztW;f&IH526L7UHqsHlh8G>dj35S-Yc~v+Zwno0)Q&+nU>% z+q=4eXmE4Va{i;y|66r}5dE{#e+fURBbZ0zp|iQMtNA1FlK9!p>|EUJT>NVP+4USk zTtZ@;f5<1s`4fyQ4gKG3`F{y52{AKM5!0V-cX0kk^-s;6{%5^yYbEgqYW)vI!SsLJ zfHi>ThmDOb#(A~!|2G=HrGVB4miwozf+g?#fAIOcl79>2zv22Du73-Ge=GCf*7Y}B z{}uxOR_4F0>;GqP{mysI?Lm_34zgL~B=8WxL`TO!N5jNG$H2nG#KIxO1vv%|3Bir) zgttg;liwn_MMgo*c$b2Tfr{)FEe9+L(7yqqjNj4Z4yKZT%RVqxK6;}GNG z60=a=qGb8EKgdRa5DSGAMFbUv7Pv-;f=Y;jYz1h*vq^ODH1p?y=6`%puA!o#V_;%o zJzYYj|^EN0rD*Am)Y}|+Vgw(Y3jLfX;FFC~}rDf$6l~vWvEv;?s9p5^? z4-5_skBp9uPs}eYE-kODuB~tE9~>SXpPZhZU;NYy1wj3WTL0AS4|)-TdR;?9Lq)^< zsTayMckn_bL_@#JjY0H41=HB+CN0k^EMm#X&qYnxbiAs2Bqq-NIHdG^^9=hxRr^h| z|C(Zc|1Hh_so3v&O#s(XQNYeaB?KVAg>+TEL&t63$mb)DZS2PrK4)|7bSD<#cy=3c z(j79jH3sE-VrhtL=}16+{}P?Hhu&F7uEAsHiNehvik|U-JX#(Zc;oBKXR-_QQztUI zNC21mvf>T&z!Q3wpme5nI(x(cT{=Jl`}{vt3>Z(6S8)qGI7k#MPHHdqukqYkEkaa~TON*d(P!Y>R0g`eH#!xTYIOU7^3ceue}BB7Bg5 zQGHa}(UHc`ig4jC|bX-jj@!!vv2xg}S zANSHTrTRJYcn||BdgdJ=W#Z2-bZM@Qx46D5?<`Y@rJWJjA;#B(LsWX6#wk?tRNFKV zE(NdAB(rMMmSg#FkCds+YMM&;c0i>Z9jzu7cp(9`PWUV^i6d<>xVeQeYHlT zI$0;2Z*9Y)^U%LH_f(6%g0D?B3>J=JL}#Argekg-crm4xurZ|u1K&n#6|>H~=r2u0 zdl)ZrJpxyuEniRhdjwlUQad{L4vU|2KF@)&XI8;A<_lrkIaA*iZWZ7aTb`*4HNrfz zb61LlKbotwCkN5U1$HruSgZ5ft|5VW(^+U=6%vTO) zR3d@R`iVgQJ3W&2haQuGv3c)akU@U4!BwWTf5;}->K1fp`$%*3CFGY@jS5TAOn4No zz-~Y-jB9+^{@CiF$QN>MwzJQkw$VHfbbF7`BI#C9wD^#?dxpLI!N%?^IX`0~)~hYp zz-kctvptUxi?^TX?Fs{zIqU=i5`*#2w9MO5O$JT=0lk|BVTN)6h1=`QDZ;^NTP&62 z$@EAd@~H+AC}eIpLN!n~M*`HONZ_~=8sk+M1X)rWZrJ?p00%~6w4ogV4RB|}Wdi}H z{YNJ}(7;DWDmq^DgmU8DhpFGo%XhiL0v~eCSx(-om3MTBW8NN0c&!&7FQuEPR$m!5 zu&^Sha*_7&yTr>q$aVEZR#6HhAb=&&S3=KDE8AV8mHh)}F&yk8>ll8)XFU2c=$?g} zdpv9Tj|M*}QnQY$E*LW#?P60HH8oCgcZz!jM~%w-NI~x|kE<~7rex2)494eqG8pBJ zFQT3&HdzsaIMDZCfQsHaQqO<#ZGGbXNJfRHnQ>95ACJ(*?XpkP4E0%92Sjpa@g!!x zFN%^K#eFW-np60to;;Zt`=SP)Ee)@GT+K3*j|A@K&R(2Fg9DRawb=ScTic-&S3yUk zPG*YTTnJVHXMSqri|(}U9GTr)NB1f>ItT5HqLqTugUG|3Wl#CHmXJVuT-JuidB7<8C`BB7RgIl!@vip4v@1R{g-|?(u|-k_=IUB_$i`_^kDlYK;`U@D!XDhid_kRQjM#cV}ub+Z{VNTiG4I1&<|}(!nFO z%P+!n*{eluL}Inf7Pdi669{X^8KSf;q8`S1>XmSx-m7~dL8_M3J>4r%Ps>mDJ#q?0 z5oWqCkyn(ha-udzaFW{(dpch!CYSyJ_ZH_}3A>Dey*_!78$P_FFed{P@&xP&8-k+NaCx0uiG(DcPUHXRYqLMwj9Fv_jy`XNt+Eqmc5W z!VBziBrqq51j1-0y)@xyTS#E5>ccJ?ZrX@y9s_FZ0j6Fxt$cO zY1X_v#Yuj+ZWt+Qd>J@#ENOtA5b%6mb93GX36L6OP4yv&eXa#7^W4wRH`wsuR2I1H zI-o0MpOf|7BvZPPA(2XvsY$4+bN%bGKzeFy;Smye!_AZ{$6iv>C- z|J=yOM&=Tqb2}(MW+{r_zc&a2E$aF;vdUhGH(BydY0SAD7l=G&jZg_XqTr#@L84RT zSfPa~*}2f#q8qpG%b?4k(x(_lJQ3!{W8J#2bq_)BDK|SvU`?)Se}IWfx0kiW*x5UH zA$hX5qOxJ$uN185XC5nt`ShK8}>%~N*1 zowIepm*f9x&Z>CZ3Xy|7b*g<`EDC-%Z>y#6OaJQ;bBBDqp#B5x8(fw(e~!~ zq^x+#$WrHfPE|@O$-}C_dy9;i-4V_zs(N`HhR2EBTlO-?RO!=l!edagL zL9HIO_mEzi``IU~h3|{uo~vVSy`xE~G*f8t=Dw|$`fg)*y0R~RH*@s zP9>qv-X}7*_*y(|+_N4ycv%E(hhWXntrI0)YwJoZgh9?D z;<^rXzg?&6?W$cu2<3{{mXDl9_PWvMF3T3=QW{qntww2?9_P4fI3r3-v;{d~EJ;3w&A zsj}O?_rWHrA~y8wQsnSeip~*^SrJ=bt*UcDhVGipdPIU)T=N9_Y_L3eS4&n$>!B-8 z$`0o-f5VsclM8}yyG5x>j<^D=)yHgU%i+c!Q-Uy2hgND5b+v5sP0P!IOal2Q-ln}| ziEa2i;SnO8_@o}wEGf4Tj{-lGAe-MYj&&%WH6I_(7iWStK>zt3N}s2VfoINo!`KY{ zkZY6UDYgxhRAeI>?9<7`Xr7iw!D;iry_m%ebM=NzZ4X0d+3Nr(!>f=J0e| zlj(-|!!V!rXO+~x6S?WQb$i` zQmWvgaocKAG+gJNY@+0%?I5-$B$U4Pk_*V*VawlH3fQ65^q`cxNu52DTFJm+rMDGa zT+{|~-12CIe=#_diTs?&_VPjE{b{S=48LJff0~4Wn%&Osgtbn3=$jdrK^OG_cMT?a z$Gisfy<4Mh?>rzAyfiSc*m}~2ee(^U9)fw9K33I@ovx>Dm9}VBI2Ck+OJZb4G!X`O>g; zd-j6gW+1oc1s)Qh8=5`AbAaD2TUS3Gx4bNtP~brVo~Gtu76A+I%&FwR9%IzLQ5h(J zaf|92w9!ql($KDJIu~CEh}=HGVYSsDy7eK0{We$g!fIf&(=cxtuGJ;N@*d?-_O6Nozy&zL?CVNe99k2`q*oh@rMxNT3%Ab82X=s}6<~ zrNO`<3$;}^hur*AacRFv!~8!W%wm|MtO~s#1TDHYiCjKLw!>MjP`m$VsQ<`Y7c0@Z z>6U)0snZVhWSYI!D=%3=j9-`=cygBFf*Quw@(5HwrW*<+C0AJN^XFhC#RJBO)@=xMizwAs>! zK7MslKF7nGbo*HTFam&noAyKk1`m@CN+tX!{IvC&I3+HsWxWQ0RwByEPIVE4h_ND&rXhi| zMQ8xcBq`)@770)~>=oLlenqrL93n7gCy5YBt@||FAvEW^NFZGBOa)$KiUh>L?P)tW zy`F3^{gx30Uc|WsQ~C>m5nJFk5i&>fKM=^W5j#*mhT9E~5*mxX&@AtlIpKq*>=A4S zK^YgE5Ey&#=~tD*{IcDGWGqBN1X_!fhdW9R)>}-ebqRcF(l~_U)Z*5~`GUgs&B09h zkNv9jQST4B;o;-vcDb+(kaDa@lb^rc^2yD~gJL6rZ$(x}02K-JO;oWA2FUk6)zt95 zXx+?EbzSRh8lSdXzk>u`D@h@N6i!$$XvQ)yS}#u6Idn-A3EUnlWG>q$c+1X5#o0=9 z7H-q&;8tSfnj7NnL|l{@7h>y8r;VC}ch1(bB#h=-7Vv38FT{}kVVkBrYo<4;@A${uegEzuYLX=x8v|C2GiQ~T zuNP5~`b2~1<@lGs(%Ws(*aqs5nL=R0vk^y{xuwAq}_YVPxX;L|mDI5;Fxrted6 z+F-tfCW@KU8c3~)8wv~X_61M`H=qKGbDG>d`@j;Fzgm8;J zt8O~Oy2mnW1|EC@H&(hgeTv@M6=;6`ih<;-$U;D1OWmjXVEt|R3R-J81br@%hA`{8(UAENy%fcwfifz^yb1oggdRHPXL@EB*EhPK ze{7X5xK9^;Kj+}=pjrIB%n9!4AYy`zLPI@bt5agThM;%F2X=2Rz@5^v06pLnqWSWj zk9GB>!>+}ftA+}hzu6C61sM`;!`X11rm~Mfw<2D^%x6}iM-`>&97~rqEKI=7SR+wO zdQ`vJ8>M4MDp2N00WN#(YrWpPdt{jB0ji`Vh(==&9r_Q?)Km~VRO5o zE&;mtyF%auaCN|FcP+vN*f}+8hHZC`s$8E-RhLwS=_uK~;SvZJ4P^B*I~X8``h z|Ic8xVbvINQ`GEWp8rUv?88D~L+In}d0nNpr|!E9rLRdb%7L(n+L1DMl=aX>>&&f+ zlEK}Kx_#0tcY41ORu9^6&qz~ELHeX-V1S(_;T zbfG3EzBuuaTsF;|je=vd=B2NCms2BOG6jBx-{m_tb@$TQnI6t76B6iy{9V_AhZ>_C z{3*dY7>N!xxe95WS>hK8re5p=CI;-q<>QeF{MR2y@+dgwiP*L5<1}ltTi6MzhRD@c zk&aBXq(d!YI$3HldC)B=9u}k1hdVyt`&!BepSXhZ7Q?70!^g3OKKdn#M>t1iTGEPCdaCLnsX_%+u^2kO4 zR#SRbBVSVak46N!+RE{Yjnl1ECRb3y(pbY?5_~@{hImiQHxC@j4)c`_4TqK7761DvSF&jH(DcMv_*#+*V`sgIodn;wb;4cvWHi)1bv*ytfS=T8g z6$$7p^;b?N)_UCdb?m+~Ear~DbozdE^#v&f{_r{3)SKg+Q*faf@3z)q4~%olR5Znm zp)+g^cpd7D?<_1aJU9Y*3tb^Y0>-zX`L=5PuZ>l$S-K>1pFGXNWOUC4snwA4Pilp3 zdZvVs3Q;9q%a>B@YzH=&fip-AsttU5~<%Q!kyW!)oLYR;aA1cW}SeOi@P z2|qP_AD&#riGFtT$=+~wW#zz8!**=K{8EG8^otQ~1`FVu8uz&@=DJS9-a8~PM5m4f z8VwP3G`~D)j##-fZVY=<2FC5pUNRbd9Br8)s}Izd+f5rTn4k8U8xFG6j_PRdT$H@srLr6W6L`{&AGTbSl1_@vjz$N(&Vd&syAMDvH zPXiZi)CRgo75b$}fF6=NT(>;}7aWECuoEQztae{bHCa= z)1b=oE4w8|HxFlCDdSyFC^qPBnzXqSKa2Bxsr;53U|Hw-E1JJPF}zLBFPH%9YMQsM zS#LPkJrEL&2ux_}c>Ad~VN^0f%WZgQQYZdnVa98@$_EjgOE?WoEs?KLag)NjX3?z$ zC5ZN=W21F~j(y~I=1jKBR6@-#M4bt~d7(~J6P(FHHwbgUT_SiwVG!}lQ$SF9Of(GE z(24eS*lcI?xbVD$TAZL&gfpltBp=1KD@5GdV~5^mJYA;r9c_Cn?Pnst?DT8>hmpz0 zkr^;SMCWLh+umuw?&mmF@3mu_N_PXvLdFkIE#nTk=mVg9Mn-dsn1$dVdV~0UyAW{= z^ii+<`lOX0m*rCjE%5iSF3dWH^SlXvA@04vF44?NUs4l1v2R&5Sh!?XQ6I`mz#&O^ zc3r)ZrSUyERfllu3z4CtZ3gynzC@+7FLWaky{1*hfj7vwKaC0By5oqld9aEKZZZC{ z=4nW8H8d65IHyy)a8vyv>7;JXX4}N!rW{F6sp1z3=Dd&{qo_%$mTrLH{Z~}KC)m51 zt{-VYRy%j*Z=f$Q4JlEKniyB4ZzK3nSXo1BkBNDvNlB2HZw+k*_bSR^ARR`5UK~rk z@XX-)-sUXr)C^)lBH23dei z30!Ucu~M2W-@B*PU*Uyw;i=d4*D6VT$?yo|A9#{$R&hhl$THICnfZhxVn>wTj-lCchZ8lckV!<`RGDJnFqbSwtDSzzu>Nmq1^>27N{mQV9>NFIU9`}_c z)>$KgZOvFRXW8=oPj96|C~I!*D1X{LXrfO-U%YOJaTYxLU~gYHR~N1%J(OOnRmjP} zeQsZz{rZW@$8xrp80PfbYoczDU#5*;4V^|iz1*h(&wpdsV6$Q2;UXUCZmaM0x`FM4 zTLlFw0FUrkI#1Ek^+o@Q9EJJBjv<(~BZI$1nH4eVT+w%))-{Fw#xDH$e41ah`PHW1 z=k`Zg)xjl&yuM^Pd}|vNqw%p?z{5FNuyMgpnLim=!Ro0>XD6Jh(zAn4Iy-tfX_wF7 z?BkxfE9@nIy0ARqNOZ!5R|}^LlHF*zdX+#M6k!`qZ7b@gXHk=Ph--4otcv%l%BV@)^{zAp5R#o}Yt@`a(^VSvK?l3n?<}YVAH7bj%!yqNQ zCW);CUplk&MQYt4Yy`&?qY}<%lgp z^GK3<7ms>s?v8#WMBcdFFn<>bWJ~9Qu80P>7vz^Gx}XfI6o=;7rNBL~4=>!o10fUZ znHvrJ_ddHxWS^X<%swMpk7g*%XAYKmv8+J5qb&N4piE%41wl9L#*PAeT&+iXK^f>~ zL%tMKc<&&A-y|=XR+&7}-jESh%xni_kXOde(Jv$Eh)t291a?_*{`I0vbMRm*ZYkV% zut_JBns5*C{!l0Ws7IUA!f3nf-Q(Q=QD@!jA}@JoYJ$N7mA{+?bdbPE$Pw9a&ddp- zA(I-^{DUF766Wmlg;k(_xbDXlzUBqTsc zYWKtMLv;Y9o5s6qtnF;!MkbKeG$gPlT}RPQP4K4mhEL|Ws<`ys z18z!rNcC*rNulUVPpR$Xn+|+z8nRkOpFiQhX&h{ESm-KGR(ngqh4LH~B6M6R6SzPT za1UEm86R63lP1Okk@%@|A?)eaNj53d{z*tG4;{@kb-qDKrd4dUL<-f%*OEe}G$GVW z$xD6c)l8``asJIq*n#w{s#MPO>!69;F?KZchp(*WWzQyfq`?>oy?K@?%?>Y)v^-NC z;K5wsm0Nhuxwx(C)EoOX=Mwc6q}D#r=4-7Wu3QC&mQ z89CD-iSS1XLi&j#)5?*%7345$FD9*=_1XA0yfj%GIwvs}D1Vz}y_5ssElV1EWu72Hm%(2)Q-{iF~j`iSGFa7oOd# z$;AIh<#2W^q!2exHk74SA^{U!HD&1TJG|#JH}_@t@oI~v9-m}W8vjWkof+%le?bPL6zh#cvy-p@FHFIwB{8| ziyja^JX6P+8dgfQ$(-#V>aj21#QpMP*zJefoS(>Qz719u8RNk0-lDX6w zlz|bZ%tv(ezK{CFV}^5b^>r1o9IDMi3!oxvd3Je=Lr2;p(?)`XH8C2tWaPMEt&3$A zogP8PK6X?wBWC72wTGAoV}h#JuHXEY&mbiwFjI{{Et0tuIW=t<+Y$KGmBRcrjl6R) zTsV8KJb6Ph_G>5atEi&SrV$3L8Py4fXOCUm4k@hj?n-+AuTp;GM%q#PdtB%FmUz3VTWy2WWA}2fXveHC18h73B$PhP9pKMiS|2BxSjt&Ekw(iM0(SoH5I* za0_2s(|RYZo_8I-?v_c%J?5$`M9Y$#94V9790;qn^E`2Ozg!PQ=^S zCgPoXlIur)oE9z2KKXWj(@_C~=9(Nr~*vWeR7ZmvDyBET%W?Us>h?PWehquzEp$=4-)e9%)U;#|JtG2DDm+x-#Z&@#K#S#hPH{ctbD! z(GFN?t5#A|s#}l1N%GSJT?5cH8oA+ciBW+F(`m~uL;)Q?$AWNwT_hl#?>2`7B8bwe z*59j0`3Qps{F|@(KYi9$e^HiZe#UWJY9oPinhPd%mdX8<6poJzH*?QzN8Fuq6lnck zR2X3FnsDo#PTE0NS2!=XG`kk&nMPu|8LIe62!2bXxjbXKv@3+cT95z~-9;3XtX<#)+6cHlW<$_MD~T-Gj%?bMG!wjRDf_4ZK?_v@}r z8G$bLVzSod;+UM#*Rj83@9wj|oY4z#IaiLWP?_S}pX!c=%tV^qP2BJr<}9B8 z{Z!qM(>;}wNyGE=HwXj{=N*p3t~3@|dkc=&F{XFFM>)%iS7r9Ph!6QG#HYtMHd;{@ zbqozF{i~wvFYCmQrw7ogs}szhzAKj1LOmPydh;)kk6y9g!p_e2owNa!;{wA(nq>aj z_L6JX6MHnjpP-955MC+7z6*XZA82k5sTJQW8nBZ=Z&elidyR;X!|Hl*Xvf zr{*qt!IWD6e(GmX)}LK6Qjz5J%HLbiwaf^$J6gObxY^UJmXgyavLG2&DspY&jolzu zvXKOrA&!qA_X`4#r<(acL;J{{fDVC2;KDqZ8b|Mq5)XP|Mgow)eSJ}IhjSCg^oLR; zSyGgdTNsuwIvp}Nit8>6O=%Bw2Ik`;URJv!t}&izd?a?3`AVL$7xv8X2z{_&j}X!v zaVA~Ld2wxh_6#d!UdL6Xle!V~8U9lH0j|Qc+obH*&CWxGA8go6<+j>33{}8gp>V80 z6$K-~7lfHw1|F#!tTn8ji@Nul7=pA=D_#6VMt>eFTx~FG$Q~vvY&7L~339}35Fyvj z>H5FlTFCtEzJ@j2Ea)69G{CA~=D!Y{vxobmu$GRrF;@HlJ7vNPjkjQ+Y$Fwr8f?Yz z6=au>|7=zLnU4Q_^wLnRvfjhZ*oKnK`F>G|nJ*dT65P7?#G(Q`LBmAIF`o6$_nw0% zH)HY+>$?1%@(D@AiBOPy@u>E-Bf7z}kToMP{t=v=ZbM4=8!m1^4&)&1xo3k~Lwt7k z`j4KMDdg5k&h!{;kjC7QyQ4a(K9ppcIj^EWyIIX%{t^D=%i3X=+m{yGALYFFGX)n- zvZEfZ5DA$`UX0{~8fw;6c(1Rn2t-{Lo`dtB`z0?paenp(sb4hg*{`*K5o{&E)7>B% z@5&a6qKLE-4bcu&mX}p7mc41_NpI>z-b-TnbU!dCZ5AG_T$Lc88bY6I#oL1Id@bmn zI(PdM8T=MM0!bz{qZ0>1L4>gl{;%S%Dra3Uwzm`F<6@a@@V$ccS+QQaK(yl0U2^=( z8{3begpJf?rfU24Vd;F>ZccmydXFr_MOn4&r&_r$n~PX)Bt?AK-&GiEskxjzf5X%; zo-hl)PIFG5vM6%}^kH2n@uTY4atBV^M5V9cGqS!vqDnuTNlNcuCp?m3*R5o5xH#%~l_;mVB zhXi$sT6nYQxL}WPdGPbHc)R8ImfY#fFEVX&@jeHAqsz|Bmdf2ctxs^rh+_QHE=yCo z{Y=%+V7$$pt$57SZ>z(Km7(fEm4Bsvj?*`^@9mb9-T-_2~MDq z7PaK2fEHfSj_t|Zz)!;fgZiK=q(eHpBDGy~^GY(G~I(Z(V~NrTuewC_HDM~A7z zR?M?{m;8b0OJUlSuVLFiF~y~z`PT+AM2eslq-Inw<|EXS%Yc_kIidAUeIc@AYZ^j- z{9MTPq)k+QzuZ4>Si+JX!s|UauMMGf;9~tQ zuc2_WJML;f{c(Jm?xVKo#2lC9Cx1HNAIiVZlc*g__TKD6YsNK)bEVE2cPiHOw6W&V zpLQMIWlC{XmbDVWw7#D1bjH@qi`Y{KAlgX3$!W~TMx&@!7zuD2UScc4S%isMqomU4KB1m2~RA)=$h_7IGe749=D{Kn{iVA2wgKBCz)ZUTb(4xUp*}glNHr zsLjmN?v^AGRC@@CX~=+cmq(UioMEY=pz09mQY&8-`?0Z|C7OJWzIjNG{*E4mEfinP zURmgf=88_>lB=Hz?cGI4Lif^h`!!BSg4jnNOxvtpeCYkOA)~gR-aNT9v}GLJcOG&% zs--Wx*};Y>l-I*4H(JY{JH8RQ=6<`l=aY_>B6{8)TkPP&%DMCK9OYfHFN~~%Nuwa! z#C{$GQjdmnY;Xe_akXZaqo)oZeKvbxZ64#Cn`v--P4)g4Y8Ly7J8|}QJN(3h&A8q% z1hjYBnjfv&k4$K*Pdd;OhNt1ZATQ|6Ivd=&U_Z{;=-1AFe>*&zBMUq%h3^d1&ngQ> z`f<6u6Q}qybeZ8M;q?cXEz7kSErx6rHgRXA9G`jy1#~}oG3Y@ ztFfQgxi#3WiLbNwyv!)L$#0B;qPjQKK(4h zw%TsEC`IG)ku+v7_QN-!7(5P+{((|J(dw(@*+0{WW#(SA`-=Xl{v>Rir4w{84dCjZfB0I#>k=?eL21TwOTQ)!|O#Ge2~KE zrQ3PEaX7Co^olM1K|R&ne)*|QvkH9yj6wHfsCQMz^qK@ic}at=f^K_x^*()^yT%Dw z5PDC~=lhs(_m0O81Lz$z8tKI5R&_iC4I&y7S|Q_tgwt{hOm4b1%w_1wR(?@KI9%8U>S{fJQ7r4`JaB274>H z3t_4k=4H`}AN?4Z@cazYA zrsejFSdZ$Wk*JLY2)~5v>jQ4vRm^20M;_0@hj|WH?fr^uaHN4S)^8RYekv%0vid?- zY)1s&Kvr16BRS@VjH~v=oJ<`x_{jH05_Of8*iC+~?o$W!!74c)R6SPg@i6 zfe|%E;?0n^W;~zE*mS39w$zgT!_&ADf?WdTXQ-OZxAViJ7eeCQDBf3uZC}e3-f%|^ zK1#%sA5^R0T|dm*$C#{p&~DRpM1bG5kl53Dt8^@p=m#bk?a7R=sM8o3p?;X44kLc==?`>;m(Sy3WOIV&?XQzJ2TA#KfTzcu@i99_$1i zM04Q3{D48*w|C*!6M7Mtkh5WHRtIzZc7p+*F36SS{y!AaNR2SZZ`x*@`fFKCC(2vY z|6Ng1?8C?1sS49iPWEHuzf>i4mU>%e$VlOb3FP$+%jMfE;-}^5HW%q`PLy(UPKS$j zs2hHw#DSi)*MPkIUI`L-)r;WQ2^N1ZiaViHWqE0;4-(^OaK>(2{Yk~k`DZW4`BPx{ z(EXetm95WX;^~*M&?~8Fe<+bJMg5)1|9@9hmifXgv6XXPdG+#e`(QN5;`w((hdbR~ zq3vGW+thufH+P28l$a-(`3$Gt`OF{fh=+NgdsQ@n^qx35Z97LH;&MUcaDT|?b9Zc( zJyCj6m998`FjL{3Bfr0}GlIn9h;s^lByfqN2VJwsRsU#;knVvZn&W6L$a98|j_T1o zNF)U8TTHDo&zz|=5L5a+o~#U|_*+E*hPB(JfsA4R*70hFdhq1sa|9D#E?QZ%F;75D zs5~aa!BPz1)+sR)<3s;OXD=f;Iqt>-?<_ew7Crf*y#6Y*`40i##DgeZv(~H}(r2f1 zt;t5R9kwzm2#8aqUL{f}@GLtzjU`zoX}suRmZ%2%YVcyHG`e_wsHd`@nq7D|pOf7o zf59=Dzsk{sF>`Js3$uuvQoVofvbS_{l7Bf$Z@RfeSJKRGz^~>d4F~bb`PJ{HKRPDc$ zc)ySF=GgjzjdMQR_*2$h@#oBps5IQSsLSx9-5n^-61$V>54Rb#Duk$wIk0dyd}9PY z+u;|bKI7Ke5Mbpay=0UtAce4zi7mgyoN`<5;O9`a(?5oj%X90E9K!k~M<3T0i%jQ@ zlv^n1npj-Q@0d}%oF=Df`?fmpq5jqf9j&RU4N`%kSq>wfnx&*gG?pldtZ=W9o{=I` z&o3Ua&+MOmk5nVtN%oVoX+olQqMp_vow z-oB4jLsfk>Vg1tV*Y*nwwhP2+48Qu>o5$D7es$_3XJmW{(67Wg5E!txy?>r5I$Xan zq2T=d+k++=F0>n7OV>5kU2-So@;zclMmD;->$t^B2j9++<2b9ueh&?uOXd*??kyV0 z8iYMaFE4X58yVSI?JAZHZl<(jd-1vSVvfjTds;d}y3jt+eEuXs@KZrTFN*>M7djIrI}zt^RB@>};eL}WI`llg-Dx{?HB{~S1#z5vkDaBvjgS_#6AP~$ z%cm$VH7+q~ESgA+(rD#j(CwLKK6Eq-4g*i?SE;h{H@md~X%G72j(TZUY0><9lWiI+ z0wty1x#YAp0=tPGnQlg0&i5&^g@kBc<+>#39}&H#I4>MT5PVy{J9sZqwrWTv?Z%{R zWObA~(RjO1hl9?hX+PiOEcyCK9IgW~L00QF*7al?Px>ev{HmoWX`c%XhiLqdb79gG zHk&fF2`i*G9`byX44Yb(!z{f2M5tG%FXCx&NJoNr*Yv|B_D~hE&rNEk*m^`FM#(-b z*7B+=%ZC-y2-DZ56fQ5oe<0#K5FT^ob+o6ZlI+iCSJAPlm8H|~KyiHPM1R3B7EA%UrQdo{5vv#wZvNHzB#q!E&4B>unmi!}Pl5V+Gzuyf zI0FigQi$}ePeumxtSY)0+=JY2nXNrU83Rv3gBKvBpl`YmAp&i#j|!0eo?w+s^-P}E zT`GsA@Fc7db_~YAD}kZYX`oDT=>8yrq=JX1gk41)`pe>pJqa!;cyfQ zP(kKpcyM`!Ni?tINA!aZ?c|2DbA_UkT_Qoox0ca_7 z8t%548=6)-0!R2`i{g6#`AH#!M(9#PcjNioJcsv32C#3LH~y+`|EKS0kmLUk;G#}9 From d647e909ed1e821403888a4a7c93814dbaa8ab8c Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Sun, 26 Sep 2021 09:34:16 -0600 Subject: [PATCH 23/26] Small cleanups --- deploy/runtime/ui/Generic System Plant.txt | 6 +----- src/combinecases.cpp | 8 -------- src/combinecases.h | 2 -- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/deploy/runtime/ui/Generic System Plant.txt b/deploy/runtime/ui/Generic System Plant.txt index f8ca60c986..9c5c41443c 100644 --- a/deploy/runtime/ui/Generic System Plant.txt +++ b/deploy/runtime/ui/Generic System Plant.txt @@ -1183,7 +1183,7 @@ equations{ 'ui_step_minutes' } = define() { } }; -1738 +1701 on_load{"Generic System Plant"}=define(){ on_change{'spec_mode'}(); on_change{'system_capacity'}(); @@ -1212,10 +1212,6 @@ function combine_cases() if ( hash != '' ) { result = hash{'result_code'}; // 0=success, 1=error - if ( result == 0 ) - { - x=1; - } } } diff --git a/src/combinecases.cpp b/src/combinecases.cpp index b12db4b1c6..858c60ba44 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -477,7 +477,6 @@ void CombineCasesDialog::RefreshList(size_t first_item) for (size_t i = 0; i < m_cases.size(); i++) { // Exclude generic case from displaying in case list - // TODO: If excluding geothermal, do here. if (m_cases[i].display_name != m_generic_case_name) { int ndx = m_chlCases->Append(m_cases[i].display_name); if (m_cases[i].is_selected) { @@ -500,10 +499,3 @@ void CombineCasesDialog::GetOpenCases() m_cases.push_back(CaseInfo(names[i], names[i])); } } - -std::vector CombineCasesDialog::matrix_to_vector(matrix_t &matrix) -{ - std::vector vector; - std::copy(matrix.data(), matrix.data() + matrix.ncells(), back_inserter(vector)); - return vector; -} diff --git a/src/combinecases.h b/src/combinecases.h index 0004eb14f8..d2b034389c 100644 --- a/src/combinecases.h +++ b/src/combinecases.h @@ -60,8 +60,6 @@ class CombineCasesDialog : public wxDialog void GetOpenCases(); - std::vector matrix_to_vector(matrix_t& matrix); - int m_result_code; Case* m_generic_case; wxString m_generic_case_name; From c250b29b41d84f98b00b0d703f2f1b31964bf2e6 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Sun, 26 Sep 2021 10:56:12 -0600 Subject: [PATCH 24/26] Add notice for generic-battery contingency --- src/combinecases.cpp | 33 +++++++++++++++++++++++++++++++++ src/combinecases.h | 2 ++ 2 files changed, 35 insertions(+) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 858c60ba44..de78e13b43 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -156,6 +156,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) double total_installed_cost = 0.; std::vector om_fixed(analysis_period, 0.); bool is_notices = false; + bool has_a_contingency = false; // Run each simulation for (size_t i = 0; i < arychecked.Count(); i++) { @@ -298,6 +299,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) else if (financial_name != "None" && analysis_period > std::numeric_limits::epsilon()) { total_installed_cost_this = current_case->Values().Get("total_installed_cost")->Value(); total_installed_cost += total_installed_cost_this; + has_a_contingency = has_a_contingency || HasContingency(bcsim); // O&M costs are taken from the cash flows of each system. All types of O&M costs // are entered in as fixed O&M costs in the financial case. This accounts for several things: // (a) the escalation of O&M costs @@ -445,6 +447,12 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) "View these messages or warnings on the Notices pane of the Results page.", "Combine Cases Message", wxOK | wxSTAY_ON_TOP, this); } + if (has_a_contingency && technology_name == "Generic Battery" && financial_name != "Third Party") { + wxMessageBox("Notices\n\n" + "At least one of the models has a contingency specified.\n\n" + "Verify contingency is not double-counted on this generic-battery system's Installation Costs page.", + "Combine Cases Message", wxOK | wxSTAY_ON_TOP, this); + } EndModal(wxID_OK); // 'Press' Edit array... button to show energy output array @@ -499,3 +507,28 @@ void CombineCasesDialog::GetOpenCases() m_cases.push_back(CaseInfo(names[i], names[i])); } } + +bool CombineCasesDialog::HasContingency(Simulation& bcsim) +{ + std::vector contingency_names{ + "contingency_rate", + "contingency_percent", + "csp.dtr.cost.contingency_percent", + "csp.pt.cost.contingency_percent", + "csp.mslf.cost.contingency_percent", + "csp.lf.cost.contingency_percent", + "csp.gss.cost.contingency_percent", + "csp.tr.cost.contingency_percent", + "biopwr.cost.contingency_percent", + "geotherm.cost.contingency_percent" + }; + + for (auto& contingency_name : contingency_names) { + VarValue* contingency_vv = bcsim.GetOutput(contingency_name); + if (contingency_vv && contingency_vv->Value() > std::numeric_limits::epsilon()) { + return true; + } + } + + return false; +} diff --git a/src/combinecases.h b/src/combinecases.h index d2b034389c..b01ccbfb42 100644 --- a/src/combinecases.h +++ b/src/combinecases.h @@ -60,6 +60,8 @@ class CombineCasesDialog : public wxDialog void GetOpenCases(); + bool HasContingency(Simulation& bcsim); + int m_result_code; Case* m_generic_case; wxString m_generic_case_name; From 3ddebab2e1af6f1379204946d683462b05015316 Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Sun, 26 Sep 2021 11:03:10 -0600 Subject: [PATCH 25/26] Fix Third Party Owner usage --- src/combinecases.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index de78e13b43..4331d31b89 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -404,7 +404,7 @@ void CombineCasesDialog::OnEvt(wxCommandEvent& e) } // Set installation and operating costs - if (financial_name != "None" && overwrite_capital) { + if (financial_name != "None" && financial_name != "Third Party" && overwrite_capital) { // Installation Costs m_generic_case->Values().Get("fixed_plant_input")->Set(total_installed_cost); m_generic_case->Values().Get("genericsys.cost.per_watt")->Set(0.); From 5e76027831481faf4f6e928543201c110792e7bf Mon Sep 17 00:00:00 2001 From: Matthew Boyd Date: Sun, 26 Sep 2021 12:14:59 -0600 Subject: [PATCH 26/26] Handle when there are no other cases --- src/combinecases.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/combinecases.cpp b/src/combinecases.cpp index 4331d31b89..452cf40b40 100644 --- a/src/combinecases.cpp +++ b/src/combinecases.cpp @@ -496,7 +496,9 @@ void CombineCasesDialog::RefreshList(size_t first_item) } } m_chlCases->Thaw(); - m_chlCases->SetFirstItem(first_item); + if (m_chlCases->GetCount() > 0) { + m_chlCases->SetFirstItem(first_item); + } } void CombineCasesDialog::GetOpenCases()