Skip to content

Attempting to address 153, by allowing user to specifically import 99%,99.6%, 0.4%,1% and 2% annual design days #791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 40 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
91e8b00
Clang format
macumber Jan 11, 2025
9bde301
Checkpoint select imports working
antonszilasi Feb 10, 2025
068e8c3
Checkpoint appears to be working
antonszilasi Feb 10, 2025
ffc742d
Checkpoint
antonszilasi Feb 13, 2025
de4cd9a
Checkpoint looking better
antonszilasi Feb 14, 2025
67aa493
this is looking good
antonszilasi Feb 15, 2025
5a781d2
Checkpoint only considering annual design days now
antonszilasi Feb 15, 2025
5a7cc1e
Good checkpoint,
antonszilasi Feb 16, 2025
f0997a8
Highlight things
antonszilasi Feb 16, 2025
e192716
Checkpoint, remove widgets before they are added
antonszilasi Feb 16, 2025
2d7eb11
Checkpoint clean up and appears to be working
antonszilasi Feb 17, 2025
33a0e60
Checkpoint this is much cleaner
antonszilasi Feb 17, 2025
3c594ab
Checkpoint make the interface more like what Dan Macumber suggested
antonszilasi Feb 17, 2025
65644e8
Even better
antonszilasi Feb 18, 2025
f18a3cd
Looking better but layout still sucks
antonszilasi Feb 18, 2025
591deaf
Checkpoint basic functionality is there
antonszilasi Feb 18, 2025
f514fb9
Oopps
antonszilasi Feb 19, 2025
82fbd84
Checkpoint
antonszilasi Feb 21, 2025
f002dc8
Checkpoint
antonszilasi Feb 21, 2025
88f8188
Checkpoint
antonszilasi Feb 21, 2025
bd5085b
Checkpoint
antonszilasi Feb 21, 2025
bf8f017
Checkpoint looking good
antonszilasi Feb 21, 2025
78291c1
Just use boost::to_lower_copy
antonszilasi Feb 28, 2025
a5263ba
use QString contains and use openstudio::isEqual
antonszilasi Feb 28, 2025
b76aeff
Rever space for DesignDaysToInsert
antonszilasi Mar 1, 2025
b620b98
Avoid refiltering and delaying filtering until Ok button is clicked
antonszilasi Mar 1, 2025
73a9060
Only add check box if the design days exist in ddy
antonszilasi Mar 1, 2025
99a64c7
clag format
antonszilasi Mar 1, 2025
d763728
this is not needed
antonszilasi Mar 1, 2025
1e4af8d
Adding some documentation
antonszilasi Mar 1, 2025
d7d13af
Update LocationTabView.cpp
antonszilasi Mar 1, 2025
10a08ee
Refactor code to use a helper class to find the design day types and …
macumber Mar 2, 2025
5b73370
Remove unintended change
macumber Mar 2, 2025
a477eb4
Fix cppcheck
macumber Mar 2, 2025
ee66ca9
Avoid copying QVector around by adding design day key to radio button…
macumber Mar 2, 2025
fc925bf
Hide borders on cells when deleted, update select all check box when …
macumber Mar 8, 2025
7dbb1f0
Clang format
macumber Mar 8, 2025
1c153d9
Fixing bug in style which prevented lines from being drawn
macumber Mar 9, 2025
949b705
Update all checkbox when adding new objects too
macumber Mar 9, 2025
4b70fb4
Clang format
macumber Mar 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/app_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ jobs:
set -x
if [ "$RUNNER_OS" == "macOS" ]; then
# Avoid "builtin __has_nothrow_assign is deprecated; use __is_nothrow_assignable instead" in boost/1.78 with recent clang
conan install . --output-folder=./build --build=missing -c tools.cmake.cmaketoolchain:generator=Ninja -s compiler.cppstd=20 -s build_type=Release -c tools.build:cxxflags="['-D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION']"
conan install . --output-folder=./build --build=missing -c tools.cmake.cmaketoolchain:generator=Ninja -s compiler.cppstd=20 -s build_type=Release -c tools.build:cxxflags="['-D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION', '-Wno-enum-constexpr-conversion']"
else
conan install . --output-folder=./build --build=missing -c tools.cmake.cmaketoolchain:generator=Ninja -s compiler.cppstd=20 -s build_type=Release
fi
Expand Down
2 changes: 1 addition & 1 deletion src/model_editor/InspectorGadget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ void InspectorGadget::layoutComboBox(QVBoxLayout* layout, QWidget* parent, opens
idx = 0;
combo->insertItem(idx, QPixmap(":/images/alert_image.png"), curVal.c_str(), "Invalid");

//QString errormsg("We have a value:");
//QString errormsg("We have a value:");
//errormsg += curVal.c_str();
//errormsg += " that does not match the allowable values in the idd.Name:";
//errormsg += name.c_str();
Expand Down
1 change: 1 addition & 0 deletions src/openstudio_lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@ set(${target_name}_test_src
test/FacilityShading_GTest.cpp
test/Geometry_GTest.cpp
test/IconLibrary_GTest.cpp
test/LocationTab_GTest.cpp
test/ObjectSelector_GTest.cpp
test/OSDropZone_GTest.cpp
test/OSLineEdit_GTest.cpp
Expand Down
257 changes: 198 additions & 59 deletions src/openstudio_lib/LocationTabView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,26 @@
#include <boost/smart_ptr.hpp>

#include <QBoxLayout>
#include <QButtonGroup>
#include <QComboBox>
#include <QDateTime>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QRegExp>
#include <QScrollArea>
#include <QSettings>
#include <QSizePolicy>
#include <QCoreApplication>
#include <QObject>
#include <QPushButton>

static constexpr auto NAME("Name: ");
static constexpr auto LATITUDE("Latitude: ");
Expand All @@ -74,6 +82,42 @@ static constexpr auto CHANGEWEATHERFILE("Change Weather File");

namespace openstudio {

SortableDesignDay::SortableDesignDay(const openstudio::model::DesignDay& designDay) : m_designDay(designDay) {
QRegExp regex("^.*Ann.*([\\d\\.]+)[\\s]?%.*$", Qt::CaseInsensitive);
if (regex.exactMatch(toQString(designDay.nameString())) && regex.captureCount() == 1) {
m_permil = qstringToPermil(regex.capturedTexts()[1]);
if (m_permil > 500) {
m_type = "Heating";
} else {
m_type = "Cooling";
}
}
}

int SortableDesignDay::qstringToPermil(const QString& str) {
return (int)(str.toDouble() * 10.0);
}

QString SortableDesignDay::permilToQString(int permil) {
return QString::number((double)permil / 10.0, 'f', 1);
}

QString SortableDesignDay::key(const QString& type, int sortablePermil) {
return type + permilToQString(sortablePermil);
}

QString SortableDesignDay::type() const {
return m_type;
}

int SortableDesignDay::permil() const {
return m_permil;
}

int SortableDesignDay::sortablePermil() const {
return ((m_permil < 500) ? m_permil : 1000 - m_permil);
}

LocationTabView::LocationTabView(const model::Model& model, const QString& modelTempDir, QWidget* parent)
: MainTabView(tr("Site"), MainTabView::SUB_TAB, parent) {}

Expand Down Expand Up @@ -625,6 +669,145 @@ void LocationView::onWeatherFileBtnClicked() {
}
}

/**
* @brief Displays a dialog for selecting design days from a given list.
*
* This function creates and displays a modal dialog that allows the user to select specific design days
* from a provided list of all available design days which are
* heatingPercentages "99.6%", "99%"
* and coolingPercentages "2%", "1%", "0.4%"
*
* . The dialog includes options for selecting heating
* and cooling design days based on predefined percentages. The user can choose to import all design days,
* select specific ones, or cancel the operation.
*
* @param allDesignDays A vector containing all available design days.
* @return A vector of selected design days if the user confirms the selection, or an empty vector if the user cancels.
*/
std::vector<model::DesignDay> LocationView::showDesignDaySelectionDialog(const std::vector<openstudio::model::DesignDay>& allDesignDays) {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmarrec somehow your other comments were lost but I addressed two of them in

78291c1

and a5263ba

std::vector<model::DesignDay> result;

// parse out the design day names into SortableDesignDays and figure out the column and row names
std::vector<SortableDesignDay> sortableDesignDays;
std::set<QString> designDayTypes; // rows
std::set<int> sortedDesignDayPermils; // columns

// key is designDayType + sortedDesignDayPermil, value is names of dds
// each cell in the table has a unique key
std::map<QString, std::vector<openstudio::model::DesignDay>> designDayMap;
for (const auto& dd : allDesignDays) {
SortableDesignDay sdd(dd);

// skip Design Days with unknown type
if (sdd.type().isEmpty()) {
continue;
}

sortableDesignDays.push_back(sdd);
designDayTypes.insert(sdd.type());
sortedDesignDayPermils.insert(sdd.sortablePermil());
QString key = SortableDesignDay::key(sdd.type(), sdd.sortablePermil());
if (!designDayMap.contains(key)) {
designDayMap[key] = std::vector<openstudio::model::DesignDay>();
}
designDayMap[key].push_back(dd);
}

// main dialog
QDialog dialog(this);
dialog.setWindowTitle(QCoreApplication::translate("LocationView", "Import Design Days"));
dialog.setModal(true);
QVBoxLayout* layout = new QVBoxLayout(&dialog);

// grid view for the design day types and permils to import
QGridLayout* gridLayout = new QGridLayout();

// first row is for headers
int row = 0;
int column = 1;
for (const auto& sddp : sortedDesignDayPermils) {
QLabel* header = new QLabel(SortableDesignDay::permilToQString(sddp) + "%");
gridLayout->addWidget(header, row, column++, Qt::AlignCenter);
}

// one row for each design day type
row = 1;
QVector<QRadioButton*> allRadioButtons;
for (const auto& ddt : designDayTypes) {
column = 0;
bool checkedFirst = false;
QLabel* label = new QLabel(ddt);
gridLayout->addWidget(label, row, column++, Qt::AlignCenter);

QButtonGroup* buttonGroup = new QButtonGroup(gridLayout);
for (const auto& sddp : sortedDesignDayPermils) {
QString key = SortableDesignDay::key(ddt, sddp);
QRadioButton* radioButton = new QRadioButton();
allRadioButtons.append(radioButton);
if (!designDayMap.contains(key)) {
radioButton->setEnabled(false);
radioButton->setCheckable(false);
radioButton->setToolTip(QString::number(0) + " " + tr("Design Days"));
radioButton->setProperty("designDayKey", "");
} else {
radioButton->setEnabled(true);
radioButton->setCheckable(true);
if (!checkedFirst) {
radioButton->setChecked(true);
checkedFirst = true;
}
radioButton->setToolTip(QString::number(designDayMap[key].size()) + " " + tr("Design Days"));
radioButton->setProperty("designDayKey", key);
}
buttonGroup->addButton(radioButton);
gridLayout->addWidget(radioButton, row, column++, Qt::AlignCenter);
}
++row;
}
layout->addLayout(gridLayout);
int columnCount = gridLayout->columnCount();
int rowCount = gridLayout->rowCount();

// ok button only imports the checked design days
QPushButton* okButton = new QPushButton(tr("Ok"), &dialog);
connect(okButton, &QPushButton::clicked, [&dialog, &result, &allRadioButtons, &designDayMap]() {
for (const auto& rb : allRadioButtons) {
if (rb->isChecked()) {
QString key = rb->property("designDayKey").toString();
if (!key.isEmpty() && designDayMap.contains(key)) {
for (const auto& dd : designDayMap[key]) {
result.push_back(dd);
}
}
}
}
dialog.accept();
});

// cancel button imports nothing
QPushButton* cancelButton = new QPushButton(tr("Cancel"), &dialog);
connect(cancelButton, &QPushButton::clicked, &dialog, &QDialog::reject);

// import all imports everythig
Copy link
Preview

Copilot AI Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: 'everythig' should be corrected to 'everything'.

Suggested change
// import all imports everythig
// import all imports everything

Copilot uses AI. Check for mistakes.

QPushButton* importAllButton = new QPushButton(tr("Import all"), &dialog);
connect(importAllButton, &QPushButton::clicked, [&dialog, &result, &allDesignDays]() {
result = allDesignDays;
dialog.accept();
});

// add all the buttons in a button box
QDialogButtonBox* buttonBox = new QDialogButtonBox(Qt::Horizontal);
buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole);
buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole);
buttonBox->addButton(importAllButton, QDialogButtonBox::YesRole);
layout->addWidget(buttonBox);

// Execute the dialog and wait for user interaction
dialog.exec();
return result;
}

