From 7bb6f5ec8b58b1de110ed5265d69a1d1ebf1adc4 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 18 Mar 2025 00:06:21 +0100 Subject: [PATCH 1/2] #5368 - Wrap OutputTableAnnual and OutputTableMonthly * https://github.com/NREL/OpenStudio/issues/5368 * Companion PR: https://github.com/NREL/OpenStudio/pull/5369 --- .gitignore | 8 +- model/simulationtests/output_tables.py | 137 +++++++++++++++++++++++ model/simulationtests/output_tables.rb | 145 +++++++++++++++++++++++++ model_tests.rb | 13 +++ 4 files changed, 299 insertions(+), 4 deletions(-) create mode 100644 model/simulationtests/output_tables.py create mode 100644 model/simulationtests/output_tables.rb diff --git a/.gitignore b/.gitignore index f7a04b5d..95b97a01 100644 --- a/.gitignore +++ b/.gitignore @@ -27,10 +27,10 @@ test/*out_*.sqltest test/*out_*.xml # TODO: keep updating as new versions are incoming # We do not commit the current develop version until it's final -test/*_3.9.1*_out.osw -test/*_3.9.1*_out*.status -test/*_3.9.1*_out.sqltest -test/*_3.9.1*_out*.xml +test/*_3.10.0*_out.osw +test/*_3.10.0*_out*.status +test/*_3.10.0*_out.sqltest +test/*_3.10.0*_out*.xml # Ignore alphas and rcs. CI will force add anything under test/ so it's unaffected test/*_[0-9].[0-9].[0-9]-*_out* diff --git a/model/simulationtests/output_tables.py b/model/simulationtests/output_tables.py new file mode 100644 index 00000000..6a8c2bbf --- /dev/null +++ b/model/simulationtests/output_tables.py @@ -0,0 +1,137 @@ +# This test aims to test the new **Unique** ModelObjects related to Output +# added in 3.0.0: +# * OutputDiagnostics, +# * OutputDebuggingData, +# * OutputJSON, and +# * OutputTableSummaryReports + +import openstudio + +from lib.baseline_model import BaselineModel + +model = BaselineModel() + +# make a 1 story, 100m X 50m, 1 zone building +model.add_geometry(length=100, width=50, num_floors=1, floor_to_floor_height=4, plenum_height=0, perimeter_zone_depth=0) + +# add windows at a 40% window-to-wall ratio +model.add_windows(wwr=0.4, offset=1, application_type="Above Floor") + +# add thermostats +model.add_thermostats(heating_setpoint=19, cooling_setpoint=26) + +# assign constructions from a local library to the walls/windows/etc. in the model +model.set_constructions() + +# set whole building space type; simplified 90.1-2004 Large Office Whole Building +model.set_space_type() + +# add design days to the model (Chicago) +model.add_design_days() + +# In order to produce more consistent results between different runs, +# we sort the zones by names +# (There's only one here, but just in case this would be copy pasted somewhere +# else...) +zones = sorted(model.getThermalZones(), key=lambda z: z.nameString()) +z = zones[0] +z.setUseIdealAirLoads(True) + +############################################################################### +# OUTPUT:TABLE:MONTHLY # +############################################################################### + +# Factory method to creates a OutputTableMonthly from the E+ datasets/StandardReports.idf +# Please use the OutputTableMonthly.validStandardReportNames() static method +# to look up the valid names as it will throw if it cannot find it +standardReportName = next(reversed(sorted(openstudio.model.OutputTableMonthly.validStandardReportNames()))) +output_table_monthly = openstudio.model.OutputTableMonthly.fromStandardReports(model, standardReportName) + +# You can of course create one yourself +output_table_monthly = openstudio.model.OutputTableMonthly(model) +output_table_monthly.setName("Fan Report") +output_table_monthly.setDigitsAfterDecimal(4) + +# To add a group, you can use the convenience method +# bool addMonthlyVariableGroup(std.string variableOrMeterName, std.string aggregationType = "SumOrAverage"); + +groups = [ + # variableOrMeterName, SumOrAverage + ("Fan Electricity Energy", "SumOrAverage"), + ("Fan Rise in Air Temperature", "SumOrAverage"), + ("Fan Electricity Rate", "Maximum"), + ("Fan Rise in Air Temperature", "ValueWhenMaximumOrMinimum"), +] +output_table_monthly.addMonthlyVariableGroup(groups[0][0], groups[0][1]) + +# This will in turn actually use the helper class MonthlyVariableGroup +output_table_monthly.addMonthlyVariableGroup(openstudio.model.MonthlyVariableGroup(groups[1][0], groups[1][1])) + +assert output_table_monthly.numberofMonthlyVariableGroups() == 2 +# This is a vector of MonthlyVariableGroup +assert len(output_table_monthly.monthlyVariableGroups()) == 2 +first_monthly_group = output_table_monthly.monthlyVariableGroups()[0] +# This returns an OptionalMonthlyVariableGroup +first_monthly_group_ = output_table_monthly.getMonthlyVariableGroup(0) +assert first_monthly_group_.is_initialized() +# The equality operator is defined to check for both variableOrMeterName and +# aggregationType +assert first_monthly_group == first_monthly_group_.get() +assert first_monthly_group.variableOrMeterName() == "Fan Electricity Energy" +assert first_monthly_group.aggregationType() == "SumOrAverage" + +output_table_monthly.removeMonthlyVariableGroup(1) +output_table_monthly.removeAllMonthlyVariableGroups() + +# There is also a batch add +monthly_groups = [openstudio.model.MonthlyVariableGroup(g[0], g[1]) for g in groups] +output_table_monthly.addMonthlyVariableGroups(monthly_groups) +assert output_table_monthly.numberofMonthlyVariableGroups() == 4 + +############################################################################### +# OUTPUT:TABLE:ANNUAL # +############################################################################### + +output_table_annual = openstudio.model.OutputTableAnnual(model) +output_table_annual.setName("Electricity Report") +output_table_annual.setSchedule(model.alwaysOnDiscreteSchedule()) +output_table_annual.setFilter("Zone 1") +output_table_annual.resetFilter() + +groups = [ + # variableorMeterorEMSVariableorField, aggregationType, digitsAfterDecimal + ("Electricity:Facility", "SumOrAverage", 3), + ("Electricity:Facility", "Maximum", 1), +] + +output_table_annual.addAnnualVariableGroup(groups[0][0], groups[0][1], groups[0][2]) + +# This will in turn actually use the helper class AnnualVariableGroup +output_table_annual.addAnnualVariableGroup( + openstudio.model.AnnualVariableGroup(groups[1][0], groups[1][1], groups[1][2]) +) + +assert output_table_annual.numberofAnnualVariableGroups() == 2 +# This is a vector of AnnualVariableGroup +assert len(output_table_annual.annualVariableGroups()) == 2 +first_annual_group = output_table_annual.annualVariableGroups()[0] +# This returns an OptionalAnnualVariableGroup +first_annual_group_ = output_table_annual.getAnnualVariableGroup(0) +assert first_annual_group_.is_initialized() +# The equality operator is defined to check for both variableorMeterorEMSVariableorField and +# aggregationType +assert first_annual_group == first_annual_group_.get() +assert first_annual_group.variableorMeterorEMSVariableorField() == "Electricity:Facility" +assert first_annual_group.aggregationType() == "SumOrAverage" +assert first_annual_group.digitsAfterDecimal() == 3 + +output_table_annual.removeAnnualVariableGroup(1) +output_table_annual.removeAllAnnualVariableGroups() + +# There is also a batch add +annual_groups = [openstudio.model.AnnualVariableGroup(g[0], g[1], g[2]) for g in groups] +output_table_annual.addAnnualVariableGroups(annual_groups) +assert output_table_annual.numberofAnnualVariableGroups() == 2 + +# save the OpenStudio model (.osm) +model.save_openstudio_osm(osm_save_directory=None, osm_name="in.osm") diff --git a/model/simulationtests/output_tables.rb b/model/simulationtests/output_tables.rb new file mode 100644 index 00000000..666d8f14 --- /dev/null +++ b/model/simulationtests/output_tables.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +# This test aims to test the new **Unique** ModelObjects related to Output +# added in 3.0.0: +# * OutputDiagnostics, +# * OutputDebuggingData, +# * OutputJSON, and +# * OutputTableSummaryReports + +require 'openstudio' +require_relative 'lib/baseline_model' + +model = BaselineModel.new + +# make a 1 story, 100m X 50m, 1 zone building +model.add_geometry({ 'length' => 100, + 'width' => 50, + 'num_floors' => 1, + 'floor_to_floor_height' => 4, + 'plenum_height' => 0, + 'perimeter_zone_depth' => 0 }) + +# add windows at a 40% window-to-wall ratio +model.add_windows({ 'wwr' => 0.4, + 'offset' => 1, + 'application_type' => 'Above Floor' }) + +# add thermostats +model.add_thermostats({ 'heating_setpoint' => 19, + 'cooling_setpoint' => 26 }) + +# assign constructions from a local library to the walls/windows/etc. in the model +model.set_constructions + +# set whole building space type; simplified 90.1-2004 Large Office Whole Building +model.set_space_type + +# add design days to the model (Chicago) +model.add_design_days + +# In order to produce more consistent results between different runs, +# we sort the zones by names +# (There's only one here, but just in case this would be copy pasted somewhere +# else...) +zones = model.getThermalZones.sort_by { |z| z.name.to_s } +z = zones[0] +z.setUseIdealAirLoads(true) + +############################################################################### +# OUTPUT:TABLE:MONTHLY # +############################################################################### + +# Factory method to creates a OutputTableMonthly from the E+ datasets/StandardReports.idf +# Please use the OutputTableMonthly::validStandardReportNames() static method +# to look up the valid names as it will throw if it cannot find it +standardReportName = OpenStudio::Model::OutputTableMonthly.validStandardReportNames.sort.reverse[0] +output_table_monthly = OpenStudio::Model::OutputTableMonthly.fromStandardReports(model, standardReportName) + +# You can of course create one yourself +output_table_monthly = OpenStudio::Model::OutputTableMonthly.new(model) +output_table_monthly.setName('Fan Report') +output_table_monthly.setDigitsAfterDecimal(4) + +# To add a group, you can use the convenience method +# bool addMonthlyVariableGroup(std::string variableOrMeterName, std::string aggregationType = "SumOrAverage"); + +groups = [ + # variableOrMeterName, SumOrAverage + ['Fan Electricity Energy', 'SumOrAverage'], + ['Fan Rise in Air Temperature', 'SumOrAverage'], + ['Fan Electricity Rate', 'Maximum'], + ['Fan Rise in Air Temperature', 'ValueWhenMaximumOrMinimum'], +] +output_table_monthly.addMonthlyVariableGroup(groups[0][0], groups[0][1]) + +# This will in turn actually use the helper class MonthlyVariableGroup +output_table_monthly.addMonthlyVariableGroup(OpenStudio::Model::MonthlyVariableGroup.new(groups[1][0], groups[1][1])) + +raise unless output_table_monthly.numberofMonthlyVariableGroups == 2 +# This is a vector of MonthlyVariableGroup +raise unless output_table_monthly.monthlyVariableGroups.size == 2 +first_monthly_group = output_table_monthly.monthlyVariableGroups.first +# This returns an OptionalMonthlyVariableGroup +first_monthly_group_ = output_table_monthly.getMonthlyVariableGroup(0) +raise unless first_monthly_group_.is_initialized +# The equality operator is defined to check for both variableOrMeterName and +# aggregationType +raise unless first_monthly_group == first_monthly_group_.get +raise unless first_monthly_group.variableOrMeterName == 'Fan Electricity Energy' +raise unless first_monthly_group.aggregationType == 'SumOrAverage' + +output_table_monthly.removeMonthlyVariableGroup(1) +output_table_monthly.removeAllMonthlyVariableGroups + +# There is also a batch add +monthly_groups = groups.map { |g| OpenStudio::Model::MonthlyVariableGroup.new(g[0], g[1]) } +output_table_monthly.addMonthlyVariableGroups(monthly_groups) +raise unless output_table_monthly.numberofMonthlyVariableGroups == 4 + +############################################################################### +# OUTPUT:TABLE:ANNUAL # +############################################################################### + +output_table_annual = OpenStudio::Model::OutputTableAnnual.new(model) +output_table_annual.setName("Electricity Report") +output_table_annual.setSchedule(model.alwaysOnDiscreteSchedule) +output_table_annual.setFilter("Zone 1") +output_table_annual.resetFilter + +groups = [ + #variableorMeterorEMSVariableorField, aggregationType, digitsAfterDecimal + ['Electricity:Facility', 'SumOrAverage', 3], + ['Electricity:Facility', 'Maximum', 1], +] + +output_table_annual.addAnnualVariableGroup(groups[0][0], groups[0][1], groups[0][2]) + +# This will in turn actually use the helper class AnnualVariableGroup +output_table_annual.addAnnualVariableGroup(OpenStudio::Model::AnnualVariableGroup.new(groups[1][0], groups[1][1], groups[1][2])) + +raise unless output_table_annual.numberofAnnualVariableGroups == 2 +# This is a vector of AnnualVariableGroup +raise unless output_table_annual.annualVariableGroups.size == 2 +first_annual_group = output_table_annual.annualVariableGroups.first +# This returns an OptionalAnnualVariableGroup +first_annual_group_ = output_table_annual.getAnnualVariableGroup(0) +raise unless first_annual_group_.is_initialized +# The equality operator is defined to check for both variableorMeterorEMSVariableorField and +# aggregationType +raise unless first_annual_group == first_annual_group_.get +raise unless first_annual_group.variableorMeterorEMSVariableorField == 'Electricity:Facility' +raise unless first_annual_group.aggregationType == 'SumOrAverage' +raise unless first_annual_group.digitsAfterDecimal == 3 + +output_table_annual.removeAnnualVariableGroup(1) +output_table_annual.removeAllAnnualVariableGroups + +# There is also a batch add +annual_groups = groups.map { |g| OpenStudio::Model::AnnualVariableGroup.new(g[0], g[1], g[2]) } +output_table_annual.addAnnualVariableGroups(annual_groups) +raise unless output_table_annual.numberofAnnualVariableGroups == 2 + +# save the OpenStudio model (.osm) +model.save_openstudio_osm({ 'osm_save_directory' => Dir.pwd, + 'osm_name' => 'in.osm' }) diff --git a/model_tests.rb b/model_tests.rb index 9623f739..7bedb445 100644 --- a/model_tests.rb +++ b/model_tests.rb @@ -1351,6 +1351,19 @@ def test_output_objects_2_osm result = sim_test('output_objects_2.osm') end + def test_output_tables_rb + result = sim_test('output_tables.rb') + end + + def test_output_tables_py + result = sim_test('output_tables.py') + end + + # TODO: To be added in the next official release after: 3.9.0 + # def test_output_tables_osm + # result = sim_test('output_tables.osm') + # end + def test_performanceprecisiontradeoffs_rb result = sim_test('performanceprecisiontradeoffs.rb') end From cebe687e017029bc34fc7f6e445a3890773306e0 Mon Sep 17 00:00:00 2001 From: "github-rubocop-actions[bot]" Date: Mon, 17 Mar 2025 23:13:53 +0000 Subject: [PATCH 2/2] Commit rubocop --auto-correct (Ruby ruby 2.7.8p225 (2023-03-30 revision 1f4d455848) [x86_64-linux], Rubocop 0.81.0) --- model/simulationtests/output_tables.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/model/simulationtests/output_tables.rb b/model/simulationtests/output_tables.rb index 666d8f14..fb6e0a91 100644 --- a/model/simulationtests/output_tables.rb +++ b/model/simulationtests/output_tables.rb @@ -69,7 +69,7 @@ ['Fan Electricity Energy', 'SumOrAverage'], ['Fan Rise in Air Temperature', 'SumOrAverage'], ['Fan Electricity Rate', 'Maximum'], - ['Fan Rise in Air Temperature', 'ValueWhenMaximumOrMinimum'], + ['Fan Rise in Air Temperature', 'ValueWhenMaximumOrMinimum'] ] output_table_monthly.addMonthlyVariableGroup(groups[0][0], groups[0][1]) @@ -79,6 +79,7 @@ raise unless output_table_monthly.numberofMonthlyVariableGroups == 2 # This is a vector of MonthlyVariableGroup raise unless output_table_monthly.monthlyVariableGroups.size == 2 + first_monthly_group = output_table_monthly.monthlyVariableGroups.first # This returns an OptionalMonthlyVariableGroup first_monthly_group_ = output_table_monthly.getMonthlyVariableGroup(0) @@ -102,15 +103,15 @@ ############################################################################### output_table_annual = OpenStudio::Model::OutputTableAnnual.new(model) -output_table_annual.setName("Electricity Report") +output_table_annual.setName('Electricity Report') output_table_annual.setSchedule(model.alwaysOnDiscreteSchedule) -output_table_annual.setFilter("Zone 1") +output_table_annual.setFilter('Zone 1') output_table_annual.resetFilter groups = [ - #variableorMeterorEMSVariableorField, aggregationType, digitsAfterDecimal + # variableorMeterorEMSVariableorField, aggregationType, digitsAfterDecimal ['Electricity:Facility', 'SumOrAverage', 3], - ['Electricity:Facility', 'Maximum', 1], + ['Electricity:Facility', 'Maximum', 1] ] output_table_annual.addAnnualVariableGroup(groups[0][0], groups[0][1], groups[0][2]) @@ -121,6 +122,7 @@ raise unless output_table_annual.numberofAnnualVariableGroups == 2 # This is a vector of AnnualVariableGroup raise unless output_table_annual.annualVariableGroups.size == 2 + first_annual_group = output_table_annual.annualVariableGroups.first # This returns an OptionalAnnualVariableGroup first_annual_group_ = output_table_annual.getAnnualVariableGroup(0)