50
50
#include < boost/smart_ptr.hpp>
51
51
52
52
#include < QBoxLayout>
53
+ #include < QButtonGroup>
53
54
#include < QComboBox>
54
55
#include < QDateTime>
56
+ #include < QCheckBox>
57
+ #include < QDialogButtonBox>
55
58
#include < QDir>
56
59
#include < QFile>
57
60
#include < QFileDialog>
61
+ #include < QGridLayout>
58
62
#include < QLabel>
59
63
#include < QLineEdit>
60
64
#include < QMessageBox>
61
65
#include < QPushButton>
66
+ #include < QRegExp>
62
67
#include < QScrollArea>
63
68
#include < QSettings>
64
69
#include < QSizePolicy>
70
+ #include < QCoreApplication>
71
+ #include < QObject>
72
+ #include < QPushButton>
65
73
66
74
static constexpr auto NAME (" Name: " );
67
75
static constexpr auto LATITUDE (" Latitude: " );
@@ -74,6 +82,42 @@ static constexpr auto CHANGEWEATHERFILE("Change Weather File");
74
82
75
83
namespace openstudio {
76
84
85
+ SortableDesignDay::SortableDesignDay (const openstudio::model::DesignDay& designDay) : m_designDay(designDay) {
86
+ QRegExp regex (" ^.*Ann.*([\\ d\\ .]+)[\\ s]?%.*$" , Qt::CaseInsensitive);
87
+ if (regex.exactMatch (toQString (designDay.nameString ())) && regex.captureCount () == 1 ) {
88
+ m_permil = qstringToPermil (regex.capturedTexts ()[1 ]);
89
+ if (m_permil > 500 ) {
90
+ m_type = " Heating" ;
91
+ } else {
92
+ m_type = " Cooling" ;
93
+ }
94
+ }
95
+ }
96
+
97
+ int SortableDesignDay::qstringToPermil (const QString& str) {
98
+ return (int )(str.toDouble () * 10.0 );
99
+ }
100
+
101
+ QString SortableDesignDay::permilToQString (int permil) {
102
+ return QString::number ((double )permil / 10.0 , ' f' , 1 );
103
+ }
104
+
105
+ QString SortableDesignDay::key (const QString& type, int sortablePermil) {
106
+ return type + permilToQString (sortablePermil);
107
+ }
108
+
109
+ QString SortableDesignDay::type () const {
110
+ return m_type;
111
+ }
112
+
113
+ int SortableDesignDay::permil () const {
114
+ return m_permil;
115
+ }
116
+
117
+ int SortableDesignDay::sortablePermil () const {
118
+ return ((m_permil < 500 ) ? m_permil : 1000 - m_permil);
119
+ }
120
+
77
121
LocationTabView::LocationTabView (const model::Model& model, const QString& modelTempDir, QWidget* parent)
78
122
: MainTabView(tr(" Site" ), MainTabView::SUB_TAB, parent) {}
79
123
@@ -625,6 +669,170 @@ void LocationView::onWeatherFileBtnClicked() {
625
669
}
626
670
}
627
671
672
+ /* *
673
+ * @brief Displays a dialog for selecting design days from a given list.
674
+ *
675
+ * This function creates and displays a modal dialog that allows the user to select specific design days
676
+ * from a provided list of all available design days which are
677
+ * heatingPercentages "99.6%", "99%"
678
+ * and coolingPercentages "2%", "1%", "0.4%"
679
+ *
680
+ * . The dialog includes options for selecting heating
681
+ * and cooling design days based on predefined percentages. The user can choose to import all design days,
682
+ * select specific ones, or cancel the operation.
683
+ *
684
+ * @param allDesignDays A vector containing all available design days.
685
+ * @return A vector of selected design days if the user confirms the selection, or an empty vector if the user cancels.
686
+ */
687
+ std::vector<model::DesignDay> LocationView::showDesignDaySelectionDialog (const std::vector<openstudio::model::DesignDay>& allDesignDays) {
688
+
689
+ std::vector<model::DesignDay> result;
690
+
691
+ // parse out the design day names into SortableDesignDays and figure out the column and row names
692
+ std::vector<SortableDesignDay> sortableDesignDays;
693
+ std::set<QString> designDayTypes; // rows
694
+ std::set<int > sortedDesignDayPermils; // columns
695
+
696
+ // key is designDayType + sortedDesignDayPermil, value is names of dds
697
+ // each cell in the table has a unique key
698
+ std::map<QString, std::vector<openstudio::model::DesignDay>> designDayMap;
699
+ size_t numUnknownType = 0 ;
700
+ for (const auto & dd : allDesignDays) {
701
+ SortableDesignDay sdd (dd);
702
+
703
+ // skip Design Days with unknown type
704
+ if (sdd.type ().isEmpty ()) {
705
+ ++numUnknownType;
706
+ continue ;
707
+ }
708
+
709
+ sortableDesignDays.push_back (sdd);
710
+ designDayTypes.insert (sdd.type ());
711
+ sortedDesignDayPermils.insert (sdd.sortablePermil ());
712
+ QString key = SortableDesignDay::key (sdd.type (), sdd.sortablePermil ());
713
+ if (!designDayMap.contains (key)) {
714
+ designDayMap[key] = std::vector<openstudio::model::DesignDay>();
715
+ }
716
+ designDayMap[key].push_back (dd);
717
+ }
718
+
719
+ // main dialog
720
+ QDialog dialog (this , Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
721
+ dialog.setWindowTitle (QCoreApplication::translate (" LocationView" , " Import Design Days" ));
722
+ dialog.setMinimumWidth (450 );
723
+ dialog.setModal (true );
724
+ dialog.setStyleSheet (" background: #E6E6E6;" );
725
+
726
+ auto * layout = new QVBoxLayout (&dialog);
727
+
728
+ // grid view for the design day types and permils to import
729
+ auto * gridLayout = new QGridLayout ();
730
+
731
+ // first row is for headers
732
+ int row = 0 ;
733
+
734
+ auto msg = tr (" There are <span style=\" font-weight:bold;\" >%1</span> Design Days available for import" ).arg (QString::number (allDesignDays.size ()));
735
+ if (numUnknownType > 0 ) {
736
+ msg += tr (" , %1 of which are unknown type" ).arg (QString::number (numUnknownType));
737
+ }
738
+
739
+ auto * numInfo = new QLabel (msg);
740
+ gridLayout->addWidget (numInfo, row, 0 , 1 , -1 , Qt::AlignCenter);
741
+
742
+ ++row;
743
+ int column = 1 ;
744
+ for (const auto & sddp : sortedDesignDayPermils) {
745
+ auto * header = new QLabel (SortableDesignDay::permilToQString (sddp) + " %" );
746
+ header->setStyleSheet (" font-weight: bold;" );
747
+ gridLayout->addWidget (header, row, column++, Qt::AlignCenter);
748
+ }
749
+
750
+ // one row for each design day type
751
+ ++row;
752
+ QVector<QRadioButton*> allRadioButtons;
753
+ for (const auto & ddt : designDayTypes) {
754
+ column = 0 ;
755
+ bool checkedFirst = false ;
756
+ auto * rowHeader = new QLabel ();
757
+ if (ddt == " Heating" ) {
758
+ rowHeader->setText (tr (" Heating" ));
759
+ rowHeader->setStyleSheet (" font-weight: bold; color: #EF1C21;" );
760
+ } else if (ddt == " Cooling" ) {
761
+ rowHeader->setText (tr (" Cooling" ));
762
+ rowHeader->setStyleSheet (" font-weight: bold; color: #0071BD;" );
763
+ } else {
764
+ rowHeader->setText (ddt);
765
+ }
766
+ gridLayout->addWidget (rowHeader, row, column++, Qt::AlignCenter);
767
+
768
+ auto * buttonGroup = new QButtonGroup (gridLayout);
769
+ for (const auto & sddp : sortedDesignDayPermils) {
770
+ QString key = SortableDesignDay::key (ddt, sddp);
771
+ auto * radioButton = new QRadioButton ();
772
+ allRadioButtons.append (radioButton);
773
+ if (!designDayMap.contains (key)) {
774
+ radioButton->setEnabled (false );
775
+ radioButton->setCheckable (false );
776
+ radioButton->setToolTip (QString::number (0 ) + " " + tr (" Design Days" ));
777
+ radioButton->setProperty (" designDayKey" , " " );
778
+ } else {
779
+ radioButton->setEnabled (true );
780
+ radioButton->setCheckable (true );
781
+ if (!checkedFirst) {
782
+ radioButton->setChecked (true );
783
+ checkedFirst = true ;
784
+ }
785
+ radioButton->setToolTip (QString::number (designDayMap[key].size ()) + " " + tr (" Design Days" ));
786
+ radioButton->setProperty (" designDayKey" , key);
787
+ }
788
+ buttonGroup->addButton (radioButton);
789
+ gridLayout->addWidget (radioButton, row, column++, Qt::AlignCenter);
790
+ }
791
+ ++row;
792
+ }
793
+ layout->addLayout (gridLayout);
794
+ int columnCount = gridLayout->columnCount ();
795
+ int rowCount = gridLayout->rowCount ();
796
+
797
+ // ok button only imports the checked design days
798
+ auto * okButton = new QPushButton (tr (" OK" ), &dialog);
799
+ connect (okButton, &QPushButton::clicked, [&dialog, &result, &allRadioButtons, &designDayMap]() {
800
+ for (const auto & rb : allRadioButtons) {
801
+ if (rb->isChecked ()) {
802
+ QString key = rb->property (" designDayKey" ).toString ();
803
+ if (!key.isEmpty () && designDayMap.contains (key)) {
804
+ for (const auto & dd : designDayMap[key]) {
805
+ result.push_back (dd);
806
+ }
807
+ }
808
+ }
809
+ }
810
+ dialog.accept ();
811
+ });
812
+
813
+ // cancel button imports nothing
814
+ auto * cancelButton = new QPushButton (tr (" Cancel" ), &dialog);
815
+ connect (cancelButton, &QPushButton::clicked, &dialog, &QDialog::reject);
816
+
817
+ // import all imports everything
818
+ auto * importAllButton = new QPushButton (tr (" Import all" ), &dialog);
819
+ connect (importAllButton, &QPushButton::clicked, [&dialog, &result, &allDesignDays]() {
820
+ result = allDesignDays;
821
+ dialog.accept ();
822
+ });
823
+
824
+ // add all the buttons in a button box
825
+ auto * buttonBox = new QDialogButtonBox (Qt::Horizontal);
826
+ buttonBox->addButton (okButton, QDialogButtonBox::AcceptRole);
827
+ buttonBox->addButton (cancelButton, QDialogButtonBox::RejectRole);
828
+ buttonBox->addButton (importAllButton, QDialogButtonBox::YesRole);
829
+ layout->addWidget (buttonBox);
830
+
831
+ // Execute the dialog and wait for user interaction
832
+ dialog.exec ();
833
+ return result;
834
+ }
835
+
628
836
void LocationView::onDesignDayBtnClicked () {
629
837
QString fileTypes (" Files (*.ddy)" );
630
838
@@ -644,87 +852,44 @@ void LocationView::onDesignDayBtnClicked() {
644
852
if (ddyIdfFile) {
645
853
646
854
openstudio::Workspace ddyWorkspace (StrictnessLevel::None, IddFileType::EnergyPlus);
855
+
647
856
for (const IdfObject& idfObject : ddyIdfFile->objects ()) {
648
857
IddObjectType iddObjectType = idfObject.iddObject ().type ();
649
858
if ((iddObjectType == IddObjectType::SizingPeriod_DesignDay) || (iddObjectType == IddObjectType::SizingPeriod_WeatherFileDays)
650
859
|| (iddObjectType == IddObjectType::SizingPeriod_WeatherFileConditionType)) {
651
-
652
860
ddyWorkspace.addObject (idfObject);
653
861
}
654
862
}
655
863
656
- energyplus::ReverseTranslator reverseTranslator;
864
+ openstudio:: energyplus::ReverseTranslator reverseTranslator;
657
865
model::Model ddyModel = reverseTranslator.translateWorkspace (ddyWorkspace);
658
866
659
867
// Use a heuristic based on the ddy files provided by EnergyPlus
660
868
// Filter out the days that are not helpful.
661
869
if (!ddyModel.objects ().empty ()) {
662
- // Containers to hold 99%, 99.6%, 2%, 1%, and 0.4% design points
663
- std::vector<model::DesignDay> days99;
664
- std::vector<model::DesignDay> days99_6;
665
- std::vector<model::DesignDay> days2;
666
- std::vector<model::DesignDay> days1;
667
- std::vector<model::DesignDay> days0_4;
668
-
669
- bool unknownDay = false ;
670
-
671
- for (const model::DesignDay& designDay : ddyModel.getConcreteModelObjects <model::DesignDay>()) {
672
- boost::optional<std::string> name;
673
- name = designDay.name ();
674
-
675
- if (name) {
676
- QString qname = QString::fromStdString (name.get ());
677
-
678
- if (qname.contains (" 99%" )) {
679
- days99.push_back (designDay);
680
- } else if (qname.contains (" 99.6%" )) {
681
- days99_6.push_back (designDay);
682
- } else if (qname.contains (" 2%" )) {
683
- days2.push_back (designDay);
684
- } else if (qname.contains (" 1%" )) {
685
- days1.push_back (designDay);
686
- } else if (qname.contains (" .4%" )) {
687
- days0_4.push_back (designDay);
688
- } else {
689
- unknownDay = true ;
690
- }
691
- }
692
- }
693
-
694
- // Pick only the most stringent design points
695
- if (!unknownDay) {
696
- if (!days99_6.empty ()) {
697
- for (model::DesignDay designDay : days99) {
698
- designDay.remove ();
699
- }
700
- }
701
-
702
- if (!days0_4.empty ()) {
703
- for (model::DesignDay designDay : days1) {
704
- designDay.remove ();
705
- }
706
- for (model::DesignDay designDay : days2) {
707
- designDay.remove ();
708
- }
709
- } else if (!days1.empty ()) {
710
- for (model::DesignDay designDay : days2) {
711
- designDay.remove ();
712
- }
713
- }
714
- }
715
870
716
871
// Evan note: do not remove existing design days
717
872
// for (model::SizingPeriod sizingPeriod : m_model.getModelObjects<model::SizingPeriod>()){
718
873
// sizingPeriod.remove();
719
874
// }
720
875
876
+ // m_model.insertObjects(ddyModel.objects());
877
+
878
+ std::vector<openstudio::model::DesignDay> designDaysToInsert =
879
+ showDesignDaySelectionDialog (ddyModel.getConcreteModelObjects <model::DesignDay>());
880
+
881
+ // Remove design days from ddyModel that are not in designDaysToInsert
882
+ for (auto & designDay : ddyModel.getConcreteModelObjects <model::DesignDay>()) {
883
+ if (std::find (designDaysToInsert.begin (), designDaysToInsert.end (), designDay) == designDaysToInsert.end ()) {
884
+ designDay.remove ();
885
+ }
886
+ }
887
+
721
888
m_model.insertObjects (ddyModel.objects ());
722
889
723
890
m_lastDdyPathOpened = QFileInfo (fileName).absoluteFilePath ();
724
891
}
725
892
}
726
-
727
- QTimer::singleShot (0 , this , &LocationView::checkNumDesignDays);
728
893
}
729
894
}
730
895
@@ -785,7 +950,7 @@ void LocationView::setDstStartDayOfWeekAndMonth(int newWeek, int newDay, int new
785
950
void LocationView::setDstStartDate (const QDate& newdate) {
786
951
auto dst = m_model.getUniqueModelObject <model::RunPeriodControlDaylightSavingTime>();
787
952
788
- dst.setStartDate (monthOfYear (newdate.month ()), newdate.day ());
953
+ dst.setStartDate (MonthOfYear (newdate.month ()), newdate.day ());
789
954
}
790
955
791
956
void LocationView::setDstEndDayOfWeekAndMonth (int newWeek, int newDay, int newMonth) {
@@ -797,7 +962,7 @@ void LocationView::setDstEndDayOfWeekAndMonth(int newWeek, int newDay, int newMo
797
962
void LocationView::setDstEndDate (const QDate& newdate) {
798
963
auto dst = m_model.getUniqueModelObject <model::RunPeriodControlDaylightSavingTime>();
799
964
800
- dst.setEndDate (monthOfYear (newdate.month ()), newdate.day ());
965
+ dst.setEndDate (MonthOfYear (newdate.month ()), newdate.day ());
801
966
}
802
967
803
968
void LocationView::onSelectItem () {
0 commit comments