diff --git a/BuildResidentialHPXML/measure.xml b/BuildResidentialHPXML/measure.xml
index 7e190bf194..cddc8b6409 100644
--- a/BuildResidentialHPXML/measure.xml
+++ b/BuildResidentialHPXML/measure.xml
@@ -3,8 +3,8 @@
3.1
build_residential_hpxml
a13a8983-2b01-4930-8af2-42030b6e4233
- 74f8cfa4-d51d-49d3-a974-af3388ec0aff
- 2025-05-03T15:58:46Z
+ a35bc8f9-f4f8-4bfc-9387-701d162da262
+ 2025-05-03T17:47:18Z
2C38F48B
BuildResidentialHPXML
HPXML Builder
@@ -7713,12 +7713,6 @@
resource
D8A38780
-
- options/ducts.tsv
- tsv
- resource
- 5F1FC0B0
-
version.txt
txt
diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml
index 549275d66c..f787f697f8 100644
--- a/HPXMLtoOpenStudio/measure.xml
+++ b/HPXMLtoOpenStudio/measure.xml
@@ -3,8 +3,8 @@
3.1
hpxm_lto_openstudio
b1543b30-9465-45ff-ba04-1d1f85e763bc
- 8af045cd-29e2-42a0-87ee-cd566e934120
- 2025-05-03T15:58:56Z
+ 7425a4d6-eed7-4b64-8ccd-671060690b7b
+ 2025-05-03T20:01:42Z
D8922A73
HPXMLtoOpenStudio
HPXML to OpenStudio Translator
@@ -435,7 +435,7 @@
meta_measure.rb
rb
resource
- D7DF1BEA
+ 0A27A2C8
minitest_helper.rb
@@ -453,7 +453,7 @@
model.rb
rb
resource
- DA47D013
+ D85755A1
output.rb
diff --git a/HPXMLtoOpenStudio/resources/meta_measure.rb b/HPXMLtoOpenStudio/resources/meta_measure.rb
index 644fcd9dbe..7d6de479f6 100644
--- a/HPXMLtoOpenStudio/resources/meta_measure.rb
+++ b/HPXMLtoOpenStudio/resources/meta_measure.rb
@@ -51,22 +51,8 @@ def run_hpxml_workflow(rundir, measures, measures_dir, debug: false, run_measure
return { success: success, runner: runner }
end
- # Remove unused objects automatically added by OpenStudio?
- remove_objects = []
- if model.alwaysOnContinuousSchedule.directUseCount == 0
- remove_objects << ['Schedule:Constant', model.alwaysOnContinuousSchedule.name.to_s]
- end
- if model.alwaysOnDiscreteSchedule.directUseCount == 0
- remove_objects << ['Schedule:Constant', model.alwaysOnDiscreteSchedule.name.to_s]
- end
- if model.alwaysOffDiscreteSchedule.directUseCount == 0
- remove_objects << ['Schedule:Constant', model.alwaysOffDiscreteSchedule.name.to_s]
- end
- model.getScheduleConstants.each do |sch|
- next unless sch.directUseCount == 0
-
- remove_objects << ['Schedule:Constant', sch.name.to_s]
- end
+ # Apply reporting measure output requests
+ apply_model_output_requests(measures_dir, measures, runner, model)
# Translate model to workspace
forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new
@@ -74,9 +60,14 @@ def run_hpxml_workflow(rundir, measures, measures_dir, debug: false, run_measure
workspace = forward_translator.translateModel(model)
success = report_ft_errors_warnings(forward_translator, rundir)
- # Remove objects
- remove_objects.uniq.each do |remove_object|
- workspace.getObjectByTypeAndName(remove_object[0].to_IddObjectType, remove_object[1]).get.remove
+ # Remove unused objects automatically added by OpenStudio?
+ [model.alwaysOnContinuousSchedule,
+ model.alwaysOnDiscreteSchedule,
+ model.alwaysOffDiscreteSchedule].each do |sch|
+ # Don't know why we need to check for AdditionalProperties too, but it works
+ if sch.directUseCount == 0 || sch.sources.all? { |s| s.to_AdditionalProperties.is_initialized }
+ workspace.getObjectByTypeAndName('Schedule:Constant'.to_IddObjectType, sch.name.to_s).get.remove
+ end
end
if not success
@@ -250,7 +241,33 @@ def get_full_measure_path(measures_dir, measure_name, runner)
register_error("Cannot find measure #{measure_name} in any of the measures_dirs: #{measures_dirs.join(', ')}.", runner)
end
-# Apply OpenStudio measures and arguments (i.e., "energyPlusOutputRequests" method) corresponding to a provided Hash.
+# Apply reporting measure output requests (i.e., "modelOutputRequests" method).
+#
+# @param measures_dir [String or Array] Parent directory path(s) of all OpenStudio-HPXML measures
+# @param measures [Hash] Map of OpenStudio-HPXML measure directory name => List of measure argument hashes
+# @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings
+# @param model [OpenStudio::Model::Model] OpenStudio Model object
+# @return [Boolean] True if EnergyPlus output requests have been applied successfully
+def apply_model_output_requests(measures_dir, measures, runner, model)
+ # Call each measure in the specified order
+ measures.keys.each do |measure_subdir|
+ # Gather measure arguments and call measure
+ full_measure_path = File.join(measures_dir, measure_subdir, 'measure.rb')
+ check_file_exists(full_measure_path, runner)
+ measure = get_measure_instance(full_measure_path)
+ measures[measure_subdir].each do |args|
+ next unless measure.class.superclass.name.to_s == 'OpenStudio::Measure::ReportingMeasure'
+
+ argument_map = get_argument_map(model, measure, args, measure_subdir, runner)
+ runner.setLastOpenStudioModel(model)
+ measure.modelOutputRequests(model, runner, argument_map)
+ end
+ end
+
+ return true
+end
+
+# Apply reporting measure output requests (i.e., "energyPlusOutputRequests" method).
#
# @param measures_dir [String or Array] Parent directory path(s) of all OpenStudio-HPXML measures
# @param measures [Hash] Map of OpenStudio-HPXML measure directory name => List of measure argument hashes
diff --git a/HPXMLtoOpenStudio/resources/model.rb b/HPXMLtoOpenStudio/resources/model.rb
index a491567c96..a71ff59fe8 100644
--- a/HPXMLtoOpenStudio/resources/model.rb
+++ b/HPXMLtoOpenStudio/resources/model.rb
@@ -722,7 +722,7 @@ def self.add_schedule_type_limits(model, schedule:, limits:)
return stl
end
- # Adds an EnergyManagementSystemSensor to the OpenStudio model.
+ # Adds an EnergyManagementSystemSensor object to the OpenStudio model.
#
# The EnergyManagementSystemSensor object gets information during the simulation
# that can be used in custom calculations.
@@ -739,7 +739,7 @@ def self.add_ems_sensor(model, name:, output_var_or_meter_name:, key_name:)
return sensor
end
- # Adds an EnergyManagementSystemGlobalVariable to the OpenStudio model.
+ # Adds an EnergyManagementSystemGlobalVariable object to the OpenStudio model.
#
# The EnergyManagementSystemGlobalVariable object allows an EMS variable to be
# global such that it can be used across EMS programs/subroutines.
@@ -751,7 +751,7 @@ def self.add_ems_global_var(model, var_name:)
return OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, ems_friendly_name(var_name))
end
- # Adds an EnergyManagementSystemTrendVariable to the OpenStudio model.
+ # Adds an EnergyManagementSystemTrendVariable object to the OpenStudio model.
#
# The EnergyManagementSystemTrendVariable object creates a global EMS variable
# that stores the recent history of an EMS variable for use in a calculation.
@@ -767,7 +767,7 @@ def self.add_ems_trend_var(model, ems_object:, num_timesteps_logged:)
return tvar
end
- # Adds an EnergyManagementSystemInternalVariable to the OpenStudio model.
+ # Adds an EnergyManagementSystemInternalVariable object to the OpenStudio model.
#
# The EnergyManagementSystemInternalVariable object is used to obtain static data from
# elsewhere in the model.
@@ -784,7 +784,7 @@ def self.add_ems_internal_var(model, name:, model_object:, type:)
return ivar
end
- # Adds an EnergyManagementSystemActuator to the OpenStudio model.
+ # Adds an EnergyManagementSystemActuator object to the OpenStudio model.
#
# The EnergyManagementSystemActuator object specifies the properties or controls
# of an EnergyPlus object that is to be overridden during the simulation.
@@ -803,7 +803,7 @@ def self.add_ems_actuator(name:, model_object:, comp_type_and_control:)
return act
end
- # Adds an EnergyManagementSystemProgram to the OpenStudio model.
+ # Adds an EnergyManagementSystemProgram object to the OpenStudio model.
#
# The EnergyManagementSystemProgram object allows custom calculations to be
# performed within the EnergyPlus simulation in order to override the properties
@@ -820,7 +820,7 @@ def self.add_ems_program(model, name:, lines: nil)
return prg
end
- # Adds an EnergyManagementSystemSubroutine to the OpenStudio model.
+ # Adds an EnergyManagementSystemSubroutine object to the OpenStudio model.
#
# The EnergyManagementSystemSubroutine object allows EMS code to be reused
# across multiple EMS programs.
@@ -836,7 +836,7 @@ def self.add_ems_subroutine(model, name:, lines: nil)
return sbrt
end
- # Adds an EnergyManagementSystemProgramCallingManager to the OpenStudio model.
+ # Adds an EnergyManagementSystemProgramCallingManager object to the OpenStudio model.
#
# The EnergyManagementSystemProgramCallingManager object is used to specify when
# an EMS program is run during the simulation.
@@ -856,6 +856,99 @@ def self.add_ems_program_calling_manager(model, name:, calling_point:, ems_progr
return pcm
end
+ # Adds an EnergyManagementSystemOutputVariable object to the OpenStudio model.
+ #
+ # The EnergyManagementSystemOutputVariable object allows generating output for
+ # an EMS variable; the object can be referenced by an OutputVariable object.
+ #
+ # @param model [OpenStudio::Model::Model] OpenStudio Model object
+ # @param name [String] User-defined name for the new output variable
+ # @param ems_variable_name [String] EMS variable name to be output
+ # @param type_of_data [String] The nature of the variable ('averaged' or 'summed')
+ # @param update_frequency [String] Timestep the variable is associated with ('ZoneTimestep' or 'SystemTimestep')
+ # @param ems_program_or_subroutine [OpenStudio::Model::EnergyManagementSystemProgram or EnergyManagementSystemSubroutine] The EMS program/subroutine with the EMS variable
+ # @param units [String] The units for the output variable in standard EnergyPlus units
+ # @return [OpenStudio::Model::EnergyManagementSystemOutputVariable] The model object
+ def self.add_ems_output_variable(model, name:, ems_variable_name:, type_of_data:, update_frequency:, ems_program_or_subroutine:, units:)
+ ov = OpenStudio::Model::EnergyManagementSystemOutputVariable.new(model, ems_variable_name)
+ ov.setName(name)
+ ov.setTypeOfDataInVariable(type_of_data)
+ ov.setUpdateFrequency(update_frequency)
+ ov.setEMSProgramOrSubroutineName(ems_program_or_subroutine)
+ ov.setUnits(units)
+ return ov
+ end
+
+ # Adds an OutputVariable object to the OpenStudio model. If there is already an
+ # identical existing object on the model, returns that instead.
+ #
+ # @param model [OpenStudio::Model::Model] OpenStudio Model object
+ # @param key_value [String] The specific object reference for reporting
+ # @param variable_name [String] The variable name as shown in the eplusout.rdd file
+ # @param reporting_frequency [String] Output reporting frequency ('detailed', 'timestep', 'hourly', 'daily', 'monthly', 'runperiod', or 'annual')
+ # @return [OpenStudio::Model::OutputVariable] The model object
+ def self.add_output_variable(model, key_value:, variable_name:, reporting_frequency:)
+ model.getOutputVariables.each do |ov|
+ next unless ov.variableName == variable_name
+ next unless ov.keyValue == key_value
+ next unless ov.reportingFrequency == reporting_frequency
+
+ return ov # Duplicate of existing object
+ end
+ ov = OpenStudio::Model::OutputVariable.new(variable_name, model)
+ ov.setKeyValue(key_value)
+ ov.setReportingFrequency(reporting_frequency)
+ return ov
+ end
+
+ # Adds an OutputMeter object to the OpenStudio model. If there is already an
+ # identical existing object on the model, returns that instead.
+ #
+ # @param model [OpenStudio::Model::Model] OpenStudio Model object
+ # @param meter_name [String] The meter name as shown in the eplusout.mdd file
+ # @param reporting_frequency [String] Output reporting frequency ('detailed', 'timestep', 'hourly', 'daily', 'monthly', 'runperiod', or 'annual')
+ # @return [OpenStudio::Model::OutputMeter] The model object
+ def self.add_output_meter(model, meter_name:, reporting_frequency:)
+ model.getOutputMeters.each do |om|
+ next unless om.name == meter_name
+ next unless om.reportingFrequency == reporting_frequency
+
+ return om # Duplicate of existing object
+ end
+ om = OpenStudio::Model::OutputMeter.new(model)
+ om.setName(meter_name)
+ om.setReportingFrequency(reporting_frequency)
+ return om
+ end
+
+ # Adds an OutputTableMonthly to the OpenStudio model. If there is already an
+ # identical existing object on the model, returns that instead.
+ #
+ # @param model [OpenStudio::Model::Model] OpenStudio Model object
+ # @param name [String] Name for the output table
+ # @param digits_after_decimal [Integer] Number of digits after the decimal point
+ # @param output_var_or_meter_name [String] EnergyPlus Output:Variable or Output:Meter name
+ # @param aggregation_type [String] Aggregation type (SumOrAverage, Maximum, Minimum, etc.)
+ # @return [OpenStudio::Model::OutputTableMonthly] The model object
+ def self.add_output_table_monthly(model, name:, digits_after_decimal: 2, output_var_or_meter_name:, aggregation_type:)
+ model.getOutputTableMonthlys.each do |otm|
+ next unless otm.name.to_s == name
+ next unless otm.digitsAfterDecimal == digits_after_decimal
+ next unless otm.numberofMonthlyVariableGroups == 1
+
+ first_group = otm.getMonthlyVariableGroup(0).get
+ next unless first_group.variableOrMeterName == output_var_or_meter_name
+ next unless first_group.aggregationType == aggregation_type
+
+ return otm # Duplicate of existing object
+ end
+ otm = OpenStudio::Model::OutputTableMonthly.new(model)
+ otm.setName(name)
+ otm.setDigitsAfterDecimal(digits_after_decimal)
+ otm.addMonthlyVariableGroup(output_var_or_meter_name, aggregation_type)
+ return otm
+ end
+
# Converts existing string to EMS friendly string.
#
# Source: openstudio-standards
diff --git a/ReportSimulationOutput/measure.rb b/ReportSimulationOutput/measure.rb
index 6b02cecb22..398a14351a 100644
--- a/ReportSimulationOutput/measure.rb
+++ b/ReportSimulationOutput/measure.rb
@@ -342,36 +342,29 @@ def get_arguments(runner, arguments, user_arguments)
return args
end
- # Return a vector of IdfObject's to request EnergyPlus objects needed by the run method.
+ # Adds OpenStudio model objects to requests desired outputs.
#
+ # @param model [OpenStudio::Model::Model] OpenStudio Model object
# @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings
# @param user_arguments [OpenStudio::Measure::OSArgumentMap] OpenStudio measure arguments
- # @return [Array] array of OpenStudio IdfObject objects
- def energyPlusOutputRequests(runner, user_arguments)
- super(runner, user_arguments)
+ # @return [Boolean] Success
+ def modelOutputRequests(model, runner, user_arguments)
+ return false if runner.halted
- result = OpenStudio::IdfObjectVector.new
- return result if runner.halted
-
- model = runner.lastOpenStudioModel
- if model.empty?
- return result
- end
-
- @model = model.get
+ @model = model
# use the built-in error checking
- if !runner.validateUserArguments(arguments(@model), user_arguments)
- return result
+ if !runner.validateUserArguments(arguments(model), user_arguments)
+ return false
end
- unmet_hours_program = @model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeUnmetHoursProgram }
- total_loads_program = @model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeTotalLoadsProgram }
- comp_loads_program = @model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeComponentLoadsProgram }
- total_airflows_program = @model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeTotalAirflowsProgram }
- unmet_driving_hrs_program = @model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeBEVDischargeProgram }
- heated_zones = eval(@model.getBuilding.additionalProperties.getFeatureAsString('heated_zones').get)
- cooled_zones = eval(@model.getBuilding.additionalProperties.getFeatureAsString('cooled_zones').get)
+ unmet_hours_program = model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeUnmetHoursProgram }
+ total_loads_program = model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeTotalLoadsProgram }
+ comp_loads_program = model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeComponentLoadsProgram }
+ total_airflows_program = model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeTotalAirflowsProgram }
+ unmet_driving_hrs_program = model.getEnergyManagementSystemPrograms.find { |p| p.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants::ObjectTypeBEVDischargeProgram }
+ heated_zones = eval(model.getBuilding.additionalProperties.getFeatureAsString('heated_zones').get)
+ cooled_zones = eval(model.getBuilding.additionalProperties.getFeatureAsString('cooled_zones').get)
args = get_arguments(runner, arguments(model), user_arguments)
@@ -392,19 +385,19 @@ def energyPlusOutputRequests(runner, user_arguments)
@fuels.each do |(_fuel_type, _total_or_net), fuel|
next if fuel.meter.nil?
- result << OpenStudio::IdfObject.load("Output:Meter,#{fuel.meter},runperiod;").get
+ Model.add_output_meter(model, meter_name: fuel.meter, reporting_frequency: 'runperiod')
if args[:include_timeseries_fuel_consumptions]
- result << OpenStudio::IdfObject.load("Output:Meter,#{fuel.meter},#{args[:timeseries_frequency]};").get
+ Model.add_output_meter(model, meter_name: fuel.meter, reporting_frequency: args[:timeseries_frequency])
end
end
if has_electricity_production || has_electricity_storage
- result << OpenStudio::IdfObject.load('Output:Meter,ElectricityProduced:Facility,runperiod;').get # Used for error checking
+ Model.add_output_meter(model, meter_name: 'ElectricityProduced:Facility', reporting_frequency: 'runperiod') # Used for error checking
end
if has_electricity_storage
- result << OpenStudio::IdfObject.load('Output:Meter,ElectricStorage:ElectricityProduced,runperiod;').get # Used for error checking
+ Model.add_output_meter(model, meter_name: 'ElectricStorage:ElectricityProduced', reporting_frequency: 'runperiod') # Used for error checking
if args[:include_timeseries_fuel_consumptions]
- result << OpenStudio::IdfObject.load("Output:Meter,ElectricStorage:ElectricityProduced,#{args[:timeseries_frequency]};").get
+ Model.add_output_meter(model, meter_name: 'ElectricStorage:ElectricityProduced', reporting_frequency: args[:timeseries_frequency])
end
# Resilience
@@ -413,12 +406,12 @@ def energyPlusOutputRequests(runner, user_arguments)
if args[:timeseries_frequency] != EPlus::TimeseriesFrequencyTimestep
resilience_frequency = EPlus::TimeseriesFrequencyHourly
end
- result << OpenStudio::IdfObject.load("Output:Meter,Electricity:Facility,#{resilience_frequency};").get
- result << OpenStudio::IdfObject.load("Output:Meter,ElectricityProduced:Facility,#{resilience_frequency};").get
- result << OpenStudio::IdfObject.load("Output:Meter,ElectricStorage:ElectricityProduced,#{resilience_frequency};").get
+ Model.add_output_meter(model, meter_name: 'Electricity:Facility', reporting_frequency: resilience_frequency)
+ Model.add_output_meter(model, meter_name: 'ElectricityProduced:Facility', reporting_frequency: resilience_frequency)
+ Model.add_output_meter(model, meter_name: 'ElectricStorage:ElectricityProduced', reporting_frequency: resilience_frequency)
@resilience.values.each do |resilience|
resilience.variables.each do |_sys_id, varkey, var|
- result << OpenStudio::IdfObject.load("Output:Variable,#{varkey},#{var},#{resilience_frequency};").get
+ Model.add_output_variable(model, key_value: varkey, variable_name: var, reporting_frequency: resilience_frequency)
end
end
end
@@ -426,30 +419,30 @@ def energyPlusOutputRequests(runner, user_arguments)
# End Use/Hot Water Use/Ideal Load outputs
{ @end_uses => args[:include_timeseries_end_use_consumptions],
- @hot_water_uses => args[:include_timeseries_hot_water_uses] }.each do |uses, include_ts|
+ @hot_water_uses => args[:include_timeseries_hot_water_uses] }.each do |uses, include_timeseries|
uses.each do |key, use|
use.variables.each do |_sys_id, varkey, var|
- result << OpenStudio::IdfObject.load("Output:Variable,#{varkey},#{var},runperiod;").get
- if include_ts
- result << OpenStudio::IdfObject.load("Output:Variable,#{varkey},#{var},#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: varkey, variable_name: var, reporting_frequency: 'runperiod')
+ if include_timeseries
+ Model.add_output_variable(model, key_value: varkey, variable_name: var, reporting_frequency: args[:timeseries_frequency])
end
next unless use.is_a?(EndUse)
fuel_type, _end_use = key
if fuel_type == FT::Elec && args[:include_hourly_electric_end_use_consumptions]
- result << OpenStudio::IdfObject.load("Output:Variable,#{varkey},#{var},hourly;").get
+ Model.add_output_variable(model, key_value: varkey, variable_name: var, reporting_frequency: 'hourly')
end
end
- use.meters.each do |_sys_id, _varkey, var|
- result << OpenStudio::IdfObject.load("Output:Meter,#{var},runperiod;").get
- if include_ts
- result << OpenStudio::IdfObject.load("Output:Meter,#{var},#{args[:timeseries_frequency]};").get
+ use.meters.each do |_, _, meter|
+ Model.add_output_meter(model, meter_name: meter, reporting_frequency: 'runperiod')
+ if include_timeseries
+ Model.add_output_meter(model, meter_name: meter, reporting_frequency: args[:timeseries_frequency])
end
next unless use.is_a?(EndUse)
fuel_type, _end_use = key
if fuel_type == FT::Elec && args[:include_hourly_electric_end_use_consumptions]
- result << OpenStudio::IdfObject.load("Output:Meter,#{var},hourly;").get
+ Model.add_output_meter(model, meter_name: meter, reporting_frequency: 'hourly')
end
end
end
@@ -457,13 +450,13 @@ def energyPlusOutputRequests(runner, user_arguments)
# Peak Fuel outputs (annual only)
@peak_fuels.values.each do |peak_fuel|
- result << OpenStudio::IdfObject.load("Output:Table:Monthly,#{peak_fuel.report},2,#{peak_fuel.meter},Maximum;").get
+ Model.add_output_table_monthly(model, name: peak_fuel.report, output_var_or_meter_name: peak_fuel.meter, aggregation_type: 'Maximum')
end
# Peak Load outputs (annual only)
@peak_loads.values.each do |peak_load|
- result << OpenStudio::IdfObject.load("EnergyManagementSystem:OutputVariable,#{peak_load.ems_variable}_peakload_outvar,#{peak_load.ems_variable},Summed,ZoneTimestep,#{total_loads_program.name},J;").get
- result << OpenStudio::IdfObject.load("Output:Table:Monthly,#{peak_load.report},2,#{peak_load.ems_variable}_peakload_outvar,Maximum;").get
+ ems_ov = Model.add_ems_output_variable(model, name: "#{peak_load.ems_variable}_peakload_outvar", ems_variable_name: peak_load.ems_variable, type_of_data: 'Summed', update_frequency: 'ZoneTimestep', ems_program_or_subroutine: total_loads_program, units: 'J')
+ Model.add_output_table_monthly(model, name: peak_load.report, output_var_or_meter_name: ems_ov.name.to_s, aggregation_type: 'Maximum')
end
# Unmet Hours (annual only)
@@ -472,11 +465,11 @@ def energyPlusOutputRequests(runner, user_arguments)
ems_program = key == UHT::Driving ? unmet_driving_hrs_program : unmet_hours_program
- result << OpenStudio::IdfObject.load("EnergyManagementSystem:OutputVariable,#{unmet_hour.ems_variable}_annual_outvar,#{unmet_hour.ems_variable},Summed,ZoneTimestep,#{ems_program.name},hr;").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{unmet_hour.ems_variable}_annual_outvar,runperiod;").get
+ ems_ov = Model.add_ems_output_variable(model, name: "#{unmet_hour.ems_variable}_annual_outvar", ems_variable_name: unmet_hour.ems_variable, type_of_data: 'Summed', update_frequency: 'ZoneTimestep', ems_program_or_subroutine: ems_program, units: 'hr')
+ Model.add_output_variable(model, key_value: '*', variable_name: ems_ov.name.to_s, reporting_frequency: 'runperiod')
if args[:include_timeseries_unmet_hours]
- result << OpenStudio::IdfObject.load("EnergyManagementSystem:OutputVariable,#{unmet_hour.ems_variable}_timeseries_outvar,#{unmet_hour.ems_variable},Summed,ZoneTimestep,#{ems_program.name},hr;").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{unmet_hour.ems_variable}_timeseries_outvar,#{args[:timeseries_frequency]};").get
+ ems_ov = Model.add_ems_output_variable(model, name: "#{unmet_hour.ems_variable}_timeseries_outvar", ems_variable_name: unmet_hour.ems_variable, type_of_data: 'Summed', update_frequency: 'ZoneTimestep', ems_program_or_subroutine: ems_program, units: 'hr')
+ Model.add_output_variable(model, key_value: '*', variable_name: ems_ov.name.to_s, reporting_frequency: args[:timeseries_frequency])
end
end
@@ -484,35 +477,36 @@ def energyPlusOutputRequests(runner, user_arguments)
@component_loads.values.each do |comp_load|
next if comp_loads_program.nil?
- result << OpenStudio::IdfObject.load("EnergyManagementSystem:OutputVariable,#{comp_load.ems_variable}_annual_outvar,#{comp_load.ems_variable},Summed,ZoneTimestep,#{comp_loads_program.name},J;").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{comp_load.ems_variable}_annual_outvar,runperiod;").get
+ ems_ov = Model.add_ems_output_variable(model, name: "#{comp_load.ems_variable}_annual_outvar", ems_variable_name: comp_load.ems_variable, type_of_data: 'Summed', update_frequency: 'ZoneTimestep', ems_program_or_subroutine: comp_loads_program, units: 'J')
+ Model.add_output_variable(model, key_value: '*', variable_name: ems_ov.name.to_s, reporting_frequency: 'runperiod')
if args[:include_timeseries_component_loads]
- result << OpenStudio::IdfObject.load("EnergyManagementSystem:OutputVariable,#{comp_load.ems_variable}_timeseries_outvar,#{comp_load.ems_variable},Summed,ZoneTimestep,#{comp_loads_program.name},J;").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{comp_load.ems_variable}_timeseries_outvar,#{args[:timeseries_frequency]};").get
+ ems_ov = Model.add_ems_output_variable(model, name: "#{comp_load.ems_variable}_timeseries_outvar", ems_variable_name: comp_load.ems_variable, type_of_data: 'Summed', update_frequency: 'ZoneTimestep', ems_program_or_subroutine: comp_loads_program, units: 'J')
+ Model.add_output_variable(model, key_value: '*', variable_name: ems_ov.name.to_s, reporting_frequency: args[:timeseries_frequency])
end
end
# Total Load outputs
@loads.values.each do |load|
if not load.ems_variable.nil?
- result << OpenStudio::IdfObject.load("EnergyManagementSystem:OutputVariable,#{load.ems_variable}_annual_outvar,#{load.ems_variable},Summed,ZoneTimestep,#{total_loads_program.name},J;").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{load.ems_variable}_annual_outvar,runperiod;").get
+ ems_ov = Model.add_ems_output_variable(model, name: "#{load.ems_variable}_annual_outvar", ems_variable_name: load.ems_variable, type_of_data: 'Summed', update_frequency: 'ZoneTimestep', ems_program_or_subroutine: total_loads_program, units: 'J')
+ Model.add_output_variable(model, key_value: '*', variable_name: ems_ov.name.to_s, reporting_frequency: 'runperiod')
if args[:include_timeseries_total_loads]
- result << OpenStudio::IdfObject.load("EnergyManagementSystem:OutputVariable,#{load.ems_variable}_timeseries_outvar,#{load.ems_variable},Summed,ZoneTimestep,#{total_loads_program.name},J;").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{load.ems_variable}_timeseries_outvar,#{args[:timeseries_frequency]};").get
+ ems_ov = Model.add_ems_output_variable(model, name: "#{load.ems_variable}_timeseries_outvar", ems_variable_name: load.ems_variable, type_of_data: 'Summed', update_frequency: 'ZoneTimestep', ems_program_or_subroutine: total_loads_program, units: 'J')
+ Model.add_output_variable(model, key_value: '*', variable_name: ems_ov.name.to_s, reporting_frequency: args[:timeseries_frequency])
end
end
load.variables.each do |_sys_id, varkey, var|
- result << OpenStudio::IdfObject.load("Output:Variable,#{varkey},#{var},runperiod;").get
+ Model.add_output_variable(model, key_value: varkey, variable_name: var, reporting_frequency: 'runperiod')
if args[:include_timeseries_total_loads]
- result << OpenStudio::IdfObject.load("Output:Variable,#{varkey},#{var},#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: varkey, variable_name: var, reporting_frequency: args[:timeseries_frequency])
end
end
end
# Temperature outputs (timeseries only)
if args[:include_timeseries_zone_temperatures]
- result << OpenStudio::IdfObject.load("Output:Variable,*,Zone Mean Air Temperature,#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: '*', variable_name: 'Zone Mean Air Temperature', reporting_frequency: args[:timeseries_frequency])
+
# For reporting temperature-scheduled spaces timeseries temperatures.
keys = [HPXML::LocationOtherHeatedSpace,
HPXML::LocationOtherMultifamilyBufferSpace,
@@ -524,52 +518,53 @@ def energyPlusOutputRequests(runner, user_arguments)
schedules = @model.getScheduleConstants.select { |sch| sch.additionalProperties.getFeatureAsString('ObjectType').to_s == key }
next if schedules.empty?
- result << OpenStudio::IdfObject.load("Output:Variable,#{schedules[0].name.to_s.upcase},Schedule Value,#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: schedules[0].name.to_s.upcase, variable_name: 'Schedule Value', reporting_frequency: args[:timeseries_frequency])
end
+
# Also report thermostat setpoints
heated_zones.each do |heated_zone|
- result << OpenStudio::IdfObject.load("Output:Variable,#{heated_zone.upcase},Zone Thermostat Heating Setpoint Temperature,#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: heated_zone.upcase, variable_name: 'Zone Thermostat Heating Setpoint Temperature', reporting_frequency: args[:timeseries_frequency])
end
cooled_zones.each do |cooled_zone|
- result << OpenStudio::IdfObject.load("Output:Variable,#{cooled_zone.upcase},Zone Thermostat Cooling Setpoint Temperature,#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: cooled_zone.upcase, variable_name: 'Zone Thermostat Cooling Setpoint Temperature', reporting_frequency: args[:timeseries_frequency])
end
end
# Detailed air condition outputs (timeseries only)
if args[:include_timeseries_zone_conditions]
- result << OpenStudio::IdfObject.load("Output:Variable,*,Zone Air Humidity Ratio,#{args[:timeseries_frequency]};").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,Zone Air Relative Humidity,#{args[:timeseries_frequency]};").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,Zone Mean Air Dewpoint Temperature,#{args[:timeseries_frequency]};").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,Zone Mean Radiant Temperature,#{args[:timeseries_frequency]};").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,Zone Operative Temperature,#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: '*', variable_name: 'Zone Air Humidity Ratio', reporting_frequency: args[:timeseries_frequency])
+ Model.add_output_variable(model, key_value: '*', variable_name: 'Zone Air Relative Humidity', reporting_frequency: args[:timeseries_frequency])
+ Model.add_output_variable(model, key_value: '*', variable_name: 'Zone Mean Air Dewpoint Temperature', reporting_frequency: args[:timeseries_frequency])
+ Model.add_output_variable(model, key_value: '*', variable_name: 'Zone Mean Radiant Temperature', reporting_frequency: args[:timeseries_frequency])
+ Model.add_output_variable(model, key_value: '*', variable_name: 'Zone Operative Temperature', reporting_frequency: args[:timeseries_frequency])
end
# Airflow outputs (timeseries only)
if args[:include_timeseries_airflows]
@airflows.each do |_airflow_type, airflow|
- result << OpenStudio::IdfObject.load("EnergyManagementSystem:OutputVariable,#{airflow.ems_variable}_timeseries_outvar,#{airflow.ems_variable},Averaged,ZoneTimestep,#{total_airflows_program.name},m^3/s;").get
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{airflow.ems_variable}_timeseries_outvar,#{args[:timeseries_frequency]};").get
+ ems_ov = Model.add_ems_output_variable(model, name: "#{airflow.ems_variable}_timeseries_outvar", ems_variable_name: airflow.ems_variable, type_of_data: 'Averaged', update_frequency: 'ZoneTimestep', ems_program_or_subroutine: total_airflows_program, units: 'm^3/s')
+ Model.add_output_variable(model, key_value: '*', variable_name: ems_ov.name.to_s, reporting_frequency: args[:timeseries_frequency])
end
end
# Weather outputs (timeseries only)
if args[:include_timeseries_weather]
@weather.values.each do |weather_data|
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{weather_data.variable},#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: '*', variable_name: weather_data.variable, reporting_frequency: args[:timeseries_frequency])
end
end
# Output variables (timeseries only)
@output_variables_requests.each do |output_variable_name|
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{output_variable_name},#{args[:timeseries_frequency]};").get
+ Model.add_output_variable(model, key_value: '*', variable_name: output_variable_name, reporting_frequency: args[:timeseries_frequency])
end
# Output meters (timeseries only)
@output_meters_requests.each do |output_meter_name|
- result << OpenStudio::IdfObject.load("Output:Meter,#{output_meter_name},#{args[:timeseries_frequency]};").get
+ Model.add_output_meter(model, meter_name: output_meter_name, reporting_frequency: args[:timeseries_frequency])
end
- return result.uniq
+ return true
end
# Define what happens when the measure is run.
diff --git a/ReportSimulationOutput/measure.xml b/ReportSimulationOutput/measure.xml
index 4f82961f5e..c9af603e5d 100644
--- a/ReportSimulationOutput/measure.xml
+++ b/ReportSimulationOutput/measure.xml
@@ -3,8 +3,8 @@
3.1
report_simulation_output
df9d170c-c21a-4130-866d-0d46b06073fd
- 52b3b0d6-345f-46a2-93ab-34f9705f6907
- 2025-04-25T21:17:08Z
+ 1a03c52f-33c2-4d29-b571-5a25b9c7c755
+ 2025-05-03T16:54:19Z
9BF1E6AC
ReportSimulationOutput
HPXML Simulation Output Report
@@ -1991,7 +1991,7 @@
measure.rb
rb
script
- 9ACFB98A
+ 645A80FD
test_report_sim_output.rb
diff --git a/ReportUtilityBills/measure.rb b/ReportUtilityBills/measure.rb
index f6bda70cb0..c44028c5ae 100644
--- a/ReportUtilityBills/measure.rb
+++ b/ReportUtilityBills/measure.rb
@@ -149,27 +149,20 @@ def check_for_next_type_warnings(utility_bill_scenario)
return warnings.uniq
end
- # Return a vector of IdfObject's to request EnergyPlus objects needed by the run method.
+ # Adds OpenStudio model objects to requests desired outputs.
#
+ # @param model [OpenStudio::Model::Model] OpenStudio Model object
# @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings
# @param user_arguments [OpenStudio::Measure::OSArgumentMap] OpenStudio measure arguments
- # @return [Array] array of OpenStudio IdfObject objects
- def energyPlusOutputRequests(runner, user_arguments)
- super(runner, user_arguments)
-
- result = OpenStudio::IdfObjectVector.new
- return result if runner.halted
+ # @return [Boolean] Success
+ def modelOutputRequests(model, runner, user_arguments)
+ return false if runner.halted
- model = runner.lastOpenStudioModel
- if model.empty?
- return result
- end
-
- @model = model.get
+ @model = model
# use the built-in error checking
if !runner.validateUserArguments(arguments(model), user_arguments)
- return result
+ return false
end
hpxml_defaults_path = @model.getBuilding.additionalProperties.getFeatureAsString('hpxml_defaults_path').get
@@ -181,12 +174,12 @@ def energyPlusOutputRequests(runner, user_arguments)
if @hpxml_header.utility_bill_scenarios.has_detailed_electric_rates
uses_unit_multipliers = @hpxml_buildings.count { |hpxml_bldg| hpxml_bldg.building_construction.number_of_units > 1 } > 0
if uses_unit_multipliers || (@hpxml_buildings.size > 1 && hpxml.header.whole_sfa_or_mf_building_sim)
- return result
+ return false
end
end
warnings = check_for_return_type_warnings()
- return result if !warnings.empty?
+ return false if !warnings.empty?
fuels = setup_fuel_outputs()
@@ -209,13 +202,13 @@ def energyPlusOutputRequests(runner, user_arguments)
next unless has_fuel[hpxml_fuel_map[fuel_type]]
next if is_production && !has_pv # we don't need to request this meter if there isn't pv
- result << OpenStudio::IdfObject.load("Output:Meter,#{fuel.meter},monthly;").get
+ Model.add_output_meter(model, meter_name: fuel.meter, reporting_frequency: 'monthly')
if fuel_type == FT::Elec && @hpxml_header.utility_bill_scenarios.has_detailed_electric_rates
- result << OpenStudio::IdfObject.load("Output:Meter,#{fuel.meter},hourly;").get
+ Model.add_output_meter(model, meter_name: fuel.meter, reporting_frequency: 'hourly')
end
end
- return result.uniq
+ return true
end
# Register to the runner each warning.
diff --git a/ReportUtilityBills/measure.xml b/ReportUtilityBills/measure.xml
index 7fd4ec4c7e..1424f28a7c 100644
--- a/ReportUtilityBills/measure.xml
+++ b/ReportUtilityBills/measure.xml
@@ -3,8 +3,8 @@
3.1
report_utility_bills
ca88a425-e59a-4bc4-af51-c7e7d1e960fe
- 61a6d525-77d4-47a5-8a6d-683afcd13b21
- 2025-04-25T17:37:30Z
+ 6f3f45c7-e7cf-4732-9fcb-99af569a557c
+ 2025-05-03T16:54:22Z
15BF4E57
ReportUtilityBills
Utility Bills Report
@@ -180,7 +180,7 @@
measure.rb
rb
script
- D50684D8
+ E6457A7B
detailed_rates/README.md
diff --git a/workflow/template-build-and-run-hpxml-with-stochastic-occupancy.osw b/workflow/template-build-and-run-hpxml-with-stochastic-occupancy.osw
index a66ff083cd..e312a799ad 100644
--- a/workflow/template-build-and-run-hpxml-with-stochastic-occupancy.osw
+++ b/workflow/template-build-and-run-hpxml-with-stochastic-occupancy.osw
@@ -76,7 +76,8 @@
"window_left_wwr": 0,
"window_right_wwr": 0,
"window_shgc": 0.45,
- "window_ufactor": 0.33
+ "window_ufactor": 0.33,
+ "utility_bill_scenario_names": "Bills"
},
"measure_dir_name": "BuildResidentialHPXML"
},
diff --git a/workflow/template-build-hpxml.osw b/workflow/template-build-hpxml.osw
index e88b41bb62..d98ac5781b 100644
--- a/workflow/template-build-hpxml.osw
+++ b/workflow/template-build-hpxml.osw
@@ -76,7 +76,8 @@
"window_left_wwr": 0,
"window_right_wwr": 0,
"window_shgc": 0.45,
- "window_ufactor": 0.33
+ "window_ufactor": 0.33,
+ "utility_bill_scenario_names": "Bills"
},
"measure_dir_name": "BuildResidentialHPXML"
}