void LocationView::onDesignDayBtnClicked() {
QString fileTypes("Files (*.ddy)");

Expand All @@ -644,87 +827,43 @@ void LocationView::onDesignDayBtnClicked() {
if (ddyIdfFile) {

openstudio::Workspace ddyWorkspace(StrictnessLevel::None, IddFileType::EnergyPlus);

for (const IdfObject& idfObject : ddyIdfFile->objects()) {
IddObjectType iddObjectType = idfObject.iddObject().type();
if ((iddObjectType == IddObjectType::SizingPeriod_DesignDay) || (iddObjectType == IddObjectType::SizingPeriod_WeatherFileDays)
|| (iddObjectType == IddObjectType::SizingPeriod_WeatherFileConditionType)) {

ddyWorkspace.addObject(idfObject);
}
}

energyplus::ReverseTranslator reverseTranslator;
openstudio::energyplus::ReverseTranslator reverseTranslator;
model::Model ddyModel = reverseTranslator.translateWorkspace(ddyWorkspace);

// Use a heuristic based on the ddy files provided by EnergyPlus
// Filter out the days that are not helpful.
if (!ddyModel.objects().empty()) {
// Containers to hold 99%, 99.6%, 2%, 1%, and 0.4% design points
std::vector<model::DesignDay> days99;
std::vector<model::DesignDay> days99_6;
std::vector<model::DesignDay> days2;
std::vector<model::DesignDay> days1;
std::vector<model::DesignDay> days0_4;

bool unknownDay = false;

for (const model::DesignDay& designDay : ddyModel.getConcreteModelObjects<model::DesignDay>()) {
boost::optional<std::string> name;
name = designDay.name();

if (name) {
QString qname = QString::fromStdString(name.get());

if (qname.contains("99%")) {
days99.push_back(designDay);
} else if (qname.contains("99.6%")) {
days99_6.push_back(designDay);
} else if (qname.contains("2%")) {
days2.push_back(designDay);
} else if (qname.contains("1%")) {
days1.push_back(designDay);
} else if (qname.contains(".4%")) {
days0_4.push_back(designDay);
} else {
unknownDay = true;
}
}
}

// Pick only the most stringent design points
if (!unknownDay) {
if (!days99_6.empty()) {
for (model::DesignDay designDay : days99) {
designDay.remove();
}
}

if (!days0_4.empty()) {
for (model::DesignDay designDay : days1) {
designDay.remove();
}
for (model::DesignDay designDay : days2) {
designDay.remove();
}
} else if (!days1.empty()) {
for (model::DesignDay designDay : days2) {
designDay.remove();
}
}
}

// Evan note: do not remove existing design days
//for (model::SizingPeriod sizingPeriod : m_model.getModelObjects<model::SizingPeriod>()){
// sizingPeriod.remove();
//}

//m_model.insertObjects(ddyModel.objects());

std::vector<openstudio::model::DesignDay> designDaysToInsert = showDesignDaySelectionDialog(ddyModel.getModelObjects<model::DesignDay>());

// Remove design days from ddyModel that are not in designDaysToInsert
for (auto& designDay : ddyModel.getConcreteModelObjects<model::DesignDay>()) {
if (std::find(designDaysToInsert.begin(), designDaysToInsert.end(), designDay) == designDaysToInsert.end()) {
designDay.remove();
}
}

m_model.insertObjects(ddyModel.objects());

m_lastDdyPathOpened = QFileInfo(fileName).absoluteFilePath();
}
}

QTimer::singleShot(0, this, &LocationView::checkNumDesignDays);
}
}

Expand Down Expand Up @@ -785,7 +924,7 @@ void LocationView::setDstStartDayOfWeekAndMonth(int newWeek, int newDay, int new
void LocationView::setDstStartDate(const QDate& newdate) {
auto dst = m_model.getUniqueModelObject<model::RunPeriodControlDaylightSavingTime>();

dst.setStartDate(monthOfYear(newdate.month()), newdate.day());
dst.setStartDate(MonthOfYear(newdate.month()), newdate.day());
}

void LocationView::setDstEndDayOfWeekAndMonth(int newWeek, int newDay, int newMonth) {
Expand All @@ -797,7 +936,7 @@ void LocationView::setDstEndDayOfWeekAndMonth(int newWeek, int newDay, int newMo
void LocationView::setDstEndDate(const QDate& newdate) {
auto dst = m_model.getUniqueModelObject<model::RunPeriodControlDaylightSavingTime>();

dst.setEndDate(monthOfYear(newdate.month()), newdate.day());
dst.setEndDate(MonthOfYear(newdate.month()), newdate.day());
}

void LocationView::onSelectItem() {
Expand Down
Loading