-
Notifications
You must be signed in to change notification settings - Fork 222
Remove allowedObjects check from energyPlusOutputRequests
#5358
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
Conversation
ca84a21 to
9a162a8
Compare
jmarrec
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/workflow/Util.cpp
Outdated
| // If already present, don't do it | ||
| for (const auto& wo : workspace.getObjectsByType(iddObjectType)) { | ||
| if (idfObject.dataFieldsEqual(wo)) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| workspace.addObject(idfObject); | ||
| workspace.addObject(idfObject); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't work like this. We still have to merge the unique ones
|
|
||
| for (const auto& newReport : reportsToAdd) { | ||
| if (std::find(reports.cbegin(), reports.cend(), newReport) != reports.cend()) { | ||
| if (std::find(reports.cbegin(), reports.cend(), newReport) == reports.cend()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was an existing logic error. Found it because I wrote a test.
| TEST_F(WorkflowFixture, Util_mergeOutputTableSummaryReports) { | ||
| IdfObject existingObject(IddObjectType::Output_Table_SummaryReports); | ||
| existingObject.pushExtensibleGroup({"InputVerificationandResultsSummary"}); | ||
| existingObject.pushExtensibleGroup({"ClimaticDataSummary"}); | ||
|
|
||
| IdfObject newObject(IddObjectType::Output_Table_SummaryReports); | ||
| newObject.pushExtensibleGroup({"ComponentSizingSummary"}); | ||
| newObject.pushExtensibleGroup({"ClimaticDataSummary"}); | ||
| newObject.pushExtensibleGroup({"EnvelopeSummary"}); | ||
|
|
||
| EXPECT_TRUE(openstudio::workflow::util::mergeOutputTableSummaryReports(existingObject, newObject)); | ||
| ASSERT_EQ(4, existingObject.numExtensibleGroups()); | ||
| auto egs = existingObject.extensibleGroups(); | ||
| EXPECT_EQ("InputVerificationandResultsSummary", egs.at(0).getString(0).get()); | ||
| EXPECT_EQ("ClimaticDataSummary", egs.at(1).getString(0).get()); | ||
| EXPECT_EQ("ComponentSizingSummary", egs.at(2).getString(0).get()); | ||
| EXPECT_EQ("EnvelopeSummary", egs.at(3).getString(0).get()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New test for mergeOutputTableSummaryReports
| TEST_F(WorkflowFixture, Util_addEnergyPlusOutputRequest) { | ||
|
|
||
| Workspace w(StrictnessLevel::Draft, IddFileType::EnergyPlus); | ||
| std::vector<IdfObject> energyPlusOutputRequests; | ||
| { | ||
| // Unique with explicit merge | ||
| WorkspaceObject wo = w.addObject(IdfObject(IddObjectType::Output_Table_SummaryReports)).get(); | ||
| wo.pushExtensibleGroup({"InputVerificationandResultsSummary"}); | ||
| wo.pushExtensibleGroup({"ClimaticDataSummary"}); | ||
|
|
||
| auto& newObject = energyPlusOutputRequests.emplace_back(IddObjectType::Output_Table_SummaryReports); | ||
| newObject.pushExtensibleGroup({"ComponentSizingSummary"}); | ||
| newObject.pushExtensibleGroup({"ClimaticDataSummary"}); | ||
| newObject.pushExtensibleGroup({"EnvelopeSummary"}); | ||
|
|
||
| EXPECT_TRUE(openstudio::workflow::util::addEnergyPlusOutputRequest(w, newObject)); | ||
| { | ||
| auto wos = w.getObjectsByType(IddObjectType::Output_Table_SummaryReports); | ||
| ASSERT_EQ(1, wos.size()); | ||
| auto egs = wos.front().extensibleGroups(); | ||
| EXPECT_EQ("InputVerificationandResultsSummary", egs.at(0).getString(0).get()); | ||
| EXPECT_EQ("ClimaticDataSummary", egs.at(1).getString(0).get()); | ||
| EXPECT_EQ("ComponentSizingSummary", egs.at(2).getString(0).get()); | ||
| EXPECT_EQ("EnvelopeSummary", egs.at(3).getString(0).get()); | ||
| } | ||
| } | ||
| { | ||
| // Unique and present already | ||
| WorkspaceObject wo = w.addObject(IdfObject(IddObjectType::Output_SQLite)).get(); | ||
| wo.setString(0, "Simple"); | ||
|
|
||
| auto& newObject = energyPlusOutputRequests.emplace_back(IddObjectType::Output_SQLite); | ||
| newObject.setString(0, "SimpleAndTabular"); | ||
| newObject.setString(1, "JtoKWH"); | ||
|
|
||
| EXPECT_TRUE(openstudio::workflow::util::addEnergyPlusOutputRequest(w, newObject)); | ||
| { | ||
| auto wos = w.getObjectsByType(IddObjectType::Output_SQLite); | ||
| ASSERT_EQ(1, wos.size()); | ||
| const auto& new_wo = wos.front(); | ||
| EXPECT_EQ("SimpleAndTabular", new_wo.getString(0).get()); | ||
| EXPECT_EQ("JtoKWH", new_wo.getString(1).get()); | ||
| } | ||
| } | ||
| { | ||
| // Unique and not present already | ||
| auto& newObject = energyPlusOutputRequests.emplace_back(IddObjectType::Output_Diagnostics); | ||
| newObject.setString(0, "DisplayExtraWarnings"); | ||
| newObject.setString(1, "DisplayUnusedSchedules"); | ||
|
|
||
| EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Output_Diagnostics).size()); | ||
| EXPECT_TRUE(openstudio::workflow::util::addEnergyPlusOutputRequest(w, newObject)); | ||
| { | ||
| auto wos = w.getObjectsByType(IddObjectType::Output_Diagnostics); | ||
| ASSERT_EQ(1, wos.size()); | ||
| const auto& new_wo = wos.front(); | ||
| EXPECT_EQ("DisplayExtraWarnings", new_wo.getString(0).get()); | ||
| EXPECT_EQ("DisplayUnusedSchedules", new_wo.getString(1).get()); | ||
| } | ||
| } | ||
| { | ||
| // Non unique, and present already | ||
| WorkspaceObject wo = w.addObject(IdfObject(IddObjectType::Schedule_Constant)).get(); | ||
| wo.setString(0, "AlwaysOn"); | ||
| wo.setDouble(2, 1.0); | ||
|
|
||
| auto& newObject = energyPlusOutputRequests.emplace_back(IddObjectType::Schedule_Constant); | ||
| newObject.setString(0, "AlwaysOn"); | ||
| newObject.setDouble(2, 1.0); | ||
|
|
||
| EXPECT_TRUE(w.getObjectByTypeAndName(IddObjectType::Schedule_Constant, "AlwaysOn")); | ||
| EXPECT_FALSE(openstudio::workflow::util::addEnergyPlusOutputRequest(w, newObject)); | ||
| { | ||
| EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Schedule_Constant).size()); | ||
| EXPECT_TRUE(w.getObjectByTypeAndName(IddObjectType::Schedule_Constant, "AlwaysOn")); | ||
| } | ||
| } | ||
| { | ||
| // Non unique, and not present already | ||
| auto& newObject = energyPlusOutputRequests.emplace_back(IddObjectType::Schedule_Constant); | ||
| newObject.setString(0, "A New Schedule"); | ||
| newObject.setDouble(2, 18.0); | ||
| EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Schedule_Constant).size()); | ||
| EXPECT_TRUE(w.getObjectByTypeAndName(IddObjectType::Schedule_Constant, "AlwaysOn")); | ||
| EXPECT_TRUE(openstudio::workflow::util::addEnergyPlusOutputRequest(w, newObject)); | ||
| { | ||
| auto wos = w.getObjectsByType(IddObjectType::Schedule_Constant); | ||
| ASSERT_EQ(2, wos.size()); | ||
| EXPECT_TRUE(w.getObjectByTypeAndName(IddObjectType::Schedule_Constant, "AlwaysOn")); | ||
| auto new_wo_ = w.getObjectByTypeAndName(IddObjectType::Schedule_Constant, "A New Schedule"); | ||
| ASSERT_TRUE(new_wo_); | ||
| EXPECT_EQ("A New Schedule", new_wo_->getString(0).get()); | ||
| EXPECT_TRUE(new_wo_->isEmpty(1)); | ||
| EXPECT_EQ(18.0, new_wo_->getDouble(2).get()); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New test for addEnergyPlusOutputRequest, specifically I'm testing what happens with unique objects. We should NOT end up with more than 1
| const auto iddObject = idfObject.iddObject(); | ||
| const bool is_unique = iddObject.properties().unique; | ||
| const auto iddObjectType = iddObject.type(); | ||
| if (!is_unique) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not unique, indeed just check if there is the same object via dataFieldsEqual and add it if not.
| // }; | ||
|
|
||
| if (iddObjectType == IddObjectType::Output_Table_SummaryReports) { | ||
| } else if (iddObjectType == IddObjectType::Output_Table_SummaryReports) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's summaryReports, merge.
| } else { | ||
| // It's a unique one, for which we didn't write a merge, so remove it first | ||
| auto existingUniqueObjects = workspace.getObjectsByType(iddObjectType); | ||
| if (!existingUniqueObjects.empty()) { | ||
| // Technically it should be size == 1 | ||
| if (std::ranges::any_of(existingUniqueObjects, [&idfObject](const auto& wo) { return idfObject.dataFieldsEqual(wo); })) { | ||
| return false; | ||
| } | ||
| for (auto& wo : existingUniqueObjects) { | ||
| wo.remove(); | ||
| } | ||
| } | ||
| workspace.addObject(idfObject); | ||
| return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's a unique one, check if there's the same exact object in there already and return false if so.
Otherwise, remove it first before adding the one (=replace).
We shouldn't end up with 2 "Output:SQLite" objects for eg, or E+ will throw
| return added; | ||
| } | ||
|
|
||
| bool addEnergyPlusOutputRequest(Workspace& workspace, IdfObject& idfObject) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was super tempted to add a LOG (Warn or Info) in there, something along the lines of
if iddObjectType.valueName does not start with "Output" or "Meter", or "EnergyManagement" or "PythonPlugin", then issue a warning like "Requested to add an object of type XXX, ensure you aren't going to modify the energy usage of the model".
But I'm guessing @eringold and @shorowit wouldn't like that too much either, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not against log messages. Though technically objects starting with 'EnergyManagement' or 'PythonPlugin' could modify energy use. Maybe better would be to check if object type names contain just 'Output' or 'Meter'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is true but PythonPlugin (and EnergyManagement is the same) were some of the objects that you specifically wanted in your OP, and they could have a valid application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to add the Warn after all, but not being restrictive and letting the request be processed anyways.
|
@wenyikuang can you look into mac failing please? |
…xisting one before you add another
bf5726e to
958020e
Compare
I aimed to not be restrictive, and it'll still allow it. I also made the list of prefix easy to change even for someone who isn't familiar with C++
| // If not marked safe, we Warn, but we let you do it anyways | ||
| if (isEnergyPlusOutputRequestPotentiallyUnsafe(iddObjectType)) { | ||
| LOG_FREE(Warn, "openstudio.worklow.Util", | ||
| "Requested to add an EnergyPlus Output Request of type '" | ||
| << iddObjectType.valueName() << "' which is not marked as safe: make sure you aren't going to modify the energy usage of the model."); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a Warn message when not safe, but still we allow it anyways.
| // Schedule can be used onto a meter/variable, the EMS/PythonPlugin **could** be used for custom reporting | ||
| // Also adding some cost/utility related objects, the goal is to be not too restrictive here | ||
| static constexpr std::array<std::string_view, 11> safe_idd_prefixes{ | ||
| "Output", "Meter", "PythonPlugin", "EnergyManagementSystem", "Schedule", "LifeCycleCost", "UtilityCost", "ComponentCost", | ||
| "Compliance", "Currency", "FuelFactors", | ||
| }; | ||
|
|
||
| // If the IddObjectType.valueName doesn't start with one of the allowed prefixes (see `safe_idd_prefixes`), it returns true. | ||
| bool isEnergyPlusOutputRequestPotentiallyUnsafe(const IddObjectType& iddObjectType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I aimed to strike the right balance here: the list is not restrictive on purpose
- Remember that this is just resulting in a Warn message, it'll still process your request.
- Also, energyplusOutputRequests is somewhat moot now that we have a modelOutputRequests method
- Finally, I made the list of prefix really easy to change: just add to that std::array and you're good to go, which would allow anyone to propose a change even without knowing much C++
| TEST_F(WorkflowFixture, Util_isEnergyPlusOutputRequestPotentiallyUnsafe) { | ||
|
|
||
| std::vector<IddObjectType> should_be_allowed{ | ||
| IddObjectType::Output_Table_Annual, | ||
| IddObjectType::Meter_Custom, | ||
| IddObjectType::Schedule_Compact, | ||
| IddObjectType::Output_Diagnostics, | ||
| IddObjectType::Output_Variable, | ||
| IddObjectType::EnergyManagementSystem_OutputVariable, | ||
| IddObjectType::PythonPlugin_OutputVariable, | ||
| IddObjectType::LifeCycleCost_Parameters, | ||
| IddObjectType::UtilityCost_Tariff, | ||
| IddObjectType::ComponentCost_Adjustments, | ||
| IddObjectType::Compliance_Building, | ||
| IddObjectType::CurrencyType, | ||
| }; | ||
| for (const auto& iddObjectType : should_be_allowed) { | ||
| EXPECT_FALSE(openstudio::workflow::util::isEnergyPlusOutputRequestPotentiallyUnsafe(iddObjectType)) << "Failed for " << iddObjectType; | ||
| } | ||
|
|
||
| EXPECT_TRUE(openstudio::workflow::util::isEnergyPlusOutputRequestPotentiallyUnsafe(IddObjectType::People)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New test
kbenne
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great. The compromise by logging a warning when certain types are added seems reasonable to me.
|
@wnykuang it looks like the non linux builds are still failing. Can you have a look? I don't think it is related to this PR. |
|
Let me clean it up (again) and run it again. I think the "real" issue (due to NREL's network) should be gone. The issues should be gone after that. |
|
well it still broken in osx-incremental, let me dive deep. |
|
OK, Actually looks like this alarm is reasonable: Looks like we do have mismatch tags. in https://github.com/NREL/OpenStudio/blob/unlimit_energyplusoutputrequests/src/utilities/idd/IddEnums.hpp#L32 I think that's something we may need update? |
This branch was rebased onto develop today. I just fixed the class -> struct (hopefully did it right, on my phone) |
|
CI Results for 5e9f22a:
|
|
OSX build still failing at CPack step. Wipe the build/_CPack_Packages and make sure there is enough space on the device? Maybe upgrade QtIFW too in the process. Also, it'd be a good idea to install pytest... |
|
Windows is happy now. Mac has been broken for ever, so just ignoring it. Merging. |
Pull request overview
energyPlusOutputRequests#5357Pull Request Author
src/model/test)src/energyplus/Test)src/osversion/VersionTranslator.cpp)Labels:
IDDChangeAPIChangePull Request - Ready for CIso that CI builds your PRReview Checklist
This will not be exhaustively relevant to every PR.