diff --git a/resources/measures/add_blinds_to_selected_windows/tests/add_blinds_to_selected_windows_test.rb b/resources/measures/add_blinds_to_selected_windows/tests/add_blinds_to_selected_windows_test.rb
index 702027e76..18ed12e93 100644
--- a/resources/measures/add_blinds_to_selected_windows/tests/add_blinds_to_selected_windows_test.rb
+++ b/resources/measures/add_blinds_to_selected_windows/tests/add_blinds_to_selected_windows_test.rb
@@ -27,6 +27,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/add_hvac_nighttime_operation_variability/tests/add_hvac_nighttime_operation_variability_test.rb b/resources/measures/add_hvac_nighttime_operation_variability/tests/add_hvac_nighttime_operation_variability_test.rb
index 72301ac9b..491f462d6 100644
--- a/resources/measures/add_hvac_nighttime_operation_variability/tests/add_hvac_nighttime_operation_variability_test.rb
+++ b/resources/measures/add_hvac_nighttime_operation_variability/tests/add_hvac_nighttime_operation_variability_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/add_thermostat_setpoint_variability/tests/add_thermostat_setpoint_variability_test.rb b/resources/measures/add_thermostat_setpoint_variability/tests/add_thermostat_setpoint_variability_test.rb
index 6ac22710b..0d86bdbce 100644
--- a/resources/measures/add_thermostat_setpoint_variability/tests/add_thermostat_setpoint_variability_test.rb
+++ b/resources/measures/add_thermostat_setpoint_variability/tests/add_thermostat_setpoint_variability_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/adjust_occupancy_schedule/tests/adjust_occupancy_schedule_test.rb b/resources/measures/adjust_occupancy_schedule/tests/adjust_occupancy_schedule_test.rb
index d21880ec1..ffd60e74e 100644
--- a/resources/measures/adjust_occupancy_schedule/tests/adjust_occupancy_schedule_test.rb
+++ b/resources/measures/adjust_occupancy_schedule/tests/adjust_occupancy_schedule_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/fault_hvac_economizer_changeover_temperature/tests/fault_hvac_economizer_changeover_temperature_test.rb b/resources/measures/fault_hvac_economizer_changeover_temperature/tests/fault_hvac_economizer_changeover_temperature_test.rb
index 087f8f20b..eac3e1e77 100644
--- a/resources/measures/fault_hvac_economizer_changeover_temperature/tests/fault_hvac_economizer_changeover_temperature_test.rb
+++ b/resources/measures/fault_hvac_economizer_changeover_temperature/tests/fault_hvac_economizer_changeover_temperature_test.rb
@@ -25,6 +25,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/fault_hvac_economizer_damper_stuck/tests/fault_hvac_economizer_damper_stuck_test.rb b/resources/measures/fault_hvac_economizer_damper_stuck/tests/fault_hvac_economizer_damper_stuck_test.rb
index bcdc5f24a..ce1d07696 100644
--- a/resources/measures/fault_hvac_economizer_damper_stuck/tests/fault_hvac_economizer_damper_stuck_test.rb
+++ b/resources/measures/fault_hvac_economizer_damper_stuck/tests/fault_hvac_economizer_damper_stuck_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/hardsize_model/tests/hardsize_model_test.rb b/resources/measures/hardsize_model/tests/hardsize_model_test.rb
index 551e0db15..3de64625c 100644
--- a/resources/measures/hardsize_model/tests/hardsize_model_test.rb
+++ b/resources/measures/hardsize_model/tests/hardsize_model_test.rb
@@ -10,6 +10,7 @@
class HardsizeModelTest < Minitest::Test
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/replace_baseline_windows/tests/replace_baseline_windows_test.rb b/resources/measures/replace_baseline_windows/tests/replace_baseline_windows_test.rb
index 7ca9e6628..8f98e7a90 100644
--- a/resources/measures/replace_baseline_windows/tests/replace_baseline_windows_test.rb
+++ b/resources/measures/replace_baseline_windows/tests/replace_baseline_windows_test.rb
@@ -25,6 +25,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_electric_equipment_bpr/tests/set_electric_equipment_bpr_test.rb b/resources/measures/set_electric_equipment_bpr/tests/set_electric_equipment_bpr_test.rb
index c9286224a..fe441ea39 100644
--- a/resources/measures/set_electric_equipment_bpr/tests/set_electric_equipment_bpr_test.rb
+++ b/resources/measures/set_electric_equipment_bpr/tests/set_electric_equipment_bpr_test.rb
@@ -25,6 +25,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_hvac_template/tests/set_hvac_template_test.rb b/resources/measures/set_hvac_template/tests/set_hvac_template_test.rb
index dda1e4308..a604c9f53 100644
--- a/resources/measures/set_hvac_template/tests/set_hvac_template_test.rb
+++ b/resources/measures/set_hvac_template/tests/set_hvac_template_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_interior_equipment_template/tests/set_interior_equipment_template_test.rb b/resources/measures/set_interior_equipment_template/tests/set_interior_equipment_template_test.rb
index 4f2a59cbe..41b0ac3ad 100644
--- a/resources/measures/set_interior_equipment_template/tests/set_interior_equipment_template_test.rb
+++ b/resources/measures/set_interior_equipment_template/tests/set_interior_equipment_template_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_interior_lighting_bpr/tests/set_interior_lighting_bpr_test.rb b/resources/measures/set_interior_lighting_bpr/tests/set_interior_lighting_bpr_test.rb
index b2fa7f93d..70177f1aa 100644
--- a/resources/measures/set_interior_lighting_bpr/tests/set_interior_lighting_bpr_test.rb
+++ b/resources/measures/set_interior_lighting_bpr/tests/set_interior_lighting_bpr_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_primary_kitchen_equipment/tests/set_primary_kitchen_equipment_test.rb b/resources/measures/set_primary_kitchen_equipment/tests/set_primary_kitchen_equipment_test.rb
index cb6aa2584..cfcbad8bc 100644
--- a/resources/measures/set_primary_kitchen_equipment/tests/set_primary_kitchen_equipment_test.rb
+++ b/resources/measures/set_primary_kitchen_equipment/tests/set_primary_kitchen_equipment_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_roof_template/tests/set_roof_template_test.rb b/resources/measures/set_roof_template/tests/set_roof_template_test.rb
index d70dd8fdd..f6f1b296c 100644
--- a/resources/measures/set_roof_template/tests/set_roof_template_test.rb
+++ b/resources/measures/set_roof_template/tests/set_roof_template_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_service_water_heating_template/tests/set_service_water_heating_template_test.rb b/resources/measures/set_service_water_heating_template/tests/set_service_water_heating_template_test.rb
index 3d5e4be9e..163fce20a 100644
--- a/resources/measures/set_service_water_heating_template/tests/set_service_water_heating_template_test.rb
+++ b/resources/measures/set_service_water_heating_template/tests/set_service_water_heating_template_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_space_type_load_subcategories/tests/set_space_type_load_subcategories_test.rb b/resources/measures/set_space_type_load_subcategories/tests/set_space_type_load_subcategories_test.rb
index 616002e5e..c79d211f6 100644
--- a/resources/measures/set_space_type_load_subcategories/tests/set_space_type_load_subcategories_test.rb
+++ b/resources/measures/set_space_type_load_subcategories/tests/set_space_type_load_subcategories_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/set_wall_template/tests/set_wall_template_test.rb b/resources/measures/set_wall_template/tests/set_wall_template_test.rb
index e0aa88a81..931b5f286 100644
--- a/resources/measures/set_wall_template/tests/set_wall_template_test.rb
+++ b/resources/measures/set_wall_template/tests/set_wall_template_test.rb
@@ -24,6 +24,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/thermal_bridging_derating/tests/thermal_bridging_derating_test.rb b/resources/measures/thermal_bridging_derating/tests/thermal_bridging_derating_test.rb
index 9c480780e..abb2f75e6 100644
--- a/resources/measures/thermal_bridging_derating/tests/thermal_bridging_derating_test.rb
+++ b/resources/measures/thermal_bridging_derating/tests/thermal_bridging_derating_test.rb
@@ -57,6 +57,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_add_pvwatts/measure.rb b/resources/measures/upgrade_add_pvwatts/measure.rb
index 2eff0f78b..4b662e208 100644
--- a/resources/measures/upgrade_add_pvwatts/measure.rb
+++ b/resources/measures/upgrade_add_pvwatts/measure.rb
@@ -149,9 +149,9 @@ def model_add_pvwatts_inverter(model,
end
# load data respirces
- def load_standards_data()
+ def load_standards_data
@standards_data = {}
- battery_data = JSON.parse(File.read(File.expand_path(File.dirname(__FILE__) + "/resources/deer_t24_2022.battery_storage_system.json")))
+ battery_data = JSON.parse(File.read(File.expand_path("#{File.dirname(__FILE__)}/resources/deer_t24_2022.battery_storage_system.json")))
@standards_data.merge!(battery_data)
return true
end
@@ -179,7 +179,7 @@ def model_find_object(hash_of_objects, search_criteria)
matching_objects << object
end
- if matching_objects.size.zero?
+ if matching_objects.empty?
desired_object = nil
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Find objects search criteria returned no results. Search criteria: #{search_criteria}. Called from #{caller(0)[1]}")
elsif matching_objects.size == 1
@@ -196,7 +196,7 @@ def model_find_object(hash_of_objects, search_criteria)
def model_get_battery_capacity(building_type)
# populate search hash
search_criteria = {
- 'building_type' => building_type,
+ 'building_type' => building_type
}
# search battery storage table for energy capacity
battery_capacity = model_find_object(@standards_data['battery_storage_system'], search_criteria)
@@ -226,7 +226,7 @@ def model_add_electric_storage_simple(model,
if schedule.nil?
# default always on
battery_schedule = model.alwaysOnDiscreteSchedule
- elsif schedule.class == String
+ elsif schedule.instance_of?(String)
if schedule == 'alwaysOffDiscreteSchedule'
battery_schedule = model.alwaysOffDiscreteSchedule
else
@@ -332,7 +332,7 @@ def run(model, runner, user_arguments)
# add electric load center distribution
electric_load_center_distribution = OpenStudio::Model::ElectricLoadCenterDistribution.new(model)
- electric_load_center_distribution.setName("PV Battery Load Center")
+ electric_load_center_distribution.setName('PV Battery Load Center')
electric_load_center_distribution.setInverter(pv_inverter)
electric_load_center_distribution.setGeneratorOperationSchemeType('TrackElectrical')
electric_load_center_distribution.setElectricalBussType('DirectCurrentWithInverter')
@@ -350,35 +350,35 @@ def run(model, runner, user_arguments)
if incl_batt_storage
# load battery data
- load_standards_data()
+ load_standards_data
# get building type to assign battery design parameters
if model.getBuilding.standardsBuildingType.is_initialized
building_type = model.getBuilding.standardsBuildingType.get
else
- runner.registerError("Building type not found.")
+ runner.registerError('Building type not found.')
return true
end
pv_size_kw = pv_system_capacity / 1000
- battery_data = model_get_battery_capacity(building_type)
+ battery_data = model_get_battery_capacity(building_type)
if battery_data.nil?
runner.registerError("Battery storage parameters not found for building type '#{building_type}'. Please ensure this building type exists in the battery storage JSON.")
return false
end
- b_factor = battery_data["battery_storage_factor_b_energy_capacity"]
+ b_factor = battery_data['battery_storage_factor_b_energy_capacity']
# D factor is Rated single charge-discharge cycle AC to AC (round-trip) efficiency of the battery storage system
# default value is 0.95 * 0.95 from CBECC Rule Batt:RoundTripEff
# d_factor = 0.95 * 0.95
# set by minimum prescriptive requirement of JA12.2.2.1(b)
d_factor = 0.80
- battery_kwh = (pv_size_kw * b_factor) / (d_factor ** 0.5)
+ battery_kwh = (pv_size_kw * b_factor) / (d_factor**0.5)
# calculate battery power capacity per Equation 140.10-C
- c_factor = battery_data["battery_storage_factor_c_power_capacity"]
+ c_factor = battery_data['battery_storage_factor_c_power_capacity']
battery_kw = (pv_size_kw * c_factor)
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.Model', "Creating a Battery Storage system with capacity of #{battery_kwh.round(2)} kWh and charge/discharge power of #{battery_kw.round(2)} kW.")
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Creating a Battery Storage system with capacity of #{battery_kwh.round(2)} kWh and charge/discharge power of #{battery_kw.round(2)} kW.")
battery = model_add_electric_storage_simple(model, max_storage_capacity_kwh: battery_kwh, max_charge_power_kw: battery_kw, max_discharge_power_kw: battery_kw)
converter = model_add_electric_storage_converter(model)
@@ -390,38 +390,38 @@ def run(model, runner, user_arguments)
electric_load_center_distribution.setStorageOperationScheme('TrackFacilityElectricDemandStoreExcessOnSite')
# get final conditions
elcss = model.getElectricLoadCenterStorageSimples
- batt_size_kwh = elcss.map { |b| b.maximumStorageCapacity }.sum / 3.6e6 # convert to kWh from joules
- total_discharge_power_kw = elcss.map { |b| b.maximumPowerforDischarging }.sum / 1000 # convert to kw from w
- total_charge_power_kw = elcss.map { |b| b.maximumPowerforCharging }.sum / 1000 # convert to kw from W
+ batt_size_kwh = elcss.map(&:maximumStorageCapacity).sum / 3.6e6 # convert to kWh from joules
+ total_discharge_power_kw = elcss.map(&:maximumPowerforDischarging).sum / 1000 # convert to kw from w
+ total_charge_power_kw = elcss.map(&:maximumPowerforCharging).sum / 1000 # convert to kw from W
end
if battery.nil?
# report final condition of model with battery
- runner.registerFinalCondition("The building finished with " \
- "#{(pv_system_capacity / 1000).round(0)} kW of PV covering " \
- "#{pv_area_ft2.round(0)} ft² of roof area. The module type is " \
- "#{pv_module_type}, the array type is " \
- "#{pv_array_type}, the system losses are " \
- "#{pv_system_losses}, the tilt angle is " \
- "#{pv_title_angle.round(0)}°, and the azimuth angle is " \
- "#{pv_azimuth_angle.round(0)}°. The inverter has a DC to AC size ratio of " \
- "#{pv_inverter.dcToACSizeRatio} and an inverter efficiency of " \
- "#{(pv_inverter.inverterEfficiency * 100).round(0)}%.")
+ runner.registerFinalCondition('The building finished with ' \
+ "#{(pv_system_capacity / 1000).round(0)} kW of PV covering " \
+ "#{pv_area_ft2.round(0)} ft² of roof area. The module type is " \
+ "#{pv_module_type}, the array type is " \
+ "#{pv_array_type}, the system losses are " \
+ "#{pv_system_losses}, the tilt angle is " \
+ "#{pv_title_angle.round(0)}°, and the azimuth angle is " \
+ "#{pv_azimuth_angle.round(0)}°. The inverter has a DC to AC size ratio of " \
+ "#{pv_inverter.dcToACSizeRatio} and an inverter efficiency of " \
+ "#{(pv_inverter.inverterEfficiency * 100).round(0)}%.")
else
# report final condition of model with no battery
- runner.registerFinalCondition("The building finished with " \
- "#{(pv_system_capacity / 1000).round(0)} kW of PV covering " \
- "#{pv_area_ft2.round(0)} ft^2 of roof area. The module type is " \
- "#{pv_module_type}, the array type is " \
- "#{pv_array_type}, the system losses are " \
- "#{pv_system_losses}, the tilt angle is " \
- "#{pv_title_angle.round(0)}°, and the azimuth angle is " \
- "#{pv_azimuth_angle.round(0)}°. The inverter has a DC to AC size ratio of " \
- "#{pv_inverter.dcToACSizeRatio} and an inverter efficiency of " \
- "#{(pv_inverter.inverterEfficiency * 100).round(0)}%. For storage, the model has a total battery capacity of " \
- "#{batt_size_kwh.round(1)} kWh, a maximum discharging power of " \
- "#{total_discharge_power_kw.round(0)} kW, and a maximum charging power of " \
- "#{total_charge_power_kw.round(0)} kW.")
+ runner.registerFinalCondition('The building finished with ' \
+ "#{(pv_system_capacity / 1000).round(0)} kW of PV covering " \
+ "#{pv_area_ft2.round(0)} ft^2 of roof area. The module type is " \
+ "#{pv_module_type}, the array type is " \
+ "#{pv_array_type}, the system losses are " \
+ "#{pv_system_losses}, the tilt angle is " \
+ "#{pv_title_angle.round(0)}°, and the azimuth angle is " \
+ "#{pv_azimuth_angle.round(0)}°. The inverter has a DC to AC size ratio of " \
+ "#{pv_inverter.dcToACSizeRatio} and an inverter efficiency of " \
+ "#{(pv_inverter.inverterEfficiency * 100).round(0)}%. For storage, the model has a total battery capacity of " \
+ "#{batt_size_kwh.round(1)} kWh, a maximum discharging power of " \
+ "#{total_discharge_power_kw.round(0)} kW, and a maximum charging power of " \
+ "#{total_charge_power_kw.round(0)} kW.")
end
true
diff --git a/resources/measures/upgrade_add_pvwatts/tests/upgrade_add_pvwatts_test.rb b/resources/measures/upgrade_add_pvwatts/tests/upgrade_add_pvwatts_test.rb
index c540c14fb..b896b4863 100644
--- a/resources/measures/upgrade_add_pvwatts/tests/upgrade_add_pvwatts_test.rb
+++ b/resources/measures/upgrade_add_pvwatts/tests/upgrade_add_pvwatts_test.rb
@@ -58,99 +58,159 @@ def test_number_of_arguments_and_argument_names
# get arguments and test that they are what we are expecting
arguments = measure.arguments(model)
- assert_equal(1, arguments.size)
- assert_equal('pv_area_fraction', arguments[0].name)
+ assert_equal(2, arguments.size)
end
- def test_bad_argument_values
- # create an instance of the measure
- measure = UpgradeAddPvwatts.new
-
- # create runner with empty OSW
- osw = OpenStudio::WorkflowJSON.new
- runner = OpenStudio::Measure::OSRunner.new(osw)
-
- # make an empty model
- model = OpenStudio::Model::Model.new
+ # return file paths to test models in test directory
+ def models_for_tests
+ paths = Dir.glob(File.join(File.dirname(__FILE__), '../../../tests/models/*.osm'))
+ paths = paths.map { |path| File.expand_path(path) }
+ return paths
+ end
- # get arguments
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
+ # return file paths to epw files in test directory
+ def epws_for_tests
+ paths = Dir.glob(File.join(File.dirname(__FILE__), '../../../tests/weather/*.epw'))
+ paths = paths.map { |path| File.expand_path(path) }
+ return paths
+ end
- # create hash of argument values
- args_hash = {}
- args_hash['space_name'] = ''
+ def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
+ translator = OpenStudio::OSVersion::VersionTranslator.new
+ model = translator.loadModel(OpenStudio::Path.new(osm_path))
+ assert(!model.empty?)
+ model = model.get
+ return model
+ end
- # populate argument with specified hash value if specified
- arguments.each do |arg|
- temp_arg_var = arg.clone
- assert(temp_arg_var.setValue(args_hash[arg.name])) if args_hash.key?(arg.name)
- argument_map[arg.name] = temp_arg_var
+ def run_dir(test_name)
+ # always generate test output in specially named 'output' directory so result files are not made part of the measure
+ path = "#{File.dirname(__FILE__)}/output/#{test_name}"
+ unless File.directory?(path)
+ FileUtils.mkdir_p(path)
end
+ return path
+ end
- # run the measure
- measure.run(model, runner, argument_map)
- result = runner.result
+ def model_output_path(test_name)
+ return "#{run_dir(test_name)}/#{test_name}.osm"
+ end
- # show the output
- show_output(result)
+ def sql_path(test_name)
+ return "#{run_dir(test_name)}/run/eplusout.sql"
+ end
- # assert that it ran correctly
- assert_equal('Fail', result.value.valueName)
+ def report_path(test_name)
+ return "#{run_dir(test_name)}/reports/eplustbl.html"
end
- def test_good_argument_values
- # create an instance of the measure
- measure = UpgradeAddPvwatts.new
+ # applies the measure and then runs the model
+ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path, run_model: false)
+ assert(File.exist?(osm_path))
+ assert(File.exist?(epw_path))
- # create runner with empty OSW
- osw = OpenStudio::WorkflowJSON.new
- runner = OpenStudio::Measure::OSRunner.new(osw)
+ # create run directory if it does not exist
+ FileUtils.mkdir_p(run_dir(test_name))
+ assert(File.exist?(run_dir(test_name)))
- # load the test model
- translator = OpenStudio::OSVersion::VersionTranslator.new
- path = "#{File.dirname(__FILE__)}/example_model.osm"
- model = translator.loadModel(path)
- assert(!model.empty?)
- model = model.get
+ # change into run directory for tests
+ start_dir = Dir.pwd
+ Dir.chdir run_dir(test_name)
- # store the number of spaces in the seed model
- num_spaces_seed = model.getSpaces.size
+ # remove prior runs if they exist
+ FileUtils.rm_f(model_output_path(test_name))
+ FileUtils.rm_f(report_path(test_name))
- # get arguments
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
-
- # create hash of argument values.
- # If the argument has a default that you want to use, you don't need it in the hash
- args_hash = {}
- args_hash['space_name'] = 'New Space'
- # using defaults values from measure.rb for other arguments
-
- # populate argument with specified hash value if specified
- arguments.each do |arg|
- temp_arg_var = arg.clone
- assert(temp_arg_var.setValue(args_hash[arg.name])) if args_hash.key?(arg.name)
- argument_map[arg.name] = temp_arg_var
- end
+ # copy the osm and epw to the test directory
+ # new_osm_path = File.expand_path("#{Dir.pwd}/#{File.basename(osm_path)}")
+ new_osm_path = "#{Dir.pwd}/#{File.basename(osm_path)}"
+ FileUtils.cp(osm_path, new_osm_path)
+ new_epw_path = "#{Dir.pwd}/#{File.basename(epw_path)}"
+ FileUtils.cp(epw_path, new_epw_path)
+ # create an instance of a runner
+ runner = OpenStudio::Measure::OSRunner.new(OpenStudio::WorkflowJSON.new)
+
+ # load the test model
+ model = load_model(new_osm_path)
+
+ # set model weather file
+ epw_file = OpenStudio::EpwFile.new(OpenStudio::Path.new(new_epw_path))
+ OpenStudio::Model::WeatherFile.setWeatherFile(model, epw_file)
+ assert(model.weatherFile.is_initialized)
# run the measure
+ puts "\nAPPLYING MEASURE..."
measure.run(model, runner, argument_map)
result = runner.result
+ result_success = result.value.valueName == 'Success'
# show the output
show_output(result)
- # assert that it ran correctly
- assert_equal('Success', result.value.valueName)
- assert(result.info.size == 1)
- assert(result.warnings.empty?)
+ # save model
+ model.save(model_output_path(test_name), true)
+
+ if run_model && result_success
+ puts "\nRUNNING MODEL..."
+
+ std = Standard.build('ComStock DEER 2020')
+ std.model_run_simulation_and_log_errors(model, run_dir(test_name))
+
+ # check that the model ran successfully
+ assert(File.exist?(sql_path(test_name)))
+ end
+
+ # change back directory
+ Dir.chdir(start_dir)
- # check that there is now 1 space
- assert_equal(1, model.getSpaces.size - num_spaces_seed)
+ return result
+ end
- # save the model to test output directory
- output_file_path = "#{File.dirname(__FILE__)}//output/test_output.osm"
- model.save(output_file_path, true)
+ # create an array of hashes with model name, weather, and expected result
+ def models_to_test
+ test_sets = []
+ test_sets << { model: 'LargeOffice_VAV_chiller_boiler', weather: 'VA_MANASSAS_724036_12', result: 'Success' }
+ test_sets << { model: 'Stripmall_Pre1980_8A', weather: 'GA_ROBINS_AFB_722175_12', result: 'Success' }
+ test_sets << { model: 'Stripmall_Pre1980_8A_new_OA', weather: 'GA_ROBINS_AFB_722175_12', result: 'Success' }
+ test_sets << { model: 'Baseboard_electric_heat_3B', weather: 'CA_LOS-ANGELES-DOWNTOWN-USC_722874S_16', result: 'Success' }
+ test_sets << { model: 'Quick_Service_Restaurant_Pre1980_3A', weather: 'CA_LOS-ANGELES-DOWNTOWN-USC_722874S_16', result: 'Success' }
+ test_sets << { model: 'Quick_Service_Restaurant_CA', weather: 'CA_LOS-ANGELES-DOWNTOWN-USC_722874S_16', result: 'Success' }
+ return test_sets
+ end
+
+ def test_models
+ test_name = 'test_models'
+ puts "\n######\nTEST:#{test_name}\n######\n"
+
+ models_to_test.each do |set|
+ instance_test_name = set[:model]
+ puts "instance test name: #{instance_test_name}"
+ osm_path = models_for_tests.select { |x| set[:model] == File.basename(x, '.osm') }
+ epw_path = epws_for_tests.select { |x| set[:weather] == File.basename(x, '.epw') }
+ assert(!osm_path.empty?)
+ assert(!epw_path.empty?)
+ osm_path = osm_path[0]
+ epw_path = epw_path[0]
+
+ # create an instance of the measure
+ measure = UpgradeAddPvwatts.new
+
+ # load the model; only used here for populating arguments
+ model = load_model(osm_path)
+
+ # set arguments here; will vary by measure
+ arguments = measure.arguments(model)
+ # argument_map = OpenStudio::Measure::OSArgumentMap.new
+ argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
+
+ # apply the measure to the model and optionally run the model
+ result = apply_measure_and_run(instance_test_name, measure, argument_map, osm_path, epw_path, run_model: false)
+
+ # check the measure result; result values will equal Success, Fail, or Not Applicable
+ # also check the amount of warnings, info, and error messages
+ # use if or case statements to change expected assertion depending on model characteristics
+ assert_equal(set[:result].to_s, result.value.valueName.to_s)
+ end
end
end
diff --git a/resources/measures/upgrade_add_thermostat_setback/measure.rb b/resources/measures/upgrade_add_thermostat_setback/measure.rb
index 0be575bdf..ea9f61ffc 100644
--- a/resources/measures/upgrade_add_thermostat_setback/measure.rb
+++ b/resources/measures/upgrade_add_thermostat_setback/measure.rb
@@ -3,7 +3,7 @@
# see the URL below for information on how to write OpenStudio measures
# http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/
-Dir[File.dirname(__FILE__) + '/resources/*.rb'].each { |file| require file }
+Dir["#{File.dirname(__FILE__)}/resources/*.rb"].sort.each { |file| require file }
# start the measure
class UpgradeAddThermostatSetback < OpenStudio::Measure::ModelMeasure
# human readable name
diff --git a/resources/measures/upgrade_add_thermostat_setback/measure.xml b/resources/measures/upgrade_add_thermostat_setback/measure.xml
index 3f32877ce..3c83121f9 100644
--- a/resources/measures/upgrade_add_thermostat_setback/measure.xml
+++ b/resources/measures/upgrade_add_thermostat_setback/measure.xml
@@ -3,8 +3,8 @@
3.1
upgrade_add_thermostat_setback
5f19c384-dbef-4747-90b7-f7500d315bd8
- edf853e9-fb49-4a34-8143-4a14e1d71e33
- 2025-10-21T17:21:21Z
+ 30753e4d-07fc-42c5-b0d0-e570754cb95d
+ 2026-01-13T17:22:55Z
B3180F38
UpgradeAddThermostatSetback
Upgrade_Add_Thermostat_Setback
@@ -118,7 +118,7 @@
LICENSE.md
md
license
- CD7F5672
+ 08D3D350
README.md
@@ -147,13 +147,13 @@
measure.rb
rb
script
- 37EA92F8
+ B04F88AC
sched_methods.rb
rb
resource
- 8788DDD5
+ 48B2DCB1
example_model.osm
@@ -165,7 +165,7 @@
upgrade_add_thermostat_setback_test.rb
rb
test
- 88063E80
+ 0CC50C6A
diff --git a/resources/measures/upgrade_add_thermostat_setback/resources/sched_methods.rb b/resources/measures/upgrade_add_thermostat_setback/resources/sched_methods.rb
index 8ac280eda..f04ea7021 100644
--- a/resources/measures/upgrade_add_thermostat_setback/resources/sched_methods.rb
+++ b/resources/measures/upgrade_add_thermostat_setback/resources/sched_methods.rb
@@ -40,7 +40,8 @@
require 'date'
require 'openstudio-standards'
-def get_tstat_profiles_and_stats(tstat_schedule) # from add thermostat setpoint variability measure
+# from add thermostat setpoint variability measure
+def get_tstat_profiles_and_stats(tstat_schedule)
if tstat_schedule.to_ScheduleRuleset.empty?
runner.registerWarning("Schedule '#{tstat_schedule.name.get}' is not a ScheduleRuleset, will not be adjusted")
false
@@ -60,7 +61,8 @@ def get_tstat_profiles_and_stats(tstat_schedule) # from add thermostat setpoint
end
end
-def get_8760_values_from_schedule_ruleset(model, schedule_ruleset) # from a PR to standards, can call directly in the future
+# from a PR to standards, can call directly in the future
+def get_8760_values_from_schedule_ruleset(model, schedule_ruleset)
Standard.build('90.1-2013') # build openstudio standards
yd = model.getYearDescription
start_date = yd.makeDate(1, 1)
@@ -199,9 +201,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
all_week_rules = { iweek_previous_week_rule: week_1_rules }
# temporary loop for debugging
- week_n_rules.each do |sch_rule|
- sch_rule.daySchedule
- end
+ week_n_rules.each(&:daySchedule)
# For each subsequent week, check if it is same as previous
# If same, then append to Schedule:Rule of previous week
@@ -223,7 +223,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
else
# Create a new week schedule for this week
num_week_scheds += 1
- week_sch_name = sch_name + '_ws' + num_week_scheds.to_s
+ week_sch_name = "#{sch_name}_ws#{num_week_scheds}"
week_n_rules = std.make_week_ruleset_sched_from_168(model, sch_ruleset, all_week_values[iweek], start_date,
end_date, week_sch_name)
all_week_rules[:iweek_previous_week_rule] = week_n_rules
@@ -233,9 +233,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
end
# temporary loop for debugging
- week_n_rules.each do |sch_rule|
- sch_rule.daySchedule
- end
+ week_n_rules.each(&:daySchedule)
# Need to handle week 52 with days 365 and 366
# For each of these days, check if it matches a day from the previous week
diff --git a/resources/measures/upgrade_add_thermostat_setback/tests/upgrade_add_thermostat_setback_test.rb b/resources/measures/upgrade_add_thermostat_setback/tests/upgrade_add_thermostat_setback_test.rb
index 4bc0a13e8..835b5721c 100644
--- a/resources/measures/upgrade_add_thermostat_setback/tests/upgrade_add_thermostat_setback_test.rb
+++ b/resources/measures/upgrade_add_thermostat_setback/tests/upgrade_add_thermostat_setback_test.rb
@@ -20,6 +20,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -60,7 +61,7 @@ def set_weather_and_apply_measure_and_run(test_name, measure, argument_map, osm_
ddy_path = "#{epw_path.gsub('.epw', '')}.ddy"
# create run directory if it does not exist
- FileUtils.mkdir_p(run_dir(test_name)) unless File.exist?(run_dir(test_name))
+ FileUtils.mkdir_p(run_dir(test_name))
assert(File.exist?(run_dir(test_name)))
# change into run directory for tests
@@ -68,8 +69,8 @@ def set_weather_and_apply_measure_and_run(test_name, measure, argument_map, osm_
Dir.chdir run_dir(test_name)
# remove prior runs if they exist
- FileUtils.rm(model_output_path(test_name)) if File.exist?(model_output_path(test_name))
- FileUtils.rm(report_path(test_name)) if File.exist?(report_path(test_name))
+ FileUtils.rm_f(model_output_path(test_name))
+ FileUtils.rm_f(report_path(test_name))
# copy the osm and epw to the test directory
new_osm_path = "#{run_dir(test_name)}/#{File.basename(osm_path)}"
@@ -118,7 +119,7 @@ def set_weather_and_apply_measure_and_run(test_name, measure, argument_map, osm_
end
# assert
- assert_equal(false, model.getDesignDays.size.zero?)
+ assert_equal(false, model.getDesignDays.empty?)
end
if apply
diff --git a/resources/measures/upgrade_advanced_rtu_control/measure.rb b/resources/measures/upgrade_advanced_rtu_control/measure.rb
index 8bab5969e..03d20f2d4 100644
--- a/resources/measures/upgrade_advanced_rtu_control/measure.rb
+++ b/resources/measures/upgrade_advanced_rtu_control/measure.rb
@@ -2,8 +2,7 @@
# See top level LICENSE.txt file for license terms.
class AdvancedRTUControl < OpenStudio::Measure::ModelMeasure
-
-require 'openstudio-standards'
+ require 'openstudio-standards'
# human readable name
def name
# Measure name should be the title case of the class name.
@@ -24,14 +23,14 @@ def modeler_description
def arguments(model)
args = OpenStudio::Measure::OSArgumentVector.new
- #economizer option
+ # economizer option
add_econo = OpenStudio::Measure::OSArgument.makeBoolArgument('add_econo', true)
add_econo.setDisplayName('Economizer to be added?')
add_econo.setDescription('Add economizer (true) or not (false)')
args << add_econo
- #dcv option
- add_dcv = OpenStudio::Measure::OSArgument.makeBoolArgument('add_dcv', true)
+ # dcv option
+ add_dcv = OpenStudio::Measure::OSArgument.makeBoolArgument('add_dcv', true)
add_dcv.setDisplayName('DCV to be added?')
add_dcv.setDescription('Add DCV (true) or not (false)')
args << add_dcv
@@ -76,7 +75,7 @@ def air_loop_evaporative_cooler?(air_loop_hvac)
return is_evap
end
- #slightly modified from OS standards to adjust log messages
+ # slightly modified from OS standards to adjust log messages
def thermal_zone_outdoor_airflow_rate(thermal_zone)
tot_oa_flow_rate = 0.0
@@ -135,30 +134,24 @@ def thermal_zone_outdoor_airflow_rate(thermal_zone)
end
def vav_terminals?(air_loop_hvac)
- air_loop_hvac.thermalZones.each do |thermal_zone| #iterate thru thermal zones and modify zone-level terminal units
- thermal_zone.equipment.each do |equip|
- if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized
- return true
- elsif equip.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized
- return true
- elsif equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
+ air_loop_hvac.thermalZones.each do |thermal_zone| # iterate thru thermal zones and modify zone-level terminal units
+ thermal_zone.equipment.each do |equip|
+ if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVNoReheat.is_initialized ||
+ equip.to_AirTerminalDualDuctVAV.is_initialized ||
+ equip.to_AirTerminalDualDuctVAVOutdoorAir.is_initialized
return true
- elsif equip.to_AirTerminalSingleDuctVAVNoReheat.is_initialized
- return true
- elsif equip.to_AirTerminalDualDuctVAV.is_initialized
- return true
- elsif equip.to_AirTerminalDualDuctVAVOutdoorAir.is_initialized
- return true
- else
- next
- end
- end
- end
- return false #if no VAV terminals found on the air loop
+ end
+ end
+ end
+ return false # if no VAV terminals found on the air loop
end
-def no_DCV_zones?(air_loop_hvac)
- selected_air_loops = []
- space_types_no_dcv = [
+
+ def no_dcv_zones?(air_loop_hvac)
+ selected_air_loops = []
+ space_types_no_dcv = [
'Kitchen',
'kitchen',
'PatRm',
@@ -185,15 +178,16 @@ def no_DCV_zones?(air_loop_hvac)
'LockerRoom',
'Stair',
'Toilet',
- 'MechElecRoom',
+ 'MechElecRoom'
]
oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem
- if oa_system.is_initialized
- oa_system = oa_system.get
- else
- return true
- end
+ return true unless oa_system.is_initialized
+
+ oa_system = oa_system.get
+
+
+
# check if airloop is DOAS; skip if true
sizing_system = air_loop_hvac.sizingSystem
@@ -206,8 +200,9 @@ def no_DCV_zones?(air_loop_hvac)
erv_components = []
air_loop_hvac.oaComponents.each do |component|
component_name = component.name.to_s
- next if component_name.include? "Node"
- if component_name.include? "ERV"
+ next if component_name.include? 'Node'
+
+ if component_name.include? 'ERV'
erv_components << component
end
end
@@ -241,473 +236,474 @@ def no_DCV_zones?(air_loop_hvac)
if space_no_dcv >= 1
return true
end
- return false
-end
-def air_loop_doas?(air_loop_hvac)
+ return false
+ end
+
+ def air_loop_doas?(air_loop_hvac)
is_doas = false
sizing_system = air_loop_hvac.sizingSystem
- if sizing_system.allOutdoorAirinCooling && sizing_system.allOutdoorAirinHeating && (air_loop_res?(air_loop_hvac) == false) && (air_loop_hvac.name.to_s.include?("DOAS") || air_loop_hvac.name.to_s.include?("doas"))
+ if sizing_system.allOutdoorAirinCooling && sizing_system.allOutdoorAirinHeating && (air_loop_res?(air_loop_hvac) == false) && (air_loop_hvac.name.to_s.include?('DOAS') || air_loop_hvac.name.to_s.include?('doas'))
is_doas = true
end
return is_doas
-end
+ end
# define what happens when the measure is run
-def run(model, runner, user_arguments)
- super(model, runner, user_arguments) # Do **NOT** remove this line
+ def run(model, runner, user_arguments)
+ super(model, runner, user_arguments) # Do **NOT** remove this line
# use the built-in error checking
if !runner.validateUserArguments(arguments(model), user_arguments)
return false
end
- #read in arguments
- add_econo = runner.getBoolArgumentValue('add_econo', user_arguments)
- add_dcv = runner.getBoolArgumentValue('add_dcv', user_arguments)
+
+ # read in arguments
+ add_econo = runner.getBoolArgumentValue('add_econo', user_arguments)
+ add_dcv = runner.getBoolArgumentValue('add_dcv', user_arguments)
- #set airflow design ratio
- min_flow = 0.4 #Based on Catalyst
+ # set airflow design ratio
+ min_flow = 0.4 # Based on Catalyst
- min_flow_fraction = 0.67 #30% power for non-inverter driven motors, this is applied to flow, so roughly 30% power with cubic fan curve
+ min_flow_fraction = 0.67 # 30% power for non-inverter driven motors, this is applied to flow, so roughly 30% power with cubic fan curve
- #Setting up OS standards
- standard = Standard.build('90.1-2013')
- standard_new_motor = Standard.build('90.1-2019') #to reflect new motors
+ # Setting up OS standards
+ standard = Standard.build('90.1-2013')
+ standard_new_motor = Standard.build('90.1-2019') # to reflect new motors
- #Set up for economizer implementation for checking applicability
+ # Set up for economizer implementation for checking applicability
no_outdoor_air_loops = 0
doas_loops = 0
existing_economizer_loops = 0
selected_air_loops = []
- added_economizers = 0
-
- if model.sqlFile.empty?
- #runner.registerInfo('Model had no sizing values--running size run')
- if standard.model_run_sizing_run(model, "#{Dir.pwd}/advanced_rtu_control") == false
- runner.registerError('Sizing run for Hardsize model failed, cannot hard-size model.')
- return false
- end
- model.applySizingValues
- end
+ added_economizers = 0
+
+ if model.sqlFile.empty?
+ # runner.registerInfo('Model had no sizing values--running size run')
+ if standard.model_run_sizing_run(model, "#{Dir.pwd}/advanced_rtu_control") == false
+ runner.registerError('Sizing run for Hardsize model failed, cannot hard-size model.')
+ return false
+ end
+ model.applySizingValues
+ end
+
+ if add_econo # if adding economizing, set high level params
+ # build standard to access methods
+ template = 'ComStock 90.1-2019'
+ std = Standard.build(template)
+ # get climate zone
+ climate_zone = OpenstudioStandards::Weather.model_get_climate_zone(model)
+ # runner.registerInfo("initial read of climate zone = #{climate_zone}")
+ if climate_zone.empty?
+ runner.registerError('Unable to determine climate zone for model. Cannot apply economizer without climate zone information.')
+ end
+ # check climate zone name validity
+ # this happens to example model but maybe not during ComStock model creation?
+ substring_count = climate_zone.scan(/ASHRAE 169-2013-/).length
+ if substring_count > 1
+ # runner.registerInfo("climate zone name includes repeated substring of 'ASHRAE 169-2013-'")
+ climate_zone = climate_zone.sub(/ASHRAE 169-2013-/, '')
+ # runner.registerInfo("revised climate zone name = #{climate_zone}")
+ end
+ # determine economizer type
+ economizer_type = std.model_economizer_type(model, climate_zone)
+ # runner.registerInfo("economizer type for the climate zone = #{economizer_type}")
+ end
+
+ # Identify suitable loops for applying the measure
+ overall_sel_air_loops = []
+
+ model.getAirLoopHVACs.sort.each do |air_loop_hvac|
+ next if ((air_loop_hvac.thermalZones.length > 1) || air_loop_res?(air_loop_hvac) || air_loop_evaporative_cooler?(air_loop_hvac) || air_loop_hvac.name.to_s.include?('DOAS') || air_loop_hvac.name.to_s.include?('doas')) || air_loop_doas?(air_loop_hvac)
+
+ # skip based on residential being in name, or if a DOAS
+ sizing_system = air_loop_hvac.sizingSystem
+ next if air_loop_hvac.name.to_s.include?('residential') || air_loop_hvac.name.to_s.include?('Residential') || (sizing_system.allOutdoorAirinCooling && sizing_system.allOutdoorAirinHeating)
+ # skip VAV systems
+ next if ['VAV', 'PVAV'].any? { |word| air_loop_hvac.name.get.include?(word) } || vav_terminals?(air_loop_hvac)
+ next if !air_loop_hvac_unitary_system?(air_loop_hvac) # select unitary systems only
+
+ overall_sel_air_loops << air_loop_hvac
+ end
+
+ # register na if no applicable air loops
+ if overall_sel_air_loops.empty?
+ runner.registerAsNotApplicable('No applicable air loops found in model')
+ end
+
- if add_econo #if adding economizing, set high level params
- # build standard to access methods
- template = 'ComStock 90.1-2019'
- std = Standard.build(template)
- # get climate zone
- climate_zone = OpenstudioStandards::Weather.model_get_climate_zone(model)
- #runner.registerInfo("initial read of climate zone = #{climate_zone}")
- if climate_zone.empty?
- runner.registerError('Unable to determine climate zone for model. Cannot apply economizer without climate zone information.')
- end
- # check climate zone name validity
- # this happens to example model but maybe not during ComStock model creation?
- substring_count = climate_zone.scan(/ASHRAE 169-2013-/).length
- if substring_count > 1
- #runner.registerInfo("climate zone name includes repeated substring of 'ASHRAE 169-2013-'")
- climate_zone = climate_zone.sub(/ASHRAE 169-2013-/, '')
- #runner.registerInfo("revised climate zone name = #{climate_zone}")
- end
- # determine economizer type
- economizer_type = std.model_economizer_type(model, climate_zone)
- #runner.registerInfo("economizer type for the climate zone = #{economizer_type}")
- end
-
- #Identify suitable loops for applying the measure
- overall_sel_air_loops =[]
-
- model.getAirLoopHVACs.sort.each do |air_loop_hvac|
- next if ((air_loop_hvac.thermalZones.length() > 1) || air_loop_res?(air_loop_hvac) || air_loop_evaporative_cooler?(air_loop_hvac)|| (air_loop_hvac.name.to_s.include?("DOAS")) || (air_loop_hvac.name.to_s.include?("doas"))) || air_loop_doas?(air_loop_hvac)
- #skip based on residential being in name, or if a DOAS
- sizing_system = air_loop_hvac.sizingSystem
- next if ((air_loop_hvac.name.to_s.include?("residential")) || (air_loop_hvac.name.to_s.include?("Residential")) || (sizing_system.allOutdoorAirinCooling && sizing_system.allOutdoorAirinHeating))
- #skip VAV systems
- next if ['VAV', 'PVAV'].any? { |word| (air_loop_hvac.name.get).include?(word) } || vav_terminals?(air_loop_hvac)
- next if !(air_loop_hvac_unitary_system?(air_loop_hvac)) #select unitary systems only
- overall_sel_air_loops << air_loop_hvac
- end
-
- #register na if no applicable air loops
- if overall_sel_air_loops.length() == 0
- runner.registerAsNotApplicable('No applicable air loops found in model')
- end
-
-
- overall_sel_air_loops.sort.each do |air_loop_hvac| #iterating thru air loops in the model to identify ones suitable for VAV conversion
- #set control type
- air_loop_hvac.supplyComponents.sort.each do |component|#identifying unitary systems
- obj_type = component.iddObjectType.valueName.to_s
- case obj_type
+ overall_sel_air_loops.sort.each do |air_loop_hvac| # iterating thru air loops in the model to identify ones suitable for VAV conversion
+ # set control type
+ air_loop_hvac.supplyComponents.sort.each do |component| # identifying unitary systems
+ obj_type = component.iddObjectType.valueName.to_s
+ case obj_type
when 'OS_AirLoopHVAC_UnitarySystem'
- component = component.to_AirLoopHVACUnitarySystem.get
- component.setControlType('SingleZoneVAV')
- #Set overall flow rates for air loop
- if air_loop_hvac.autosizedDesignSupplyAirFlowRate.is_initialized #change supply air flow design parameters to match VAV conversion
- des_supply_airflow = air_loop_hvac.autosizedDesignSupplyAirFlowRate.get #handle autosized
- component.setSupplyAirFlowRateDuringCoolingOperation(des_supply_airflow) #Set the same as before
- component.setSupplyAirFlowRateDuringHeatingOperation(des_supply_airflow) #Set same as before
- component.setSupplyAirFlowRateWhenNoCoolingorHeatingisRequired(min_flow*des_supply_airflow) #Set min based on limit after retrofit
- elsif air_loop_hvac.designSupplyAirFlowRate.is_initialized #handle hard-sized
- des_supply_airflow = air_loop_hvac.designSupplyAirFlowRate.get
- component.setSupplyAirFlowRateDuringCoolingOperation(des_supply_airflow)
- component.setSupplyAirFlowRateDuringHeatingOperation(des_supply_airflow)
- component.setSupplyAirFlowRateWhenNoCoolingorHeatingisRequired(min_flow*des_supply_airflow)
- end
- sup_fan = component.supplyFan
- if sup_fan.is_initialized #Replace constant speed with variable speed fan objects
- sup_fan = sup_fan.get
- #handle fan on off objects; replace FanOnOff with FanVariableVolume
+ component = component.to_AirLoopHVACUnitarySystem.get
+ component.setControlType('SingleZoneVAV')
+ # Set overall flow rates for air loop
+ if air_loop_hvac.autosizedDesignSupplyAirFlowRate.is_initialized # change supply air flow design parameters to match VAV conversion
+ des_supply_airflow = air_loop_hvac.autosizedDesignSupplyAirFlowRate.get # handle autosized
+ component.setSupplyAirFlowRateDuringCoolingOperation(des_supply_airflow) # Set the same as before
+ component.setSupplyAirFlowRateDuringHeatingOperation(des_supply_airflow) # Set same as before
+ component.setSupplyAirFlowRateWhenNoCoolingorHeatingisRequired(min_flow * des_supply_airflow) # Set min based on limit after retrofit
+ elsif air_loop_hvac.designSupplyAirFlowRate.is_initialized # handle hard-sized
+ des_supply_airflow = air_loop_hvac.designSupplyAirFlowRate.get
+ component.setSupplyAirFlowRateDuringCoolingOperation(des_supply_airflow)
+ component.setSupplyAirFlowRateDuringHeatingOperation(des_supply_airflow)
+ component.setSupplyAirFlowRateWhenNoCoolingorHeatingisRequired(min_flow * des_supply_airflow)
+ end
+ sup_fan = component.supplyFan
+ if sup_fan.is_initialized # Replace constant speed with variable speed fan objects
+ sup_fan = sup_fan.get
+ # handle fan on off objects; replace FanOnOff with FanVariableVolume
if sup_fan.to_FanOnOff.is_initialized
- sup_fan = sup_fan.to_FanOnOff.get
- pressure_rise = sup_fan.pressureRise()
- motor_hp = standard.fan_motor_horsepower(sup_fan)
- fan_eff = standard.fan_baseline_impeller_efficiency(sup_fan)
- if sup_fan.autosizedMaximumFlowRate.is_initialized
- fan_flow = sup_fan.autosizedMaximumFlowRate.get
- elsif sup_fan.maximumFlowRate.is_initialized
- fan_flow = sup_fan.maximumFlowRate.get
- end
- #ASHRAE 90.1 2019 version of the standard to reflect motor replacement
- fan_motor_eff = standard_new_motor.fan_standard_minimum_motor_efficiency_and_size(sup_fan, motor_hp)[0] #calculate fan motor eff per Standards
- end
- #handle constant speed fan objects; replace FanConstantVolume with FanVariableVolume
- if sup_fan.to_FanConstantVolume.is_initialized
- sup_fan = sup_fan.to_FanConstantVolume.get
- pressure_rise = sup_fan.pressureRise()
- motor_hp = standard.fan_motor_horsepower(sup_fan)
- fan_eff = standard.fan_baseline_impeller_efficiency(sup_fan)
- if sup_fan.autosizedMaximumFlowRate.is_initialized
- fan_flow = sup_fan.autosizedMaximumFlowRate.get
- elsif sup_fan.maximumFlowRate.is_initialized
- fan_flow = sup_fan.maximumFlowRate.get
- end
- #ASHRAE 90.1 2019 version of the standard to reflect motor replacement
- fan_motor_eff = standard_new_motor.fan_standard_minimum_motor_efficiency_and_size(sup_fan, motor_hp)[0] #calculate fan motor eff per Standards
- end
- #create new VS fan
- fan = OpenStudio::Model::FanVariableVolume.new(model)
- fan.setName("#{air_loop_hvac.name} Fan")
- fan.setFanPowerMinimumFlowRateInputMethod("Fraction")
- fan.setPressureRise(pressure_rise)#keep it the same as the existing fan, since the balance of systems is the same
- fan.setMotorEfficiency(fan_motor_eff)
- fan.setMaximumFlowRate(fan_flow) #keep it the same as the existing fan, since the fan itself will be the same
- fan.setFanTotalEfficiency(fan_motor_eff * fan_eff)
- #set fan curve coefficients
- standard.fan_variable_volume_set_control_type(fan, 'Single Zone VAV Fan ')
- fan.setFanPowerMinimumFlowFraction(min_flow_fraction) #resetting minimum flow fraction to be appropriate for retrofit as opposed to 10% in method above
- #Add it to the unitary sys
- component.setSupplyFan(fan)
- end
- end
- end
- air_loop_hvac.thermalZones.each do |thermal_zone| #iterate thru thermal zones and modify zone-level terminal units
- min_oa_flow_rate_cont = 0
- #See if a minimum OA flow rate is already set
- if air_loop_hvac.airLoopHVACOutdoorAirSystem.is_initialized
- oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
- if controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
- min_oa_flow_rate_cont = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
- elsif controller_oa.minimumOutdoorAirFlowRate.is_initialized
- min_oa_flow_rate_cont = controller_oa.minimumOutdoorAirFlowRate.get
- end
- end
- #if min OA flow rate is 0, or if it isn't set, calculate it
- if min_oa_flow_rate_cont == 0
- min_oa_flow_rate = thermal_zone_outdoor_airflow_rate(thermal_zone)
- elsif
- min_oa_flow_rate = min_oa_flow_rate_cont
- end
- thermal_zone.equipment.each do |equip|
- if equip.to_AirTerminalSingleDuctConstantVolumeNoReheat.is_initialized
- term = equip.to_AirTerminalSingleDuctConstantVolumeNoReheat.get
- new_term = OpenStudio::Model::AirTerminalSingleDuctVAVHeatAndCoolNoReheat.new(model) #create new terminal unit
- if term.autosizedMaximumAirFlowRate.is_initialized
- des_airflow_rate = term.autosizedMaximumAirFlowRate.get
- new_term.setMaximumAirFlowRate(des_airflow_rate) #same as before
- #set minimum based on max of 40% of max flow, or min ventilation level req'd
- new_term.setZoneMinimumAirFlowFraction([min_flow, min_oa_flow_rate/des_airflow_rate].max)
- elsif term.maximumAirFlowRate.is_initialized
- des_airflow_rate = term.maximumAirFlowRate.get
- new_term.setMaximumAirFlowRate(des_airflow_rate) #same as before
- #set minimum based on max of 40% of max flow, or min ventilation level req'd
- new_term.setZoneMinimumAirFlowFraction([min_flow, min_oa_flow_rate/des_airflow_rate].max)
- end
- air_loop_hvac.removeBranchForZone(thermal_zone)
- air_loop_hvac.addBranchForZone(thermal_zone, new_term)
- end
- end
- end
- end
- #handle DCV in appropriate air loops, after screening out those that aren't suitable
- if add_dcv
- overall_sel_air_loops.sort.each do |air_loop_hvac|
- unless(no_DCV_zones?(air_loop_hvac))
- oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
- controller_mv = controller_oa.controllerMechanicalVentilation
- controller_mv.setDemandControlledVentilation(true)
- air_loop_hvac.thermalZones.each do |thermal_zone|
- #Set design OA object attributes
- thermal_zone.spaces.each do |space|
- dsn_oa = space.designSpecificationOutdoorAir
- next if dsn_oa.empty?
- dsn_oa = dsn_oa.get
- # set design specification outdoor air objects to sum
- dsn_oa.setOutdoorAirMethod('Sum')
- # Get the space properties
- floor_area = space.floorArea * space.multiplier
- number_of_people = space.numberOfPeople * space.multiplier
- people_per_m2 = space.peoplePerFloorArea
-
- # Sum up the total OA from all sources
- oa_for_people_per_m2 = people_per_m2 * dsn_oa.outdoorAirFlowperPerson
- oa_for_floor_area_per_m2 = dsn_oa.outdoorAirFlowperFloorArea
- tot_oa_per_m2 = oa_for_people_per_m2 + oa_for_floor_area_per_m2
- tot_oa_cfm_per_ft2 = OpenStudio.convert(OpenStudio.convert(tot_oa_per_m2, 'm^3/s', 'cfm').get, '1/m^2', '1/ft^2').get
- tot_oa_cfm = floor_area * tot_oa_cfm_per_ft2
-
- # if both per-area and per-person are present, does not need to be modified
- if !dsn_oa.outdoorAirFlowperPerson.zero? && !dsn_oa.outdoorAirFlowperFloorArea.zero?
- next
-
- # if both are zero, skip space
- elsif dsn_oa.outdoorAirFlowperPerson.zero? && dsn_oa.outdoorAirFlowperFloorArea.zero?
- #runner.registerInfo("Space '#{space.name}' has 0 outdoor air per-person and per-area rates. DCV may be still be applied to this air loop, but it will not function on this space.")
- next
-
- # if per-person or per-area values are zero, set to 10 cfm / person and allocate the rest to per-area
- elsif dsn_oa.outdoorAirFlowperPerson.zero? || dsn_oa.outdoorAirFlowperFloorArea.zero?
-
- if dsn_oa.outdoorAirFlowperPerson.zero?
- #runner.registerInfo("Space '#{space.name}' per-person outdoor air rate is 0. Using a minimum of 10 cfm / person and assigning the remaining space outdoor air requirement to per-area.")
- elsif dsn_oa.outdoorAirFlowperFloorArea.zero?
- #runner.registerInfo("Space '#{space.name}' per-area outdoor air rate is 0. Using a minimum of 10 cfm / person and assigning the remaining space outdoor air requirement to per-area.")
- end
-
- # default ventilation is 10 cfm / person
- per_person_ventilation_rate = OpenStudio.convert(10, 'ft^3/min', 'm^3/s').get
-
- # assign remaining oa to per-area
- new_oa_for_people_per_m2 = people_per_m2 * per_person_ventilation_rate
- new_oa_for_people_cfm_per_f2 = OpenStudio.convert(OpenStudio.convert(new_oa_for_people_per_m2, 'm^3/s', 'cfm').get, '1/m^2', '1/ft^2').get
- new_oa_for_people_cfm = number_of_people * new_oa_for_people_cfm_per_f2
- remaining_oa_per_m2 = tot_oa_per_m2 - new_oa_for_people_per_m2
- if remaining_oa_per_m2 <= 0
- #runner.registerInfo("Space '#{space.name}' has #{number_of_people.round(1)} people which corresponds to a ventilation minimum requirement of #{new_oa_for_people_cfm.round(0)} cfm at 10 cfm / person, but total zone outdoor air is only #{tot_oa_cfm.round(0)} cfm. Setting all outdoor air as per-person.")
- per_person_ventilation_rate = tot_oa_per_m2 / people_per_m2
- dsn_oa.setOutdoorAirFlowperFloorArea(0.0)
- else
- oa_per_area_per_m2 = remaining_oa_per_m2
- dsn_oa.setOutdoorAirFlowperFloorArea(oa_per_area_per_m2)
- end
- dsn_oa.setOutdoorAirFlowperPerson(per_person_ventilation_rate)
- end
- end
- end
- standard.air_loop_hvac_enable_demand_control_ventilation(air_loop_hvac, '')
- end
- end
- end
- if add_econo #handle economizing if implementing it
- overall_sel_air_loops.sort.each do |air_loop_hvac|
- oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem
- if oa_system.is_initialized
- oa_system = oa_system.get
- else
- #runner.registerInfo("Air loop #{air_loop_hvac.name} does not have outdoor air and cannot economize.")
- next
- end
- sizing_system = air_loop_hvac.sizingSystem
- type_of_load = sizing_system.typeofLoadtoSizeOn
- if type_of_load == 'VentilationRequirement'
- #runner.registerInfo("Air loop #{air_loop_hvac.name} is a DOAS system and cannot economize.")
- next
- end
- oa_controller = oa_system.getControllerOutdoorAir
- current_economizer_type = oa_controller.getEconomizerControlType
- if current_economizer_type == 'NoEconomizer'
- #runner.registerInfo("Air loop #{air_loop_hvac.name} does not have an existing economizer. This measure will add an economizer.")
- selected_air_loops << air_loop_hvac
- else
- #runner.registerInfo("Air loop #{air_loop_hvac.name} has an existing #{current_economizer_type} economizer.")
- next
- end
- # get airLoopHVACOutdoorAirSystem
- oa_sys = air_loop_hvac.airLoopHVACOutdoorAirSystem
- if oa_sys.is_initialized
- oa_sys = oa_sys.get
- else
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.Model', "#{air_loop.name} is required to have an economizer, but it has no OA system.")
- next
- end
- # get controller:outdoorair
- oa_control = oa_sys.getControllerOutdoorAir
- oa_control.setEconomizerControlType(economizer_type)
- if oa_control.getEconomizerControlType != economizer_type
- ##runner.registerInfo("--- adding economizer to air loop hvac = #{air_loop_hvac.name}")
- oa_control.setEconomizerControlType(economizer_type)
- end
- # get economizer limits
- limits = std.air_loop_hvac_economizer_limits(air_loop_hvac, climate_zone) # in IP unit
- # #runner.registerInfo("--- economizer limits [db max|enthal max|dewpoint max] for the climate zone = #{limits}")
- # implement limits for each control type
- case economizer_type
- when 'FixedDryBulb'
- if oa_control.getEconomizerMaximumLimitDryBulbTemperature.is_initialized
- ##runner.registerInfo("--- economizer limit for #{economizer_type} before: #{oa_control.getEconomizerMaximumLimitDryBulbTemperature.get}")
- end
- drybulb_limit_c = OpenStudio.convert(limits[0], 'F', 'C').get
- oa_control.resetEconomizerMaximumLimitDryBulbTemperature
- oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
- # #runner.registerInfo("--- economizer limit for #{economizer_type} new: #{oa_control.getEconomizerMaximumLimitDryBulbTemperature.get}")
- when 'FixedEnthalpy'
- if oa_control.getEconomizerMaximumLimitEnthalpy.is_initialized
- ##runner.registerInfo("--- economizer limit for #{economizer_type} before: #{oa_control.getEconomizerMaximumLimitEnthalpy.get}")
- end
- enthalpy_limit_j_per_kg = OpenStudio.convert(limits[1], 'Btu/lb', 'J/kg').get
- oa_control.resetEconomizerMaximumLimitEnthalpy
- oa_control.setEconomizerMaximumLimitEnthalpy(enthalpy_limit_j_per_kg)
- # #runner.registerInfo("--- economizer limit for #{economizer_type} new: #{oa_control.getEconomizerMaximumLimitEnthalpy.get}")
- when 'FixedDewPointAndDryBulb'
- if oa_control.getEconomizerMaximumLimitDewpointTemperature.is_initialized
- ##runner.registerInfo("--- economizer limit for #{economizer_type} before: #{oa_control.getEconomizerMaximumLimitDewpointTemperature.get}")
- end
- drybulb_limit_f = 75
- dewpoint_limit_f = 55
- drybulb_limit_c = OpenStudio.convert(drybulb_limit_f, 'F', 'C').get
- dewpoint_limit_c = OpenStudio.convert(dewpoint_limit_f, 'F', 'C').get
- oa_control.resetEconomizerMaximumLimitDryBulbTemperature
- oa_control.resetEconomizerMaximumLimitDewpointTemperature
- oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
- oa_control.setEconomizerMaximumLimitDewpointTemperature(dewpoint_limit_c)
- # #runner.registerInfo("--- economizer limit (max db T) for #{economizer_type} new: #{oa_control.getEconomizerMaximumLimitDryBulbTemperature.get}")
- # #runner.registerInfo("--- economizer limit (max dp T) for #{economizer_type} new: #{oa_control.getEconomizerMaximumLimitDewpointTemperature.get}")
- end
- # change/check settings: lockout type
- # #runner.registerInfo("--- economizer lockout type before: #{oa_control.getLockoutType}")
- if oa_control.getLockoutType != "LockoutWithHeating"
- oa_control.setLockoutType("LockoutWithHeating") # integrated economizer
- end
- # #runner.registerInfo("--- economizer lockout type new: #{oa_control.getLockoutType}")
-
- # calc statistics
- added_economizers += 1
- end
- end
-
- if selected_air_loops.size.zero? && add_econo
- #runner.registerInfo('Model contains no air loops eligible for adding an outdoor air economizer.')
- end
- #deal with economizer controls
- if add_econo
- # #runner.registerInfo("### implement EMS for economizing only when cooling")
- # ----------------------------------------------------
- # for ems output variables
- li_ems_clg_coil_rate = []
- li_ems_sens_econ_status = []
- li_ems_sens_min_flow = []
- li_ems_act_oa_flow = []
-
- # loop through air loops
- overall_sel_air_loops.each do |air_loop_hvac|
-
- # get OA system
- oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem
- if oa_system.is_initialized
- oa_system = oa_system.get
- else
- #runner.registerInfo("Air loop #{air_loop_hvac.name} does not have outdoor air and cannot economize.")
- next
- end
-
- # get economizer from OA controller
- oa_controller = oa_system.getControllerOutdoorAir
- # oa_controller.setName(oa_controller.name.to_s.gsub("-", ""))
- economizer_type = oa_controller.getEconomizerControlType
- next unless economizer_type != 'NoEconomizer'
-
- # get zones
- zone = air_loop_hvac.thermalZones[0]
- # zone.setName(zone.name.to_s.gsub("-", ""))
-
- # get main cooling coil from air loop
- # this is used to determine if there is a cooling load on the air loop
- clg_coil=nil
- air_loop_hvac.supplyComponents.each do |component|
- # Get the object type
- obj_type = component.iddObjectType.valueName.to_s
- case obj_type
- when 'OS_Coil_Cooling_DX_SingleSpeed'
- clg_coil = component.to_CoilCoolingDXSingleSpeed.get
- when 'OS_Coil_Cooling_DX_TwoSpeed'
- clg_coil = component.to_CoilCoolingDXTwoSpeed.get
- when 'OS_Coil_Cooling_DX_MultiSpeed'
- clg_coil = component.to_CoilCoolingDXMultiSpeed.get
- when 'OS_Coil_Cooling_DX_VariableSpeed'
- clg_coil = component.to_CoilCoolingDXVariableSpeed.get
- when 'OS_Coil_Cooling_Water'
- clg_coil = component.to_CoilCoolingWater.get
- when 'OS_Coil_Cooling_WaterToAirHeatPumpEquationFit'
- clg_coil = component.to_CoilCoolingWatertoAirHeatPumpEquationFit.get
- when 'OS_AirLoopHVAC_UnitarySystem'
- unitary_sys = component.to_AirLoopHVACUnitarySystem.get
- if unitary_sys.coolingCoil.is_initialized
- clg_coil = unitary_sys.coolingCoil.get
- end
- when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir'
- unitary_sys = component.to_AirLoopHVACUnitaryHeatPumpAirToAir.get
- if unitary_sys.coolingCoil.is_initialized
- clg_coil = unitary_sys.coolingCoil.get
- end
- when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir_MultiSpeed'
- unitary_sys = component.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get
- if unitary_sys.coolingCoil.is_initialized
- clg_coil = unitary_sys.coolingCoil.get
- end
- when 'OS_AirLoopHVAC_UnitaryHeatCool_VAVChangeoverBypass'
- unitary_sys = component.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get
- if unitary_sys.coolingCoil.is_initialized
- clg_coil = unitary_sys.coolingCoil.get
- end
- end
- end
-
- # set sensor for zone cooling load from cooling coil cooling rate
- sens_clg_coil_rate = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Cooling Coil Total Cooling Rate')
- sens_clg_coil_rate.setName("sens_zn_clg_rate_#{std.ems_friendly_name(zone.name.get.to_s)}")
- sens_clg_coil_rate.setKeyName("#{clg_coil.name.get}")
- # EMS variables are added to lists for export
- li_ems_clg_coil_rate << sens_clg_coil_rate
-
- # set sensor - Outdoor Air Controller Minimum Mass Flow Rate
- # TODO need to confirm if this variable is reliable
- sens_min_oa_rate = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Air System Outdoor Air Mechanical Ventilation Requested Mass Flow Rate')
- sens_min_oa_rate.setName("sens_min_oa_flow_#{std.ems_friendly_name(oa_controller.name.get.to_s)}")
- sens_min_oa_rate.setKeyName("#{air_loop_hvac.name.get}")
-
- li_ems_sens_min_flow << sens_min_oa_rate
-
- # set sensor - Air System Outdoor Air Economizer Status
- sens_econ_status = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Air System Outdoor Air Economizer Status')
- sens_econ_status.setName("sens_econ_status_#{std.ems_friendly_name(oa_controller.name.get.to_s)}")
- sens_econ_status.setKeyName("#{air_loop_hvac.name.get}")
- li_ems_sens_econ_status << sens_econ_status
-
- #### Actuators #####
- # set actuator - oa controller air mass flow rate
- act_oa_flow = OpenStudio::Model::EnergyManagementSystemActuator.new(oa_controller,'Outdoor Air Controller', 'Air Mass Flow Rate')
- act_oa_flow.setName("act_oa_flow_#{std.ems_friendly_name(air_loop_hvac.name.get.to_s)}")
- li_ems_act_oa_flow << act_oa_flow
-
- #### Program #####
- # reset OA to min OA if there is a call for economizer but no cooling load
- prgrm_econ_override = model.getEnergyManagementSystemTrendVariableByName('econ_override')
- unless prgrm_econ_override.is_initialized
- prgrm_econ_override = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
- prgrm_econ_override.setName("#{std.ems_friendly_name(air_loop_hvac.name.get.to_s)}_program")
- prgrm_econ_override_body = <<-EMS
+ sup_fan = sup_fan.to_FanOnOff.get
+ pressure_rise = sup_fan.pressureRise
+ motor_hp = standard.fan_motor_horsepower(sup_fan)
+ fan_eff = standard.fan_baseline_impeller_efficiency(sup_fan)
+ if sup_fan.autosizedMaximumFlowRate.is_initialized
+ fan_flow = sup_fan.autosizedMaximumFlowRate.get
+ elsif sup_fan.maximumFlowRate.is_initialized
+ fan_flow = sup_fan.maximumFlowRate.get
+ end
+ # ASHRAE 90.1 2019 version of the standard to reflect motor replacement
+ fan_motor_eff = standard_new_motor.fan_standard_minimum_motor_efficiency_and_size(sup_fan, motor_hp)[0] # calculate fan motor eff per Standards
+ end
+ # handle constant speed fan objects; replace FanConstantVolume with FanVariableVolume
+ if sup_fan.to_FanConstantVolume.is_initialized
+ sup_fan = sup_fan.to_FanConstantVolume.get
+ pressure_rise = sup_fan.pressureRise
+ motor_hp = standard.fan_motor_horsepower(sup_fan)
+ fan_eff = standard.fan_baseline_impeller_efficiency(sup_fan)
+ if sup_fan.autosizedMaximumFlowRate.is_initialized
+ fan_flow = sup_fan.autosizedMaximumFlowRate.get
+ elsif sup_fan.maximumFlowRate.is_initialized
+ fan_flow = sup_fan.maximumFlowRate.get
+ end
+ # ASHRAE 90.1 2019 version of the standard to reflect motor replacement
+ fan_motor_eff = standard_new_motor.fan_standard_minimum_motor_efficiency_and_size(sup_fan, motor_hp)[0] # calculate fan motor eff per Standards
+ end
+ # create new VS fan
+ fan = OpenStudio::Model::FanVariableVolume.new(model)
+ fan.setName("#{air_loop_hvac.name} Fan")
+ fan.setFanPowerMinimumFlowRateInputMethod('Fraction')
+ fan.setPressureRise(pressure_rise) # keep it the same as the existing fan, since the balance of systems is the same
+ fan.setMotorEfficiency(fan_motor_eff)
+ fan.setMaximumFlowRate(fan_flow) # keep it the same as the existing fan, since the fan itself will be the same
+ fan.setFanTotalEfficiency(fan_motor_eff * fan_eff)
+ # set fan curve coefficients
+ standard.fan_variable_volume_set_control_type(fan, 'Single Zone VAV Fan ')
+ fan.setFanPowerMinimumFlowFraction(min_flow_fraction) # resetting minimum flow fraction to be appropriate for retrofit as opposed to 10% in method above
+ # Add it to the unitary sys
+ component.setSupplyFan(fan)
+ end
+ end
+ end
+ air_loop_hvac.thermalZones.each do |thermal_zone| # iterate thru thermal zones and modify zone-level terminal units
+ min_oa_flow_rate_cont = 0
+ # See if a minimum OA flow rate is already set
+ if air_loop_hvac.airLoopHVACOutdoorAirSystem.is_initialized
+ oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get
+ controller_oa = oa_system.getControllerOutdoorAir
+ if controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
+ min_oa_flow_rate_cont = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
+ elsif controller_oa.minimumOutdoorAirFlowRate.is_initialized
+ min_oa_flow_rate_cont = controller_oa.minimumOutdoorAirFlowRate.get
+ end
+ end
+ # if min OA flow rate is 0, or if it isn't set, calculate it
+ if min_oa_flow_rate_cont == 0
+ min_oa_flow_rate = thermal_zone_outdoor_airflow_rate(thermal_zone)
+ else
+ min_oa_flow_rate = min_oa_flow_rate_cont
+ end
+ thermal_zone.equipment.each do |equip|
+ if equip.to_AirTerminalSingleDuctConstantVolumeNoReheat.is_initialized
+ term = equip.to_AirTerminalSingleDuctConstantVolumeNoReheat.get
+ new_term = OpenStudio::Model::AirTerminalSingleDuctVAVHeatAndCoolNoReheat.new(model) # create new terminal unit
+ if term.autosizedMaximumAirFlowRate.is_initialized
+ des_airflow_rate = term.autosizedMaximumAirFlowRate.get
+ new_term.setMaximumAirFlowRate(des_airflow_rate) # same as before
+ # set minimum based on max of 40% of max flow, or min ventilation level req'd
+ new_term.setZoneMinimumAirFlowFraction([min_flow, min_oa_flow_rate / des_airflow_rate].max)
+ elsif term.maximumAirFlowRate.is_initialized
+ des_airflow_rate = term.maximumAirFlowRate.get
+ new_term.setMaximumAirFlowRate(des_airflow_rate) # same as before
+ # set minimum based on max of 40% of max flow, or min ventilation level req'd
+ new_term.setZoneMinimumAirFlowFraction([min_flow, min_oa_flow_rate / des_airflow_rate].max)
+ end
+ air_loop_hvac.removeBranchForZone(thermal_zone)
+ air_loop_hvac.addBranchForZone(thermal_zone, new_term)
+ end
+ end
+ end
+ end
+ # handle DCV in appropriate air loops, after screening out those that aren't suitable
+ if add_dcv
+ overall_sel_air_loops.sort.each do |air_loop_hvac|
+ unless no_dcv_zones?(air_loop_hvac)
+ oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get
+ controller_oa = oa_system.getControllerOutdoorAir
+ controller_mv = controller_oa.controllerMechanicalVentilation
+ controller_mv.setDemandControlledVentilation(true)
+ air_loop_hvac.thermalZones.each do |thermal_zone|
+ # Set design OA object attributes
+ thermal_zone.spaces.each do |space|
+ dsn_oa = space.designSpecificationOutdoorAir
+ next if dsn_oa.empty?
+
+ dsn_oa = dsn_oa.get
+ # set design specification outdoor air objects to sum
+ dsn_oa.setOutdoorAirMethod('Sum')
+ # Get the space properties
+ floor_area = space.floorArea * space.multiplier
+ number_of_people = space.numberOfPeople * space.multiplier
+ people_per_m2 = space.peoplePerFloorArea
+
+ # Sum up the total OA from all sources
+ oa_for_people_per_m2 = people_per_m2 * dsn_oa.outdoorAirFlowperPerson
+ oa_for_floor_area_per_m2 = dsn_oa.outdoorAirFlowperFloorArea
+ tot_oa_per_m2 = oa_for_people_per_m2 + oa_for_floor_area_per_m2
+ tot_oa_cfm_per_ft2 = OpenStudio.convert(OpenStudio.convert(tot_oa_per_m2, 'm^3/s', 'cfm').get, '1/m^2', '1/ft^2').get
+ tot_oa_cfm = floor_area * tot_oa_cfm_per_ft2
+
+ # if both per-area and per-person are present, does not need to be modified
+ # if both are zero, skip space
+ if (!dsn_oa.outdoorAirFlowperPerson.zero? && !dsn_oa.outdoorAirFlowperFloorArea.zero?) ||
+ (dsn_oa.outdoorAirFlowperPerson.zero? && dsn_oa.outdoorAirFlowperFloorArea.zero?)
+ # runner.registerInfo("Space '#{space.name}' has 0 outdoor air per-person and per-area rates. DCV may be still be applied to this air loop, but it will not function on this space.") if dsn_oa.outdoorAirFlowperPerson.zero?
+ next
+
+ # if per-person or per-area values are zero, set to 10 cfm / person and allocate the rest to per-area
+ elsif dsn_oa.outdoorAirFlowperPerson.zero? || dsn_oa.outdoorAirFlowperFloorArea.zero?
+
+ if dsn_oa.outdoorAirFlowperPerson.zero?
+ # runner.registerInfo("Space '#{space.name}' per-person outdoor air rate is 0. Using a minimum of 10 cfm / person and assigning the remaining space outdoor air requirement to per-area.")
+ elsif dsn_oa.outdoorAirFlowperFloorArea.zero?
+ # runner.registerInfo("Space '#{space.name}' per-area outdoor air rate is 0. Using a minimum of 10 cfm / person and assigning the remaining space outdoor air requirement to per-area.")
+ end
+
+ # default ventilation is 10 cfm / person
+ per_person_ventilation_rate = OpenStudio.convert(10, 'ft^3/min', 'm^3/s').get
+
+ # assign remaining oa to per-area
+ new_oa_for_people_per_m2 = people_per_m2 * per_person_ventilation_rate
+ new_oa_for_people_cfm_per_f2 = OpenStudio.convert(OpenStudio.convert(new_oa_for_people_per_m2, 'm^3/s', 'cfm').get, '1/m^2', '1/ft^2').get
+ new_oa_for_people_cfm = number_of_people * new_oa_for_people_cfm_per_f2
+ remaining_oa_per_m2 = tot_oa_per_m2 - new_oa_for_people_per_m2
+ if remaining_oa_per_m2 <= 0
+ # runner.registerInfo("Space '#{space.name}' has #{number_of_people.round(1)} people which corresponds to a ventilation minimum requirement of #{new_oa_for_people_cfm.round(0)} cfm at 10 cfm / person, but total zone outdoor air is only #{tot_oa_cfm.round(0)} cfm. Setting all outdoor air as per-person.")
+ per_person_ventilation_rate = tot_oa_per_m2 / people_per_m2
+ dsn_oa.setOutdoorAirFlowperFloorArea(0.0)
+ else
+ oa_per_area_per_m2 = remaining_oa_per_m2
+ dsn_oa.setOutdoorAirFlowperFloorArea(oa_per_area_per_m2)
+ end
+ dsn_oa.setOutdoorAirFlowperPerson(per_person_ventilation_rate)
+ end
+ end
+ end
+ standard.air_loop_hvac_enable_demand_control_ventilation(air_loop_hvac, '')
+ end
+ end
+ end
+ if add_econo # handle economizing if implementing it
+ overall_sel_air_loops.sort.each do |air_loop_hvac|
+ oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem
+ next unless oa_system.is_initialized
+
+ oa_system = oa_system.get
+
+ # runner.registerInfo("Air loop #{air_loop_hvac.name} does not have outdoor air and cannot economize.")
+
+
+ sizing_system = air_loop_hvac.sizingSystem
+ type_of_load = sizing_system.typeofLoadtoSizeOn
+ if type_of_load == 'VentilationRequirement'
+ # runner.registerInfo("Air loop #{air_loop_hvac.name} is a DOAS system and cannot economize.")
+ next
+ end
+
+ oa_controller = oa_system.getControllerOutdoorAir
+ current_economizer_type = oa_controller.getEconomizerControlType
+ next unless current_economizer_type == 'NoEconomizer'
+
+ # runner.registerInfo("Air loop #{air_loop_hvac.name} does not have an existing economizer. This measure will add an economizer.")
+ selected_air_loops << air_loop_hvac
+
+ # runner.registerInfo("Air loop #{air_loop_hvac.name} has an existing #{current_economizer_type} economizer.")
+
+
+ # get airLoopHVACOutdoorAirSystem
+ oa_sys = air_loop_hvac.airLoopHVACOutdoorAirSystem
+ if oa_sys.is_initialized
+ oa_sys = oa_sys.get
+ else
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.Model', "#{air_loop.name} is required to have an economizer, but it has no OA system.")
+ next
+ end
+ # get controller:outdoorair
+ oa_control = oa_sys.getControllerOutdoorAir
+ oa_control.setEconomizerControlType(economizer_type)
+ if oa_control.getEconomizerControlType != economizer_type
+ # #runner.registerInfo("--- adding economizer to air loop hvac = #{air_loop_hvac.name}")
+ oa_control.setEconomizerControlType(economizer_type)
+ end
+ # get economizer limits
+ limits = std.air_loop_hvac_economizer_limits(air_loop_hvac, climate_zone) # in IP unit
+ # #runner.registerInfo("--- economizer limits [db max|enthal max|dewpoint max] for the climate zone = #{limits}")
+ # implement limits for each control type
+ case economizer_type
+ when 'FixedDryBulb'
+ if oa_control.getEconomizerMaximumLimitDryBulbTemperature.is_initialized
+ # #runner.registerInfo("--- economizer limit for #{economizer_type} before: #{oa_control.getEconomizerMaximumLimitDryBulbTemperature.get}")
+ end
+ drybulb_limit_c = OpenStudio.convert(limits[0], 'F', 'C').get
+ oa_control.resetEconomizerMaximumLimitDryBulbTemperature
+ oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
+ # #runner.registerInfo("--- economizer limit for #{economizer_type} new: #{oa_control.getEconomizerMaximumLimitDryBulbTemperature.get}")
+ when 'FixedEnthalpy'
+ if oa_control.getEconomizerMaximumLimitEnthalpy.is_initialized
+ # #runner.registerInfo("--- economizer limit for #{economizer_type} before: #{oa_control.getEconomizerMaximumLimitEnthalpy.get}")
+ end
+ enthalpy_limit_j_per_kg = OpenStudio.convert(limits[1], 'Btu/lb', 'J/kg').get
+ oa_control.resetEconomizerMaximumLimitEnthalpy
+ oa_control.setEconomizerMaximumLimitEnthalpy(enthalpy_limit_j_per_kg)
+ # #runner.registerInfo("--- economizer limit for #{economizer_type} new: #{oa_control.getEconomizerMaximumLimitEnthalpy.get}")
+ when 'FixedDewPointAndDryBulb'
+ if oa_control.getEconomizerMaximumLimitDewpointTemperature.is_initialized
+ # #runner.registerInfo("--- economizer limit for #{economizer_type} before: #{oa_control.getEconomizerMaximumLimitDewpointTemperature.get}")
+ end
+ drybulb_limit_f = 75
+ dewpoint_limit_f = 55
+ drybulb_limit_c = OpenStudio.convert(drybulb_limit_f, 'F', 'C').get
+ dewpoint_limit_c = OpenStudio.convert(dewpoint_limit_f, 'F', 'C').get
+ oa_control.resetEconomizerMaximumLimitDryBulbTemperature
+ oa_control.resetEconomizerMaximumLimitDewpointTemperature
+ oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
+ oa_control.setEconomizerMaximumLimitDewpointTemperature(dewpoint_limit_c)
+ # #runner.registerInfo("--- economizer limit (max db T) for #{economizer_type} new: #{oa_control.getEconomizerMaximumLimitDryBulbTemperature.get}")
+ # #runner.registerInfo("--- economizer limit (max dp T) for #{economizer_type} new: #{oa_control.getEconomizerMaximumLimitDewpointTemperature.get}")
+ end
+ # change/check settings: lockout type
+ # #runner.registerInfo("--- economizer lockout type before: #{oa_control.getLockoutType}")
+ if oa_control.getLockoutType != 'LockoutWithHeating'
+ oa_control.setLockoutType('LockoutWithHeating') # integrated economizer
+ end
+ # #runner.registerInfo("--- economizer lockout type new: #{oa_control.getLockoutType}")
+
+ # calc statistics
+ added_economizers += 1
+ end
+ end
+
+ if selected_air_loops.empty? && add_econo
+ # runner.registerInfo('Model contains no air loops eligible for adding an outdoor air economizer.')
+ end
+ # deal with economizer controls
+ if add_econo
+ # #runner.registerInfo("### implement EMS for economizing only when cooling")
+ # ----------------------------------------------------
+ # for ems output variables
+ li_ems_clg_coil_rate = []
+ li_ems_sens_econ_status = []
+ li_ems_sens_min_flow = []
+ li_ems_act_oa_flow = []
+
+ # loop through air loops
+ overall_sel_air_loops.each do |air_loop_hvac|
+ # get OA system
+ oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem
+ next unless oa_system.is_initialized
+
+ oa_system = oa_system.get
+
+ # runner.registerInfo("Air loop #{air_loop_hvac.name} does not have outdoor air and cannot economize.")
+
+
+
+ # get economizer from OA controller
+ oa_controller = oa_system.getControllerOutdoorAir
+ # oa_controller.setName(oa_controller.name.to_s.gsub("-", ""))
+ economizer_type = oa_controller.getEconomizerControlType
+ next unless economizer_type != 'NoEconomizer'
+
+ # get zones
+ zone = air_loop_hvac.thermalZones[0]
+ # zone.setName(zone.name.to_s.gsub("-", ""))
+
+ # get main cooling coil from air loop
+ # this is used to determine if there is a cooling load on the air loop
+ clg_coil = nil
+ air_loop_hvac.supplyComponents.each do |component|
+ # Get the object type
+ obj_type = component.iddObjectType.valueName.to_s
+ case obj_type
+ when 'OS_Coil_Cooling_DX_SingleSpeed'
+ clg_coil = component.to_CoilCoolingDXSingleSpeed.get
+ when 'OS_Coil_Cooling_DX_TwoSpeed'
+ clg_coil = component.to_CoilCoolingDXTwoSpeed.get
+ when 'OS_Coil_Cooling_DX_MultiSpeed'
+ clg_coil = component.to_CoilCoolingDXMultiSpeed.get
+ when 'OS_Coil_Cooling_DX_VariableSpeed'
+ clg_coil = component.to_CoilCoolingDXVariableSpeed.get
+ when 'OS_Coil_Cooling_Water'
+ clg_coil = component.to_CoilCoolingWater.get
+ when 'OS_Coil_Cooling_WaterToAirHeatPumpEquationFit'
+ clg_coil = component.to_CoilCoolingWatertoAirHeatPumpEquationFit.get
+ when 'OS_AirLoopHVAC_UnitarySystem'
+ unitary_sys = component.to_AirLoopHVACUnitarySystem.get
+ if unitary_sys.coolingCoil.is_initialized
+ clg_coil = unitary_sys.coolingCoil.get
+ end
+ when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir'
+ unitary_sys = component.to_AirLoopHVACUnitaryHeatPumpAirToAir.get
+ if unitary_sys.coolingCoil.is_initialized
+ clg_coil = unitary_sys.coolingCoil.get
+ end
+ when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir_MultiSpeed', 'OS_AirLoopHVAC_UnitaryHeatCool_VAVChangeoverBypass'
+ unitary_sys = component.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get
+ if unitary_sys.coolingCoil.is_initialized
+ clg_coil = unitary_sys.coolingCoil.get
+ end
+ end
+ end
+
+ # set sensor for zone cooling load from cooling coil cooling rate
+ sens_clg_coil_rate = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Cooling Coil Total Cooling Rate')
+ sens_clg_coil_rate.setName("sens_zn_clg_rate_#{std.ems_friendly_name(zone.name.get.to_s)}")
+ sens_clg_coil_rate.setKeyName(clg_coil.name.get.to_s)
+ # EMS variables are added to lists for export
+ li_ems_clg_coil_rate << sens_clg_coil_rate
+
+ # set sensor - Outdoor Air Controller Minimum Mass Flow Rate
+ # TODO need to confirm if this variable is reliable
+ sens_min_oa_rate = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Air System Outdoor Air Mechanical Ventilation Requested Mass Flow Rate')
+ sens_min_oa_rate.setName("sens_min_oa_flow_#{std.ems_friendly_name(oa_controller.name.get.to_s)}")
+ sens_min_oa_rate.setKeyName(air_loop_hvac.name.get.to_s)
+
+ li_ems_sens_min_flow << sens_min_oa_rate
+
+ # set sensor - Air System Outdoor Air Economizer Status
+ sens_econ_status = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Air System Outdoor Air Economizer Status')
+ sens_econ_status.setName("sens_econ_status_#{std.ems_friendly_name(oa_controller.name.get.to_s)}")
+ sens_econ_status.setKeyName(air_loop_hvac.name.get.to_s)
+ li_ems_sens_econ_status << sens_econ_status
+
+ #### Actuators #####
+ # set actuator - oa controller air mass flow rate
+ act_oa_flow = OpenStudio::Model::EnergyManagementSystemActuator.new(oa_controller, 'Outdoor Air Controller', 'Air Mass Flow Rate')
+ act_oa_flow.setName("act_oa_flow_#{std.ems_friendly_name(air_loop_hvac.name.get.to_s)}")
+ li_ems_act_oa_flow << act_oa_flow
+
+ #### Program #####
+ # reset OA to min OA if there is a call for economizer but no cooling load
+ prgrm_econ_override = model.getEnergyManagementSystemTrendVariableByName('econ_override')
+ unless prgrm_econ_override.is_initialized
+ prgrm_econ_override = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
+ prgrm_econ_override.setName("#{std.ems_friendly_name(air_loop_hvac.name.get.to_s)}_program")
+ prgrm_econ_override_body = <<-EMS
SET #{act_oa_flow.name} = #{act_oa_flow.name},
SET sens_zn_clg_rate = #{sens_clg_coil_rate.name},
SET sens_min_oa_rate = #{sens_min_oa_rate.name},
@@ -717,17 +713,17 @@ def run(model, runner, user_arguments)
ELSE,
SET #{act_oa_flow.name} = Null,
ENDIF
- EMS
- prgrm_econ_override.setBody(prgrm_econ_override_body)
- end
- programs_at_beginning_of_timestep = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
- programs_at_beginning_of_timestep.setName("#{std.ems_friendly_name(air_loop_hvac.name.get.to_s)}_Programs_At_Beginning_Of_Timestep")
- programs_at_beginning_of_timestep.setCallingPoint('InsideHVACSystemIterationLoop')
- programs_at_beginning_of_timestep.addProgram(prgrm_econ_override)
- end
+ EMS
+ prgrm_econ_override.setBody(prgrm_econ_override_body)
+ end
+ programs_at_beginning_of_timestep = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
+ programs_at_beginning_of_timestep.setName("#{std.ems_friendly_name(air_loop_hvac.name.get.to_s)}_Programs_At_Beginning_Of_Timestep")
+ programs_at_beginning_of_timestep.setCallingPoint('InsideHVACSystemIterationLoop')
+ programs_at_beginning_of_timestep.addProgram(prgrm_econ_override)
+ end
end
- return true
-end
+ return true
+ end
end
# register the measure to be used by the application
AdvancedRTUControl.new.registerWithApplication
diff --git a/resources/measures/upgrade_advanced_rtu_control/tests/advanced_rtu_control_test.rb b/resources/measures/upgrade_advanced_rtu_control/tests/advanced_rtu_control_test.rb
index adc25ce9b..f2bb8b958 100644
--- a/resources/measures/upgrade_advanced_rtu_control/tests/advanced_rtu_control_test.rb
+++ b/resources/measures/upgrade_advanced_rtu_control/tests/advanced_rtu_control_test.rb
@@ -41,11 +41,10 @@
require 'openstudio/measure/ShowRunnerOutput'
require 'fileutils'
require 'minitest/autorun'
-require_relative '../measure.rb'
+require_relative '../measure'
require_relative '../../../../test/helpers/minitest_helper'
class AdvancedRTUControlTest < Minitest::Test
-
# return file paths to test models in test directory
def models_for_tests
paths = Dir.glob(File.join(File.dirname(__FILE__), '../../../tests/models/*.osm'))
@@ -61,6 +60,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -70,13 +70,13 @@ def load_model(osm_path)
def run_dir(test_name)
# always generate test output in specially named 'output' directory so result files are not made part of the measure
- puts "run dir expanded=" + "#{File.expand_path(File.join(File.dirname(__FILE__),'output', test_name.to_s))}"
- return File.join(File.dirname(__FILE__),"output","#{test_name}")
+ puts "run dir expanded=#{File.expand_path(File.join(File.dirname(__FILE__), 'output', test_name.to_s))}"
+ return File.join(File.dirname(__FILE__), 'output', test_name.to_s)
end
def model_input_path(osm_name)
# return models_for_tests.select { |x| set[:model] == osm_name }
- puts (File.expand_path(File.dirname(__FILE__))) #expands path relative to current wd, passing abs path back
+ puts(__dir__) # expands path relative to current wd, passing abs path back
return File.expand_path(File.join(File.dirname(__FILE__), '../../../tests/models', osm_name))
end
@@ -102,25 +102,21 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
assert(File.exist?(epw_path))
# create run directory if it does not exist
- if !File.exist?(run_dir(test_name))
- FileUtils.mkdir_p(run_dir(test_name))
- end
+ FileUtils.mkdir_p(run_dir(test_name))
assert(File.exist?(run_dir(test_name)))
# remove prior runs if they exist
# if File.exist?(model_output_path(test_name))
- # FileUtils.rm(model_output_path(test_name))
+ # FileUtils.rm(model_output_path(test_name))
# end
- if File.exist?(report_path(test_name))
- FileUtils.rm(report_path(test_name))
- end
+ FileUtils.rm_f(report_path(test_name))
# copy the osm and epw to the test directory
- #osm_path = File.expand_path(osm_path)
- puts(osm_path)
+ # osm_path = File.expand_path(osm_path)
+ puts(osm_path)
new_osm_path = "#{run_dir(test_name)}/#{File.basename(osm_path)}"
- new_osm_path = File.expand_path(new_osm_path)
- puts(new_osm_path)
+ new_osm_path = File.expand_path(new_osm_path)
+ puts(new_osm_path)
FileUtils.cp(osm_path, new_osm_path)
new_epw_path = File.expand_path("#{run_dir(test_name)}/#{File.basename(epw_path)}")
FileUtils.cp(epw_path, new_epw_path)
@@ -144,7 +140,7 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
std = Standard.build('90.1-2013')
std.model_run_sizing_run(model, run_dir(test_name))
end
- assert(File.exist?(File.join(run_dir(test_name), "in.osm")))
+ assert(File.exist?(File.join(run_dir(test_name), 'in.osm')))
assert(File.exist?(sql_path(test_name)))
# change into run directory for tests
@@ -158,14 +154,14 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
result = runner.result
result_success = result.value.valueName == 'Success'
- # change back directory
+ # change back directory
Dir.chdir(start_dir)
# Show the output
show_output(result)
# Save model
- puts "saving model to" + File.expand_path(model_output_path(test_name))
+ puts "saving model to#{File.expand_path(model_output_path(test_name))}"
model.save(File.expand_path(model_output_path(test_name)), true)
if run_model && result_success
@@ -197,9 +193,7 @@ def test_number_of_arguments_and_argument_names
assert_equal(2, arguments.size)
end
-
-
- def test_econo
+ def test_econo
osm_name = '361_Small_Office_PSZ_Gas_3a.osm'
epw_name = 'CA_LOS-ANGELES-DOWNTOWN-USC_722874S_16.epw'
@@ -213,9 +207,9 @@ def test_econo
model = load_model(osm_path)
arguments = measure.arguments(model)
argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
- #put base case assertions here
+ # put base case assertions here
# create hash of argument values
- args_hash = { 'add_econo' => true, 'add_dcv' => false}
+ args_hash = { 'add_econo' => true, 'add_dcv' => false }
# populate argument with specified hash value if specified
arguments.each do |arg|
temp_arg_var = arg.clone
@@ -229,25 +223,24 @@ def test_econo
# Apply the measure to the model and optionally run the model
result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
model = load_model(File.expand_path(model_output_path(__method__)))
- #confirm that at least one air loop now has an economizer
- has_econo = false
- model.getAirLoopHVACs.sort.each do |air_loop_hvac|
- oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem
- if oa_system.is_initialized
- oa_system = oa_system.get
- oa_controller = oa_system.getControllerOutdoorAir
- economizer_type = oa_controller.getEconomizerControlType
- if economizer_type != 'NoEconomizer'
- has_econo = true
- end
- else
- runner.registerInfo("Air loop #{air_loop_hvac.name} does not have outdoor air and cannot economize.")
- end
-
- end
- assert(has_econo)
-#put in assertions here
-#then duplicate it for other models if needed
+ # confirm that at least one air loop now has an economizer
+ has_econo = false
+ model.getAirLoopHVACs.sort.each do |air_loop_hvac|
+ oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem
+ if oa_system.is_initialized
+ oa_system = oa_system.get
+ oa_controller = oa_system.getControllerOutdoorAir
+ economizer_type = oa_controller.getEconomizerControlType
+ if economizer_type != 'NoEconomizer'
+ has_econo = true
+ end
+ else
+ runner.registerInfo("Air loop #{air_loop_hvac.name} does not have outdoor air and cannot economize.")
+ end
+ end
+ assert(has_econo)
+ # put in assertions here
+ # then duplicate it for other models if needed
end
def test_var_vol_fan
@@ -266,9 +259,9 @@ def test_var_vol_fan
model = load_model(osm_path)
arguments = measure.arguments(model)
argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
- #put base case assertions here
+ # put base case assertions here
# create hash of argument values
- args_hash = { 'add_econo' => false, 'add_dcv' => false}
+ args_hash = { 'add_econo' => false, 'add_dcv' => false }
# populate argument with specified hash value if specified
arguments.each do |arg|
temp_arg_var = arg.clone
@@ -283,26 +276,24 @@ def test_var_vol_fan
result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
model = load_model(File.expand_path(model_output_path(__method__)))
- var_vol_fan = false
- model.getAirLoopHVACs.sort.each do |air_loop_hvac|
- air_loop_hvac.supplyComponents.each do |component|
- obj_type = component.iddObjectType.valueName.to_s
- case obj_type
- when 'OS_AirLoopHVAC_UnitarySystem'
- component = component.to_AirLoopHVACUnitarySystem.get
- sup_fan = component.supplyFan
- if sup_fan.is_initialized
- sup_fan = sup_fan.get
- if sup_fan.to_FanVariableVolume.is_initialized
- var_vol_fan = true
- end
- end
- end
+ var_vol_fan = false
+ model.getAirLoopHVACs.sort.each do |air_loop_hvac|
+ air_loop_hvac.supplyComponents.each do |component|
+ obj_type = component.iddObjectType.valueName.to_s
+ case obj_type
+ when 'OS_AirLoopHVAC_UnitarySystem'
+ component = component.to_AirLoopHVACUnitarySystem.get
+ sup_fan = component.supplyFan
+ if sup_fan.is_initialized
+ sup_fan = sup_fan.get
+ if sup_fan.to_FanVariableVolume.is_initialized
+ var_vol_fan = true
+ end
+ end
+ end
end
- end
-
- assert(var_vol_fan)
+ end
+ assert(var_vol_fan)
end
-
end
diff --git a/resources/measures/upgrade_df_load_shed/resources/call_other_measures.rb b/resources/measures/upgrade_df_load_shed/call_other_measures.rb
similarity index 98%
rename from resources/measures/upgrade_df_load_shed/resources/call_other_measures.rb
rename to resources/measures/upgrade_df_load_shed/call_other_measures.rb
index 40c36324d..2e1aef2ea 100644
--- a/resources/measures/upgrade_df_load_shed/resources/call_other_measures.rb
+++ b/resources/measures/upgrade_df_load_shed/call_other_measures.rb
@@ -76,7 +76,7 @@ def child_to_parent_runner_logging(runner_parent, measure_name, results_child, r
# create methods to call other measures for package runs
# putting this code in a resource file prevents issues with the OS app parsing
def call_pv(model, runner)
- pv_measure_path = File.join(__dir__, '../../upgrade_add_pvwatts/measure.rb')
+ pv_measure_path = File.join(__dir__, '../upgrade_add_pvwatts/measure.rb')
unless File.exist?(pv_measure_path)
runner.registerError('PV measure not found. Check that this measure exists in your file structure and modify the measure path if necessary.')
return false
diff --git a/resources/measures/upgrade_df_load_shed/resources/dispatch_schedule_generation.rb b/resources/measures/upgrade_df_load_shed/dispatch_schedule_generation.rb
similarity index 96%
rename from resources/measures/upgrade_df_load_shed/resources/dispatch_schedule_generation.rb
rename to resources/measures/upgrade_df_load_shed/dispatch_schedule_generation.rb
index 5b659e13e..de2c16418 100644
--- a/resources/measures/upgrade_df_load_shed/resources/dispatch_schedule_generation.rb
+++ b/resources/measures/upgrade_df_load_shed/dispatch_schedule_generation.rb
@@ -41,57 +41,57 @@
require 'openstudio-standards'
def cambium_emissions_scenarios
- %w[
- AER_95DecarbBy2035
- AER_95DecarbBy2050
- AER_HighRECost
- AER_LowRECost
- AER_MidCase
- LRMER_95DecarbBy2035_15
- LRMER_95DecarbBy2035_30
- LRMER_95DecarbBy2035_15_2025start
- LRMER_95DecarbBy2035_25_2025start
- LRMER_95DecarbBy2050_15
- LRMER_95DecarbBy2050_30
- LRMER_HighRECost_15
- LRMER_HighRECost_30
- LRMER_LowRECost_15
- LRMER_LowRECost_30
- LRMER_LowRECost_15_2025start
- LRMER_LowRECost_25_2025start
- LRMER_MidCase_15
- LRMER_MidCase_30
- LRMER_MidCase_15_2025start
- LRMER_MidCase_25_2025start
+ [
+ 'AER_95DecarbBy2035',
+ 'AER_95DecarbBy2050',
+ 'AER_HighRECost',
+ 'AER_LowRECost',
+ 'AER_MidCase',
+ 'LRMER_95DecarbBy2035_15',
+ 'LRMER_95DecarbBy2035_30',
+ 'LRMER_95DecarbBy2035_15_2025start',
+ 'LRMER_95DecarbBy2035_25_2025start',
+ 'LRMER_95DecarbBy2050_15',
+ 'LRMER_95DecarbBy2050_30',
+ 'LRMER_HighRECost_15',
+ 'LRMER_HighRECost_30',
+ 'LRMER_LowRECost_15',
+ 'LRMER_LowRECost_30',
+ 'LRMER_LowRECost_15_2025start',
+ 'LRMER_LowRECost_25_2025start',
+ 'LRMER_MidCase_15',
+ 'LRMER_MidCase_30',
+ 'LRMER_MidCase_15_2025start',
+ 'LRMER_MidCase_25_2025start'
]
end
def grid_regions
- %w[
- AZNMc
- AKGD
- AKMS
- CAMXc
- ERCTc
- FRCCc
- HIMS
- HIOA
- MROEc
- MROWc
- NEWEc
- NWPPc
- NYSTc
- RFCEc
- RFCMc
- RFCWc
- RMPAc
- SPNOc
- SPSOc
- SRMVc
- SRMWc
- SRSOc
- SRTVc
- SRVCc
+ [
+ 'AZNMc',
+ 'AKGD',
+ 'AKMS',
+ 'CAMXc',
+ 'ERCTc',
+ 'FRCCc',
+ 'HIMS',
+ 'HIOA',
+ 'MROEc',
+ 'MROWc',
+ 'NEWEc',
+ 'NWPPc',
+ 'NYSTc',
+ 'RFCEc',
+ 'RFCMc',
+ 'RFCWc',
+ 'RMPAc',
+ 'SPNOc',
+ 'SPSOc',
+ 'SRMVc',
+ 'SRMWc',
+ 'SRSOc',
+ 'SRTVc',
+ 'SRVCc'
]
end
@@ -914,7 +914,7 @@ def read_emission_factors(model, scenario, year = 2021)
grid_region = grid_region.get
puts("Using grid region #{grid_region} from model building additional properties.")
- if %w[AKMS AKGD HIMS HIOA].include? grid_region
+ if ['AKMS', 'AKGD', 'HIMS', 'HIOA'].include? grid_region
cambium_grid_region = nil
egrid_region = grid_region
puts("Grid region '#{grid_region}' is not available in Cambium. Using eGrid factors only for electricty related emissions.")
@@ -923,7 +923,7 @@ def read_emission_factors(model, scenario, year = 2021)
egrid_region = grid_region.chop
end
# read egrid factors
- egrid_subregion_emissions_factors_csv = "#{File.dirname(__FILE__)}/egrid/egrid_subregion_emissions_factors.csv"
+ egrid_subregion_emissions_factors_csv = "#{File.dirname(__FILE__)}/resources/egrid/egrid_subregion_emissions_factors.csv"
unless File.file?(egrid_subregion_emissions_factors_csv)
raise "Unable to find file: #{egrid_subregion_emissions_factors_csv}"
end
@@ -948,7 +948,7 @@ def read_emission_factors(model, scenario, year = 2021)
else
scenario
end
- emissions_csv = "#{File.dirname(__FILE__)}/cambium/#{scenario_lookup}/#{cambium_grid_region}.csv"
+ emissions_csv = "#{File.dirname(__FILE__)}/resources/cambium/#{scenario_lookup}/#{cambium_grid_region}.csv"
raise "Unable to find file: #{emissions_csv}" unless File.file?(emissions_csv)
cambium_co2e_kg_per_mwh = CSV.read(emissions_csv, converters: :float).flatten
@@ -981,7 +981,7 @@ def emissions_prediction(load, factor, num_timesteps_in_hr)
raise 'Unable to calculate emissions for run periods not of length 8760 or 8784'
end
- puts("Leap year but emissions factor data has 8760 hours. Copying Feb 28 data for Feb 29.")
+ puts('Leap year but emissions factor data has 8760 hours. Copying Feb 28 data for Feb 29.')
factor = factor[0..1415] + factor[1392..1415] + factor[1416..8759]
end
hourly_emissions_kg = hourly_load_mwh.zip(factor).map { |n, f| n * f }
@@ -999,19 +999,18 @@ def load_prediction_from_grid_data(model, scenario = 'Load_MidCase_2035')
grid_region = model.getBuilding.additionalProperties.getFeatureAsString('grid_region')
raise 'Unable to find grid region in model building additional properties' unless grid_region.is_initialized
grid_region = grid_region.get
- load_csv = "#{File.dirname(__FILE__)}/cambium/#{scenario}/#{grid_region}.csv"
+ load_csv = "#{File.dirname(__FILE__)}/resources/cambium/#{scenario}/#{grid_region}.csv"
raise "Unable to find file: #{load_csv}" unless File.file?(load_csv)
+
grid_load_data = CSV.read(load_csv, converters: :float).flatten
year = model.getYearDescription.calendarYear.to_i
- if leap_year?(year)
- if grid_load_data.size == 8760
- puts("Leap year but grid load data has 8760 hours. Copying Feb 28 data for Feb 29.")
- # hour index at which Feb 28 starts in a non-leap year
- feb_28_start = (31 + 28 - 1) * 24
- grid_load_data = grid_load_data[0...feb_28_start + 24] +
- grid_load_data[feb_28_start...feb_28_start + 24] +
- grid_load_data[feb_28_start + 24..-1]
- end
+ if leap_year?(year) && (grid_load_data.size == 8760)
+ puts('Leap year but grid load data has 8760 hours. Copying Feb 28 data for Feb 29.')
+ # hour index at which Feb 28 starts in a non-leap year
+ feb_28_start = (31 + 28 - 1) * 24
+ grid_load_data = grid_load_data[0...feb_28_start + 24] +
+ grid_load_data[feb_28_start...feb_28_start + 24] +
+ grid_load_data[feb_28_start + 24..-1]
end
grid_load_data
end
diff --git a/resources/measures/upgrade_df_load_shed/measure.rb b/resources/measures/upgrade_df_load_shed/measure.rb
index d3b6ce131..7077077eb 100644
--- a/resources/measures/upgrade_df_load_shed/measure.rb
+++ b/resources/measures/upgrade_df_load_shed/measure.rb
@@ -36,7 +36,7 @@
# *******************************************************************************
# require all .rb files in resources folder
-Dir["#{File.dirname(__FILE__)}/resources/*.rb"].sort.each { |file| require file }
+Dir["#{File.dirname(__FILE__)}/*.rb"].sort.each { |file| require file }
require 'openstudio'
require 'date'
@@ -128,28 +128,28 @@ def arguments(_model)
peak_window_strategy.setDefaultValue('center with peak')
args << peak_window_strategy
- choices_scenarios = %w[
- AER_95DecarbBy2035
- AER_95DecarbBy2050
- AER_HighRECost
- AER_LowRECost
- AER_MidCase
- LRMER_95DecarbBy2035_15
- LRMER_95DecarbBy2035_30
- LRMER_95DecarbBy2035_15_2025start
- LRMER_95DecarbBy2035_25_2025start
- LRMER_95DecarbBy2050_15
- LRMER_95DecarbBy2050_30
- LRMER_HighRECost_15
- LRMER_HighRECost_30
- LRMER_LowRECost_15
- LRMER_LowRECost_30
- LRMER_LowRECost_15_2025start
- LRMER_LowRECost_25_2025start
- LRMER_MidCase_15
- LRMER_MidCase_30
- LRMER_MidCase_15_2025start
- LRMER_MidCase_25_2025start
+ choices_scenarios = [
+ 'AER_95DecarbBy2035',
+ 'AER_95DecarbBy2050',
+ 'AER_HighRECost',
+ 'AER_LowRECost',
+ 'AER_MidCase',
+ 'LRMER_95DecarbBy2035_15',
+ 'LRMER_95DecarbBy2035_30',
+ 'LRMER_95DecarbBy2035_15_2025start',
+ 'LRMER_95DecarbBy2035_25_2025start',
+ 'LRMER_95DecarbBy2050_15',
+ 'LRMER_95DecarbBy2050_30',
+ 'LRMER_HighRECost_15',
+ 'LRMER_HighRECost_30',
+ 'LRMER_LowRECost_15',
+ 'LRMER_LowRECost_30',
+ 'LRMER_LowRECost_15_2025start',
+ 'LRMER_LowRECost_25_2025start',
+ 'LRMER_MidCase_15',
+ 'LRMER_MidCase_30',
+ 'LRMER_MidCase_15_2025start',
+ 'LRMER_MidCase_25_2025start'
]
cambium_scenario = OpenStudio::Ruleset::OSArgument.makeChoiceArgument('cambium_scenario', choices_scenarios, true)
cambium_scenario.setDisplayName('Cambium scenario of emission factor')
@@ -547,7 +547,7 @@ def run(model, runner, user_arguments)
raise 'Unable to find grid region in model building additional properties' unless grid_region.is_initialized
grid_region = grid_region.get
- if %w[AKMS AKGD HIMS HIOA].include? grid_region
+ if ['AKMS', 'AKGD', 'HIMS', 'HIOA'].include? grid_region
runner.registerAsNotApplicable('applicability not passed for grid load data availability')
return true
else
diff --git a/resources/measures/upgrade_df_load_shed/tests/df_load_shed_test.rb b/resources/measures/upgrade_df_load_shed/tests/df_load_shed_test.rb
index 7aee824ef..996d252da 100644
--- a/resources/measures/upgrade_df_load_shed/tests/df_load_shed_test.rb
+++ b/resources/measures/upgrade_df_load_shed/tests/df_load_shed_test.rb
@@ -57,12 +57,12 @@ def models_to_test
# }
test_sets << {
model: '3340_small_office_OS38', # small office
- weather: 'IL_Dupage_3340_18',
+ weather: 'CO_FortCollins_16',
result: 'Success'
}
test_sets << {
model: '4774_secondary_school_OS38', # secondary school
- weather: 'MI_Tulip_City_4774_18',
+ weather: 'CO_FortCollins_16',
result: 'Success'
}
# test: not applicable building type
@@ -76,6 +76,7 @@ def models_to_test
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -264,7 +265,7 @@ def test_models
# argument_map['apply_measure'] = apply_measure
# actual hourly timestep
- if demand_flexibility_objective.valueAsString == 'grid peak load' or demand_flexibility_objective.valueAsString == 'emissions'
+ if demand_flexibility_objective.valueAsString == 'grid peak load' || demand_flexibility_objective.valueAsString == 'emissions'
timestep = 1
else
timestep = num_timesteps_in_hr.valueAsInteger
diff --git a/resources/measures/upgrade_df_thermostat_control_load_shift/resources/dispatch_schedule_generation.rb b/resources/measures/upgrade_df_thermostat_control_load_shift/dispatch_schedule_generation.rb
similarity index 91%
rename from resources/measures/upgrade_df_thermostat_control_load_shift/resources/dispatch_schedule_generation.rb
rename to resources/measures/upgrade_df_thermostat_control_load_shift/dispatch_schedule_generation.rb
index 6199c6469..6302c572c 100644
--- a/resources/measures/upgrade_df_thermostat_control_load_shift/resources/dispatch_schedule_generation.rb
+++ b/resources/measures/upgrade_df_thermostat_control_load_shift/dispatch_schedule_generation.rb
@@ -41,57 +41,57 @@
require 'openstudio-standards'
def cambium_emissions_scenarios
- %w[
- AER_95DecarbBy2035
- AER_95DecarbBy2050
- AER_HighRECost
- AER_LowRECost
- AER_MidCase
- LRMER_95DecarbBy2035_15
- LRMER_95DecarbBy2035_30
- LRMER_95DecarbBy2035_15_2025start
- LRMER_95DecarbBy2035_25_2025start
- LRMER_95DecarbBy2050_15
- LRMER_95DecarbBy2050_30
- LRMER_HighRECost_15
- LRMER_HighRECost_30
- LRMER_LowRECost_15
- LRMER_LowRECost_30
- LRMER_LowRECost_15_2025start
- LRMER_LowRECost_25_2025start
- LRMER_MidCase_15
- LRMER_MidCase_30
- LRMER_MidCase_15_2025start
- LRMER_MidCase_25_2025start
+ [
+ 'AER_95DecarbBy2035',
+ 'AER_95DecarbBy2050',
+ 'AER_HighRECost',
+ 'AER_LowRECost',
+ 'AER_MidCase',
+ 'LRMER_95DecarbBy2035_15',
+ 'LRMER_95DecarbBy2035_30',
+ 'LRMER_95DecarbBy2035_15_2025start',
+ 'LRMER_95DecarbBy2035_25_2025start',
+ 'LRMER_95DecarbBy2050_15',
+ 'LRMER_95DecarbBy2050_30',
+ 'LRMER_HighRECost_15',
+ 'LRMER_HighRECost_30',
+ 'LRMER_LowRECost_15',
+ 'LRMER_LowRECost_30',
+ 'LRMER_LowRECost_15_2025start',
+ 'LRMER_LowRECost_25_2025start',
+ 'LRMER_MidCase_15',
+ 'LRMER_MidCase_30',
+ 'LRMER_MidCase_15_2025start',
+ 'LRMER_MidCase_25_2025start'
]
end
def grid_regions
- %w[
- AZNMc
- AKGD
- AKMS
- CAMXc
- ERCTc
- FRCCc
- HIMS
- HIOA
- MROEc
- MROWc
- NEWEc
- NWPPc
- NYSTc
- RFCEc
- RFCMc
- RFCWc
- RMPAc
- SPNOc
- SPSOc
- SRMVc
- SRMWc
- SRSOc
- SRTVc
- SRVCc
+ [
+ 'AZNMc',
+ 'AKGD',
+ 'AKMS',
+ 'CAMXc',
+ 'ERCTc',
+ 'FRCCc',
+ 'HIMS',
+ 'HIOA',
+ 'MROEc',
+ 'MROWc',
+ 'NEWEc',
+ 'NWPPc',
+ 'NYSTc',
+ 'RFCEc',
+ 'RFCMc',
+ 'RFCWc',
+ 'RMPAc',
+ 'SPNOc',
+ 'SPSOc',
+ 'SRMVc',
+ 'SRMWc',
+ 'SRSOc',
+ 'SRTVc',
+ 'SRVCc'
]
end
@@ -105,7 +105,7 @@ def day_of_year_to_date(year, day_of_year)
### if year is leap year
def leap_year?(year)
- (year % 4).zero? && !(year % 100).zero? || (year % 400).zero?
+ ((year % 4).zero? && !(year % 100).zero?) || (year % 400).zero?
end
### obtain oat profile from epw file
@@ -178,90 +178,90 @@ def create_binsamples(oat, option)
'other' => [] }
}
(0..nd - 1).each do |d|
- oatmax = oat[24 * d..24 * (d + 1) - 1].max
- oatmaxind = oat[24 * d..24 * (d + 1) - 1].index(oat[24 * d..24 * (d + 1) - 1].max)
+ oatmax = oat[24 * d..(24 * (d + 1)) - 1].max
+ oatmaxind = oat[24 * d..(24 * (d + 1)) - 1].index(oat[24 * d..(24 * (d + 1)) - 1].max)
if oatmax >= 32.0
if (oatmaxind >= 9.0) && (oatmaxind <= 11.0)
- combbins['ext-hot']['morning'] << d + 1
+ combbins['ext-hot']['morning'] << (d + 1)
elsif (oatmaxind > 11.0) && (oatmaxind <= 14.0)
- combbins['ext-hot']['noon'] << d + 1
+ combbins['ext-hot']['noon'] << (d + 1)
elsif (oatmaxind > 14.0) && (oatmaxind <= 15.0)
- combbins['ext-hot']['afternoon'] << d + 1
+ combbins['ext-hot']['afternoon'] << (d + 1)
elsif (oatmaxind > 15.0) && (oatmaxind <= 17.0)
- combbins['ext-hot']['late-afternoon'] << d + 1
+ combbins['ext-hot']['late-afternoon'] << (d + 1)
elsif (oatmaxind > 17.0) && (oatmaxind <= 20.0)
- combbins['ext-hot']['evening'] << d + 1
+ combbins['ext-hot']['evening'] << (d + 1)
else
- combbins['ext-hot']['other'] << d + 1
+ combbins['ext-hot']['other'] << (d + 1)
end
elsif oatmax >= 30.0
if (oatmaxind >= 9.0) && (oatmaxind <= 11.0)
- combbins['hot']['morning'] << d + 1
+ combbins['hot']['morning'] << (d + 1)
elsif (oatmaxind > 11.0) && (oatmaxind <= 14.0)
- combbins['hot']['noon'] << d + 1
+ combbins['hot']['noon'] << (d + 1)
elsif (oatmaxind > 14.0) && (oatmaxind <= 15.0)
- combbins['hot']['afternoon'] << d + 1
+ combbins['hot']['afternoon'] << (d + 1)
elsif (oatmaxind > 15.0) && (oatmaxind <= 17.0)
- combbins['hot']['late-afternoon'] << d + 1
+ combbins['hot']['late-afternoon'] << (d + 1)
elsif (oatmaxind > 17.0) && (oatmaxind <= 20.0)
- combbins['hot']['evening'] << d + 1
+ combbins['hot']['evening'] << (d + 1)
else
- combbins['hot']['other'] << d + 1
+ combbins['hot']['other'] << (d + 1)
end
elsif oatmax >= 26.0
if (oatmaxind >= 9.0) && (oatmaxind <= 11.0)
- combbins['mild']['morning'] << d + 1
+ combbins['mild']['morning'] << (d + 1)
elsif (oatmaxind > 11.0) && (oatmaxind <= 14.0)
- combbins['mild']['noon'] << d + 1
+ combbins['mild']['noon'] << (d + 1)
elsif (oatmaxind > 14.0) && (oatmaxind <= 15.0)
- combbins['mild']['afternoon'] << d + 1
+ combbins['mild']['afternoon'] << (d + 1)
elsif (oatmaxind > 15.0) && (oatmaxind <= 17.0)
- combbins['mild']['late-afternoon'] << d + 1
+ combbins['mild']['late-afternoon'] << (d + 1)
elsif (oatmaxind > 17.0) && (oatmaxind <= 20.0)
- combbins['mild']['evening'] << d + 1
+ combbins['mild']['evening'] << (d + 1)
else
- combbins['mild']['other'] << d + 1
+ combbins['mild']['other'] << (d + 1)
end
elsif oatmax >= 20.0
if (oatmaxind >= 9.0) && (oatmaxind <= 11.0)
- combbins['cool-mild']['morning'] << d + 1
+ combbins['cool-mild']['morning'] << (d + 1)
elsif (oatmaxind > 11.0) && (oatmaxind <= 14.0)
- combbins['cool-mild']['noon'] << d + 1
+ combbins['cool-mild']['noon'] << (d + 1)
elsif (oatmaxind > 14.0) && (oatmaxind <= 15.0)
- combbins['cool-mild']['afternoon'] << d + 1
+ combbins['cool-mild']['afternoon'] << (d + 1)
elsif (oatmaxind > 15.0) && (oatmaxind <= 17.0)
- combbins['cool-mild']['late-afternoon'] << d + 1
+ combbins['cool-mild']['late-afternoon'] << (d + 1)
elsif (oatmaxind > 17.0) && (oatmaxind <= 20.0)
- combbins['cool-mild']['evening'] << d + 1
+ combbins['cool-mild']['evening'] << (d + 1)
else
- combbins['cool-mild']['other'] << d + 1
+ combbins['cool-mild']['other'] << (d + 1)
end
elsif oatmax >= 15.0
if (oatmaxind >= 9.0) && (oatmaxind <= 11.0)
- combbins['cool']['morning'] << d + 1
+ combbins['cool']['morning'] << (d + 1)
elsif (oatmaxind > 11.0) && (oatmaxind <= 14.0)
- combbins['cool']['noon'] << d + 1
+ combbins['cool']['noon'] << (d + 1)
elsif (oatmaxind > 14.0) && (oatmaxind <= 15.0)
- combbins['cool']['afternoon'] << d + 1
+ combbins['cool']['afternoon'] << (d + 1)
elsif (oatmaxind > 15.0) && (oatmaxind <= 17.0)
- combbins['cool']['late-afternoon'] << d + 1
+ combbins['cool']['late-afternoon'] << (d + 1)
elsif (oatmaxind > 17.0) && (oatmaxind <= 20.0)
- combbins['cool']['evening'] << d + 1
+ combbins['cool']['evening'] << (d + 1)
else
- combbins['cool']['other'] << d + 1
+ combbins['cool']['other'] << (d + 1)
end
elsif (oatmaxind >= 9.0) && (oatmaxind <= 11.0)
- combbins['cold']['morning'] << d + 1
+ combbins['cold']['morning'] << (d + 1)
elsif (oatmaxind > 11.0) && (oatmaxind <= 14.0)
- combbins['cold']['noon'] << d + 1
+ combbins['cold']['noon'] << (d + 1)
elsif (oatmaxind > 14.0) && (oatmaxind <= 15.0)
- combbins['cold']['afternoon'] << d + 1
+ combbins['cold']['afternoon'] << (d + 1)
elsif (oatmaxind > 15.0) && (oatmaxind <= 17.0)
- combbins['cold']['late-afternoon'] << d + 1
+ combbins['cold']['late-afternoon'] << (d + 1)
elsif (oatmaxind > 17.0) && (oatmaxind <= 20.0)
- combbins['cold']['evening'] << d + 1
+ combbins['cold']['evening'] << (d + 1)
else
- combbins['cold']['other'] << d + 1
+ combbins['cold']['other'] << (d + 1)
end
end
ns = 0
@@ -496,7 +496,7 @@ def run_samples(model, selectdays, num_timesteps_in_hr, epw_path = nil)
y_seed[key][keykey] = if y_seed[key][keykey] == []
yd.map { |a| a / ns }
else
- yd.zip(y_seed[key][keykey]).map { |a, b| (a / ns + b) }
+ yd.zip(y_seed[key][keykey]).map { |a, b| ((a / ns) + b) }
end
end
end
@@ -688,13 +688,13 @@ def run_part_year_samples(model, max_doy, selectdays, num_timesteps_in_hr, epw_p
ns = selectdays[key][keykey].length.to_f
selectdays[key][keykey].each do |doy|
if ns == 1
- y_seed[key][keykey] = yd[(doy * 24 - 24)..(doy * 24 - 1)]
+ y_seed[key][keykey] = yd[((doy * 24) - 24)..((doy * 24) - 1)]
elsif ns > 1
y_seed[key][keykey] = if y_seed[key][keykey] == []
- yd[(doy * 24 - 24)..(doy * 24 - 1)].map { |a| a / ns }
+ yd[((doy * 24) - 24)..((doy * 24) - 1)].map { |a| a / ns }
else
- yd[(doy * 24 - 24)..(doy * 24 - 1)].zip(y_seed[key][keykey]).map do |a, b|
- (a / ns + b)
+ yd[((doy * 24) - 24)..((doy * 24) - 1)].zip(y_seed[key][keykey]).map do |a, b|
+ ((a / ns) + b)
end
end
end
@@ -906,7 +906,7 @@ def read_emission_factors(model, scenario, year = 2021)
grid_region = grid_region.get
puts("Using grid region #{grid_region} from model building additional properties.")
- if %w[AKMS AKGD HIMS HIOA].include? grid_region
+ if ['AKMS', 'AKGD', 'HIMS', 'HIOA'].include? grid_region
cambium_grid_region = nil
egrid_region = grid_region
puts("Grid region '#{grid_region}' is not available in Cambium. Using eGrid factors only for electricty related emissions.")
@@ -915,7 +915,7 @@ def read_emission_factors(model, scenario, year = 2021)
egrid_region = grid_region.chop
end
# read egrid factors
- egrid_subregion_emissions_factors_csv = "#{File.dirname(__FILE__)}/egrid/egrid_subregion_emissions_factors.csv"
+ egrid_subregion_emissions_factors_csv = "#{File.dirname(__FILE__)}/resources/egrid/egrid_subregion_emissions_factors.csv"
unless File.file?(egrid_subregion_emissions_factors_csv)
raise "Unable to find file: #{egrid_subregion_emissions_factors_csv}"
end
@@ -928,7 +928,7 @@ def read_emission_factors(model, scenario, year = 2021)
if [2018, 2019, 2020, 2021].include?(year)
egrid_co2e_kg_per_mwh = egrid_subregion_hsh[0][:"#{year}"] * lbm_to_kg
elsif year == 'average'
- egrid_co2e_kg_per_mwh = (egrid_subregion_hsh[0][:"2018"] + egrid_subregion_hsh[0][:"2019"] + egrid_subregion_hsh[0][:"2020"] + egrid_subregion_hsh[0][:"2021"]) / 4.0 * lbm_to_kg
+ egrid_co2e_kg_per_mwh = (egrid_subregion_hsh[0][:'2018'] + egrid_subregion_hsh[0][:'2019'] + egrid_subregion_hsh[0][:'2020'] + egrid_subregion_hsh[0][:'2021']) / 4.0 * lbm_to_kg
else
raise "Unable to find eGRID data for year: #{year}"
end
@@ -940,7 +940,7 @@ def read_emission_factors(model, scenario, year = 2021)
else
scenario
end
- emissions_csv = "#{File.dirname(__FILE__)}/cambium/#{scenario_lookup}/#{cambium_grid_region}.csv"
+ emissions_csv = "#{File.dirname(__FILE__)}/resources/cambium/#{scenario_lookup}/#{cambium_grid_region}.csv"
raise "Unable to find file: #{emissions_csv}" unless File.file?(emissions_csv)
cambium_co2e_kg_per_mwh = CSV.read(emissions_csv, converters: :float).flatten
@@ -961,7 +961,7 @@ def emissions_prediction(load, factor, num_timesteps_in_hr)
end
# convert load from J to mwh
hourly_load_mwh = []
- hourly_load.each { |val| hourly_load_mwh << val * j_to_mwh }
+ hourly_load.each { |val| hourly_load_mwh << (val * j_to_mwh) }
# calculate emissions
case factor
when Array
@@ -999,7 +999,7 @@ def load_prediction_from_grid_data(model, scenario = 'Load_MidCase_2035')
# cambium_grid_region = grid_region
# egrid_region = grid_region.chop
# end
- load_csv = "#{File.dirname(__FILE__)}/cambium/#{scenario}/#{grid_region}.csv"
+ load_csv = "#{File.dirname(__FILE__)}/resources/cambium/#{scenario}/#{grid_region}.csv"
raise "Unable to find file: #{load_csv}" unless File.file?(load_csv)
CSV.read(load_csv, converters: :float).flatten
@@ -1016,8 +1016,8 @@ def find_daily_peak_window(daily_load, peak_len, num_timesteps_in_hr, peak_windo
case peak_window_strategy
when 'max savings'
# peak_sum = (0...peak_len).map { |i| load[maxload_ind - i, peak_len].sum }
- peak_sum = (0..peak_len * num_timesteps_in_hr - 1).map do |i|
- daily_load[(maxload_ind - i)..(maxload_ind - i + peak_len * num_timesteps_in_hr - 1)].sum
+ peak_sum = (0..(peak_len * num_timesteps_in_hr) - 1).map do |i|
+ daily_load[(maxload_ind - i)..(maxload_ind - i + (peak_len * num_timesteps_in_hr) - 1)].sum
end
peak_ind = maxload_ind - peak_sum.index(peak_sum.max)
when 'start with peak'
@@ -1027,8 +1027,8 @@ def find_daily_peak_window(daily_load, peak_len, num_timesteps_in_hr, peak_windo
maxload_ind
end
when 'end with peak'
- peak_ind = if maxload_ind >= peak_len * num_timesteps_in_hr - 1
- maxload_ind - peak_len * num_timesteps_in_hr + 1
+ peak_ind = if maxload_ind >= (peak_len * num_timesteps_in_hr) - 1
+ maxload_ind - (peak_len * num_timesteps_in_hr) + 1
else
0
end
@@ -1069,8 +1069,8 @@ def peak_schedule_generation(annual_load, oat, peak_len, num_timesteps_in_hr, pe
temperature_range = seasons[season]
(0..nd - 1).each do |d|
range_start = d * 24 * num_timesteps_in_hr
- range_end = (d + 1) * 24 * num_timesteps_in_hr - 1
- temps = oat[d * 24..d * 24 + 23]
+ range_end = ((d + 1) * 24 * num_timesteps_in_hr) - 1
+ temps = oat[d * 24..(d * 24) + 23]
avg_temp = temps.inject { |sum, el| sum + el }.to_f / temps.size
next unless (avg_temp > temperature_range[0]) && (avg_temp < temperature_range[1])
@@ -1078,18 +1078,18 @@ def peak_schedule_generation(annual_load, oat, peak_len, num_timesteps_in_hr, pe
peak_window_strategy)
# peak and rebound schedule
if prepeak_len.zero?
- peak_schedule[(range_start + peak_ind)..(range_start + peak_ind + peak_len * num_timesteps_in_hr - 1)] =
+ peak_schedule[(range_start + peak_ind)..(range_start + peak_ind + (peak_len * num_timesteps_in_hr) - 1)] =
Array.new(peak_len * num_timesteps_in_hr, 1)
if rebound_len.positive?
- range_rebound_start = range_start + peak_ind + peak_len * num_timesteps_in_hr - 1
- range_rebound_end = range_start + peak_ind + (peak_len + rebound_len) * num_timesteps_in_hr
- peak_schedule[range_rebound_start..range_rebound_end] = (0..rebound_len * num_timesteps_in_hr + 1).map do |i|
- 1.0 - i.to_f / (rebound_len * num_timesteps_in_hr + 1)
+ range_rebound_start = range_start + peak_ind + (peak_len * num_timesteps_in_hr) - 1
+ range_rebound_end = range_start + peak_ind + ((peak_len + rebound_len) * num_timesteps_in_hr)
+ peak_schedule[range_rebound_start..range_rebound_end] = (0..(rebound_len * num_timesteps_in_hr) + 1).map do |i|
+ 1.0 - (i.to_f / ((rebound_len * num_timesteps_in_hr) + 1))
end
end
# prepeak schedule
elsif peak_ind >= prepeak_len
- peak_schedule[(range_start + peak_ind - prepeak_len * num_timesteps_in_hr)..(range_start + peak_ind - 1)] =
+ peak_schedule[(range_start + peak_ind - (prepeak_len * num_timesteps_in_hr))..(range_start + peak_ind - 1)] =
Array.new(prepeak_len * num_timesteps_in_hr, 1)
else
peak_schedule[range_start..(range_start + peak_ind - 1)] = Array.new(peak_ind, 1)
@@ -1263,7 +1263,7 @@ def peak_schedule_generation_fix(climatezone, oat, rebound_len = 0, prepeak_len
peak_end_htg = peak_window_fix_based_on_climate_zone[climatezone]['wint_end'] - 1
(0..nd - 1).each do |d|
range_start = d * 24
- range_end = d * 24 + 23
+ range_end = (d * 24) + 23
temps = oat[range_start..range_end]
avg_temp = temps.inject { |sum, el| sum + el }.to_f / temps.size
if (avg_temp > temperature_range[0]) && (avg_temp < temperature_range[1])
@@ -1277,12 +1277,12 @@ def peak_schedule_generation_fix(climatezone, oat, rebound_len = 0, prepeak_len
range_rebound_start_clg = range_start + peak_end_clg
range_rebound_end_clg = range_start + peak_end_clg + 1 + rebound_len
peak_schedule_clg[range_rebound_start_clg..range_rebound_end_clg] = (0..rebound_len + 1).map do |i|
- 1.0 - i.to_f / (rebound_len + 1)
+ 1.0 - (i.to_f / (rebound_len + 1))
end
range_rebound_start_htg = range_start + peak_end_htg
range_rebound_end_htg = range_start + peak_end_htg + 1 + rebound_len
peak_schedule_htg[range_rebound_start_htg..range_rebound_end_htg] = (0..rebound_len + 1).map do |i|
- 1.0 - i.to_f / (rebound_len + 1)
+ 1.0 - (i.to_f / (rebound_len + 1))
end
end
# prepeak schedule
@@ -1330,7 +1330,7 @@ def peak_schedule_generation_oat(oat, peak_len, peak_lag, rebound_len = 0, prepe
temperature_range = seasons[season]
(0..nd - 1).each do |d|
range_start = d * 24
- range_end = d * 24 + 23
+ range_end = (d * 24) + 23
temps = oat[range_start..range_end]
avg_temp = temps.inject { |sum, el| sum + el }.to_f / temps.size
next unless (avg_temp > temperature_range[0]) && (avg_temp < temperature_range[1])
@@ -1347,12 +1347,12 @@ def peak_schedule_generation_oat(oat, peak_len, peak_lag, rebound_len = 0, prepe
range_rebound_start_clg = range_start + peak_end_clg
range_rebound_end_clg = range_start + peak_end_clg + 1 + rebound_len
peak_schedule_clg[range_rebound_start_clg..range_rebound_end_clg] = (0..rebound_len + 1).map do |i|
- 1.0 - i.to_f / (rebound_len + 1)
+ 1.0 - (i.to_f / (rebound_len + 1))
end
range_rebound_start_htg = range_start + peak_end_htg
range_rebound_end_htg = range_start + peak_end_htg + 1 + rebound_len
peak_schedule_htg[range_rebound_start_htg..range_rebound_end_htg] = (0..rebound_len + 1).map do |i|
- 1.0 - i.to_f / (rebound_len + 1)
+ 1.0 - (i.to_f / (rebound_len + 1))
end
end
# prepeak schedule
diff --git a/resources/measures/upgrade_df_thermostat_control_load_shift/measure.rb b/resources/measures/upgrade_df_thermostat_control_load_shift/measure.rb
index 07f138308..d39054cf9 100644
--- a/resources/measures/upgrade_df_thermostat_control_load_shift/measure.rb
+++ b/resources/measures/upgrade_df_thermostat_control_load_shift/measure.rb
@@ -36,7 +36,7 @@
# *******************************************************************************
# require all .rb files in resources folder
-Dir["#{File.dirname(__FILE__)}/resources/*.rb"].sort.each { |file| require file }
+Dir["#{File.dirname(__FILE__)}/*.rb"].sort.each { |file| require file }
require 'openstudio'
require 'date'
diff --git a/resources/measures/upgrade_df_thermostat_control_load_shift/tests/df_thermostat_control_load_shift_test.rb b/resources/measures/upgrade_df_thermostat_control_load_shift/tests/df_thermostat_control_load_shift_test.rb
index 53e888811..b3047c289 100644
--- a/resources/measures/upgrade_df_thermostat_control_load_shift/tests/df_thermostat_control_load_shift_test.rb
+++ b/resources/measures/upgrade_df_thermostat_control_load_shift/tests/df_thermostat_control_load_shift_test.rb
@@ -76,6 +76,7 @@ def models_to_test
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -114,7 +115,7 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
assert(File.exist?(epw_path))
# create run directory if it does not exist
- FileUtils.mkdir_p(run_dir(test_name)) unless File.exist?(run_dir(test_name))
+ FileUtils.mkdir_p(run_dir(test_name))
assert(File.exist?(run_dir(test_name)))
# change into run directory for tests
@@ -122,8 +123,8 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
Dir.chdir run_dir(test_name)
# remove prior runs if they exist
- FileUtils.rm(model_output_path(test_name)) if File.exist?(model_output_path(test_name))
- FileUtils.rm(report_path(test_name)) if File.exist?(report_path(test_name))
+ FileUtils.rm_f(model_output_path(test_name))
+ FileUtils.rm_f(report_path(test_name))
# copy the osm and epw to the test directory
new_osm_path = "#{run_dir(test_name)}/#{File.basename(osm_path)}"
@@ -310,7 +311,7 @@ def test_models
puts("--- Detected #{nts_clg} df adjusted cooling schedules and #{nts_htg} df adjusted heating schedules")
assert(nts_clg + nts_htg > 0)
puts(new_cool_schedules.keys)
-
+
# compare before/after schedules
if nts_clg > 0
cool_schedules.each do |cool_sch_name, cool_sch_vals|
diff --git a/resources/measures/upgrade_env_exterior_wall_insulation/tests/upgrade_env_exterior_wall_insulation_test.rb b/resources/measures/upgrade_env_exterior_wall_insulation/tests/upgrade_env_exterior_wall_insulation_test.rb
index e5b163fc6..4489b5ab1 100644
--- a/resources/measures/upgrade_env_exterior_wall_insulation/tests/upgrade_env_exterior_wall_insulation_test.rb
+++ b/resources/measures/upgrade_env_exterior_wall_insulation/tests/upgrade_env_exterior_wall_insulation_test.rb
@@ -61,6 +61,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_env_latest_envelope_code/measure.rb b/resources/measures/upgrade_env_latest_envelope_code/measure.rb
index 971e4b70b..b88dffc3e 100644
--- a/resources/measures/upgrade_env_latest_envelope_code/measure.rb
+++ b/resources/measures/upgrade_env_latest_envelope_code/measure.rb
@@ -43,7 +43,6 @@
# start the measure
class SetEnvelopeToCurrentCode < OpenStudio::Measure::ModelMeasure
-
# human readable name
def name
return 'Set Envelope to Current Code'
@@ -75,7 +74,7 @@ def run(model, runner, user_arguments)
return false
end
- # Get additional properties of the model. Used to look up state, climate zone, existing wall template, existing roof template, existing window construction.
+ # Get additional properties of the model. Used to look up state, climate zone, existing wall template, existing roof template, existing window construction.
addtl_props = model.getBuilding.additionalProperties
# Get state and then lookup current_code_in_force using resource file
@@ -96,15 +95,15 @@ def run(model, runner, user_arguments)
if current_code_in_force
runner.registerInfo("Current code in force for #{state_name} is: #{current_code_in_force}.")
else
- runner.registerError("Current code in force not found for #{state_name}, cannot apply measure.")
+ runner.registerError("Current code in force not found for #{state_name}, cannot apply measure.")
end
else
- runner.registerError("State not found, cannot lookup current code in force.")
+ runner.registerError('State not found, cannot lookup current code in force.')
return false
end
# Make a standard that matches the current code in force
- standard = Standard.build("#{current_code_in_force}")
+ standard = Standard.build(current_code_in_force.to_s)
# Check that a default construction set is defined
bldg_def_const_set = model.getBuilding.defaultConstructionSet
@@ -159,7 +158,7 @@ def run(model, runner, user_arguments)
end
puts "Climate zone is: #{climate_zone}."
else
- runner.registerError("Climate zone not found. Cannot lookup window construction.")
+ runner.registerError('Climate zone not found. Cannot lookup window construction.')
end
## WALLS ##
@@ -169,15 +168,15 @@ def run(model, runner, user_arguments)
puts "Existing walls template is: #{existing_walls_template}."
existing_walls_ranking = template_ranking[existing_walls_template]
else
- puts "Existing walls template not found."
- # Assume worst wall insulation ranking and wall insulation will be upgraded.
+ puts 'Existing walls template not found.'
+ # Assume worst wall insulation ranking and wall insulation will be upgraded.
existing_walls_ranking = 0
end
- # Check if existing wall template is worse than current code in force. If so, then proceed with wall insulation upgrade. Otherwise, wall insulation will not be upgraded.
+ # Check if existing wall template is worse than current code in force. If so, then proceed with wall insulation upgrade. Otherwise, wall insulation will not be upgraded.
if existing_walls_ranking >= current_code_in_force_ranking
runner.registerInfo('Existing wall insulation is already equivalent or better than the current code in force. Wall insulation will not be upgraded.')
- else
+ else
# Check that a default exterior wall is defined
unless ext_surf_consts.wallConstruction.is_initialized
runner.registerError("Default surface construction set #{ext_surf_consts.name} has no default exterior wall construction.")
@@ -208,15 +207,16 @@ def run(model, runner, user_arguments)
climate_zone_set = standard.model_find_climate_zone_set(model, climate_zone)
new_wall_construction = standard.model_find_and_add_construction(model,
- climate_zone_set,
- 'ExteriorWall',
- old_wall_construction_type,
- occ_type)
- #apply new construction to exterior wall surfaces
+ climate_zone_set,
+ 'ExteriorWall',
+ old_wall_construction_type,
+ occ_type)
+ # apply new construction to exterior wall surfaces
model.getSurfaces.each do |surface|
next unless surface.outsideBoundaryCondition == 'Outdoors' && surface.surfaceType == 'Wall'
+
surface.setConstruction(new_wall_construction)
- end
+ end
runner.registerInfo("Successfully applied wall construction #{new_wall_construction.name} to the model.")
end
@@ -228,12 +228,12 @@ def run(model, runner, user_arguments)
puts "Existing roof template is: #{existing_roof_template}."
existing_roof_ranking = template_ranking[existing_roof_template]
else
- puts "Existing roof template not found."
- # Assume worst roof insulation ranking and roof insulation will be upgraded.
+ puts 'Existing roof template not found.'
+ # Assume worst roof insulation ranking and roof insulation will be upgraded.
existing_roof_ranking = 0
end
- # Check if existing wall template is worse than current code in force. If so, then proceed with wall insulation upgrade. Otherwise, wall insulation will not be upgraded.
+ # Check if existing wall template is worse than current code in force. If so, then proceed with wall insulation upgrade. Otherwise, wall insulation will not be upgraded.
if existing_roof_ranking >= current_code_in_force_ranking
runner.registerInfo('Existing roof insulation is already equivalent or better than the current code in force. Roof will not be replaced.')
else
@@ -265,22 +265,23 @@ def run(model, runner, user_arguments)
end
climate_zone_set = standard.model_find_climate_zone_set(model, climate_zone)
new_roof_construction = standard.model_find_and_add_construction(model,
- climate_zone_set,
- 'ExteriorRoof',
- old_roof_construction_type,
- occ_type)
- #apply new construction to exterior roof surfaces
+ climate_zone_set,
+ 'ExteriorRoof',
+ old_roof_construction_type,
+ occ_type)
+ # apply new construction to exterior roof surfaces
model.getSurfaces.each do |surface|
next unless surface.outsideBoundaryCondition == 'Outdoors' && surface.surfaceType == 'RoofCeiling'
+
surface.setConstruction(new_roof_construction)
- end
+ end
runner.registerInfo("Successfully applied roof construction #{new_roof_construction.name} to the model.")
end
## WINDOWS ##
# Create ranking of window construction (worst to best U-val) so we can evaluate whether the existing window is better than the current code in force
- # Some constructions have the same or nearly the same U-value so they are ranked the same.
+ # Some constructions have the same or nearly the same U-value so they are ranked the same.
# This avoids a scenario where you are replacing a window with U-value 0.559 with U-value 0.557, which is not realistic.
window_ranking = {
'Single - No LowE - Clear - Aluminum' => 0,
@@ -303,8 +304,8 @@ def run(model, runner, user_arguments)
puts "Existing window construction is: #{existing_window_construction}."
existing_window_ranking = window_ranking[existing_window_construction]
else
- puts "Existing window construction not found."
- # Assume worst window ranking and windows will be replaced.
+ puts 'Existing window construction not found.'
+ # Assume worst window ranking and windows will be replaced.
existing_window_ranking = 0
end
@@ -380,7 +381,7 @@ def run(model, runner, user_arguments)
end
# report final condition of model
- runner.registerFinalCondition("Upgraded model to follow the current wall, roof, and window code in the state.")
+ runner.registerFinalCondition('Upgraded model to follow the current wall, roof, and window code in the state.')
return true
end
diff --git a/resources/measures/upgrade_env_latest_envelope_code/tests/set_envelope_to_current_code_test.rb b/resources/measures/upgrade_env_latest_envelope_code/tests/set_envelope_to_current_code_test.rb
index 9b484d21a..b36226b53 100644
--- a/resources/measures/upgrade_env_latest_envelope_code/tests/set_envelope_to_current_code_test.rb
+++ b/resources/measures/upgrade_env_latest_envelope_code/tests/set_envelope_to_current_code_test.rb
@@ -41,12 +41,11 @@
require 'openstudio/measure/ShowRunnerOutput'
require 'fileutils'
require 'minitest/autorun'
-require_relative '../measure.rb'
+require_relative '../measure'
require_relative '../../../../test/helpers/minitest_helper'
class SetEnvelopeToCurrentCodeTest < Minitest::Test
-
# return file paths to test models in test directory
def models_for_tests
paths = Dir.glob(File.join(File.dirname(__FILE__), '../../../tests/models/*.osm'))
@@ -62,6 +61,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -101,18 +101,12 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
assert(File.exist?(epw_path))
# create run directory if it does not exist
- if !File.exist?(run_dir(test_name))
- FileUtils.mkdir_p(run_dir(test_name))
- end
+ FileUtils.mkdir_p(run_dir(test_name))
assert(File.exist?(run_dir(test_name)))
# remove prior runs if they exist
- if File.exist?(model_output_path(test_name))
- FileUtils.rm(model_output_path(test_name))
- end
- if File.exist?(report_path(test_name))
- FileUtils.rm(report_path(test_name))
- end
+ FileUtils.rm_f(model_output_path(test_name))
+ FileUtils.rm_f(report_path(test_name))
# create an instance of a runner
runner = OpenStudio::Measure::OSRunner.new(OpenStudio::WorkflowJSON.new)
@@ -251,6 +245,7 @@ def dont_test_r_value_cz_7
old_ext_surf_material = nil
model.getSurfaces.each do |surface|
next unless (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'Wall')
+
surf_const = surface.construction.get.to_LayeredConstruction.get
old_r_val_si = 1 / surface.thermalConductance.to_f
old_r_val_ip = OpenStudio.convert(old_r_val_si, 'm^2*K/W', 'ft^2*h*R/Btu').get
@@ -265,6 +260,7 @@ def dont_test_r_value_cz_7
model = load_model(model_output_path(__method__))
model.getSurfaces.each do |surface|
next unless (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'Wall')
+
surf_const = surface.construction.get.to_LayeredConstruction.get
new_r_val_si = 1.0 / surface.thermalConductance.to_f
new_r_val_ip = OpenStudio.convert(new_r_val_si, 'm^2*K/W', 'ft^2*h*R/Btu').get
@@ -311,6 +307,7 @@ def dont_test_existing_thermal_bridging
old_ext_surf_material = nil
model.getSurfaces.each do |surface|
next unless (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'Wall')
+
surf_const = surface.construction.get.to_LayeredConstruction.get
old_r_val_si = 1 / surface.thermalConductance.to_f
old_r_val_ip = OpenStudio.convert(old_r_val_si, 'm^2*K/W', 'ft^2*h*R/Btu').get
@@ -325,6 +322,7 @@ def dont_test_existing_thermal_bridging
model = load_model(model_output_path(__method__))
model.getSurfaces.each do |surface|
next unless (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'Wall')
+
surf_const = surface.construction.get.to_LayeredConstruction.get
new_r_val_si = 1.0 / surface.thermalConductance.to_f
new_r_val_ip = OpenStudio.convert(new_r_val_si, 'm^2*K/W', 'ft^2*h*R/Btu').get
@@ -435,6 +433,7 @@ def dont_test_na_metal_building
old_ext_surf_material = nil
model.getSurfaces.each do |surface|
next unless (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'Wall')
+
surf_const = surface.construction.get.to_LayeredConstruction.get
old_r_val_si = 1 / surface.thermalConductance.to_f
old_r_val_ip = OpenStudio.convert(old_r_val_si, 'm^2*K/W', 'ft^2*h*R/Btu').get
@@ -450,4 +449,3 @@ def dont_test_na_metal_building
assert_equal('NA', result.value.valueName)
end
end
-
diff --git a/resources/measures/upgrade_env_new_aedg_windows/tests/measure_test.rb b/resources/measures/upgrade_env_new_aedg_windows/tests/measure_test.rb
index 39051bfb7..1b8ad83ed 100644
--- a/resources/measures/upgrade_env_new_aedg_windows/tests/measure_test.rb
+++ b/resources/measures/upgrade_env_new_aedg_windows/tests/measure_test.rb
@@ -60,6 +60,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -172,70 +173,13 @@ def test_number_of_arguments_and_argument_names
assert_equal(0, arguments.size)
end
- def test_r_value_cz_3a_single_pane
- osm_name = 'Small_Office_CEC8.osm'
- epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
-
- # Test expectations for Single - No LowE - Clear - Wood in 3A
- # is to increase to U-0.37
- target_u_value_ip = 0.37
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvNewAedgWindows.new
-
- # Load the model; only used here for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Check that the starting R-value is less than the target
- old_u_val_ip = 0
- old_ext_surf_material = nil
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- old_u_val_si = glazing_layer.uFactor
- old_u_val_ip = OpenStudio.convert(old_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- break
- end
- assert(old_u_val_ip > target_u_value_ip)
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- model = load_model(model_output_path(__method__))
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- new_u_val_si = glazing_layer.uFactor
- new_u_val_ip = OpenStudio.convert(new_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- # Check that original U-value was above (worse than) the target threshold
- assert(old_u_val_ip > new_u_val_ip)
-
- # Check that the new U-value matches the target
- tolerance = 0.01
- assert_in_delta(target_u_value_ip, new_u_val_ip, tolerance)
-
- break
- end
- end
-
def test_r_value_cz_3a_double_pane
osm_name = 'Quick_Service_Restaurant_Pre1980_3A.osm'
epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
# Test expectations for Double - LowE - Clear - Aluminum in 3A
- # is to increase to U-0.50
- target_u_value_ip = 0.50
+ target_u_value_si = 2.27
+ target_u_value_ip = OpenStudio.convert(target_u_value_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
osm_path = model_input_path(osm_name)
epw_path = epw_input_path(epw_name)
@@ -291,8 +235,9 @@ def test_r_value_cz_5a
epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
# Test expectations for Double - No LowE - Clear - Aluminum
- # is to increase to 0.61 in all climate zones
- target_u_value_ip = 0.61
+ target_u_value_si = 1.93
+ target_u_value_ip = OpenStudio.convert(target_u_value_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
+
osm_path = model_input_path(osm_name)
epw_path = epw_input_path(epw_name)
@@ -348,8 +293,9 @@ def test_r_value_cz_8a_double_pane_thermally_broken
epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
# Test expectations for Double - No LowE - Clear - Aluminum
- # is to increase to U-0.44 in CZ 8
- target_u_value_ip = 0.44
+ # is to increase to U-0.250 in CZ 8
+ target_u_value_si = 1.42
+ target_u_value_ip = OpenStudio.convert(target_u_value_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
osm_path = model_input_path(osm_name)
epw_path = epw_input_path(epw_name)
@@ -392,92 +338,13 @@ def test_r_value_cz_8a_double_pane_thermally_broken
# Check that original U-value was above (worse than) the target threshold
assert(old_u_val_ip > new_u_val_ip)
- # Check that the new U-value matches the target
- tolerance = 0.01
- assert_in_delta(target_u_value_ip, new_u_val_ip, tolerance)
-
- break
- end
- end
-
- def test_r_value_cz_cec16_double_pane_thermally_broken
- osm_name = 'Retail_DEERPre1975_CEC16.osm'
- epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
-
- # Test expectations for Single - No LowE - Clear - Aluminum
- # is to increase to U-0.61 in all climate zones
- target_u_value_ip = 0.61
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvNewAedgWindows.new
-
- # Load the model; only used here for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Check that the starting R-value is less than the target
- old_u_val_ip = 0
- old_ext_surf_material = nil
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- old_u_val_si = glazing_layer.uFactor
- old_u_val_ip = OpenStudio.convert(old_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- break
- end
- assert(old_u_val_ip > target_u_value_ip)
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- model = load_model(model_output_path(__method__))
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- new_u_val_si = glazing_layer.uFactor
- new_u_val_ip = OpenStudio.convert(new_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- # Check that original U-value was above (worse than) the target threshold
- assert(old_u_val_ip > new_u_val_ip)
+ puts glazing_layer
# Check that the new U-value matches the target
- # Set the tolerance higher for this test because the
- # Single - No LowE - Clear - Aluminum
tolerance = 0.01
assert_in_delta(target_u_value_ip, new_u_val_ip, tolerance)
break
end
end
-
- def test_na_simple_glazing_name_not_recognized
- osm_name = 'Warehouse_5A.osm'
- epw_name = 'MI_DETROIT_725375_12.epw'
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvNewAedgWindows.new
-
- # Load the model for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- # Should be NA because this is a warehouse with metal building walls
- assert_equal('NA', result.value.valueName)
- end
end
diff --git a/resources/measures/upgrade_env_roof_insul_aedg/tests/upgrade_env_roof_insul_aedg_test.rb b/resources/measures/upgrade_env_roof_insul_aedg/tests/upgrade_env_roof_insul_aedg_test.rb
index 905fc38b3..981c1b8be 100644
--- a/resources/measures/upgrade_env_roof_insul_aedg/tests/upgrade_env_roof_insul_aedg_test.rb
+++ b/resources/measures/upgrade_env_roof_insul_aedg/tests/upgrade_env_roof_insul_aedg_test.rb
@@ -81,6 +81,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_env_secondary_windows/tests/measure_test.rb b/resources/measures/upgrade_env_secondary_windows/tests/measure_test.rb
index f90e771b3..bd0518f7a 100644
--- a/resources/measures/upgrade_env_secondary_windows/tests/measure_test.rb
+++ b/resources/measures/upgrade_env_secondary_windows/tests/measure_test.rb
@@ -60,6 +60,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_env_window_film/measure.rb b/resources/measures/upgrade_env_window_film/measure.rb
index f92020835..c8ba2a786 100644
--- a/resources/measures/upgrade_env_window_film/measure.rb
+++ b/resources/measures/upgrade_env_window_film/measure.rb
@@ -64,10 +64,10 @@ def arguments(model)
args = OpenStudio::Measure::OSArgumentVector.new
filmtypes = [
- "no film",
- "int. film / min. SHGC / min. VLT",
- "int. film / min. SHGC / min. U-factor / min. VLT",
- "ext. film / min. SHGC / min. VLT"
+ 'no film',
+ 'int. film / min. SHGC / min. VLT',
+ 'int. film / min. SHGC / min. U-factor / min. VLT',
+ 'ext. film / min. SHGC / min. VLT'
]
################################################################################
@@ -233,72 +233,70 @@ def run(model, runner, user_arguments)
# ]
#-------------------------------------------------------------------------------
filmtypes_singlepane = [
- "int. film / min. SHGC / min. U-factor / min. VLT",
- "int. film / min. SHGC / min. U-factor / min. VLT",
- "int. film / min. SHGC / min. U-factor / min. VLT",
- "int. film / min. SHGC / min. U-factor / min. VLT",
- "int. film / min. SHGC / min. U-factor / min. VLT",
- "int. film / min. SHGC / min. U-factor / min. VLT",
- "int. film / min. SHGC / min. U-factor / min. VLT",
- "int. film / min. SHGC / min. U-factor / min. VLT"
+ 'int. film / min. SHGC / min. U-factor / min. VLT',
+ 'int. film / min. SHGC / min. U-factor / min. VLT',
+ 'int. film / min. SHGC / min. U-factor / min. VLT',
+ 'int. film / min. SHGC / min. U-factor / min. VLT',
+ 'int. film / min. SHGC / min. U-factor / min. VLT',
+ 'int. film / min. SHGC / min. U-factor / min. VLT',
+ 'int. film / min. SHGC / min. U-factor / min. VLT',
+ 'int. film / min. SHGC / min. U-factor / min. VLT'
]
filmtypes_doublepane = [
- "ext. film / min. SHGC / min. VLT",
- "ext. film / min. SHGC / min. VLT",
- "ext. film / min. SHGC / min. VLT",
- "ext. film / min. SHGC / min. VLT",
- "ext. film / min. SHGC / min. VLT",
- "ext. film / min. SHGC / min. VLT",
- "no film",
- "no film"
+ 'ext. film / min. SHGC / min. VLT',
+ 'ext. film / min. SHGC / min. VLT',
+ 'ext. film / min. SHGC / min. VLT',
+ 'ext. film / min. SHGC / min. VLT',
+ 'ext. film / min. SHGC / min. VLT',
+ 'ext. film / min. SHGC / min. VLT',
+ 'no film',
+ 'no film'
]
#-------------------------------------------------------------------------------
# create hash: map_input_arg[window pane type][climate zone number] = window film type (user input)
map_input_arg = {}
- [filmtypes_singlepane, filmtypes_doublepane].zip(["Single","Double"]).each do |filmtypes, label|
+ [filmtypes_singlepane, filmtypes_doublepane].zip(['Single', 'Double']).each do |filmtypes, label|
map_input_arg[label] = {}
- filmtypes.each_with_index do |filmtype,i|
- map_input_arg[label][i+1] = filmtype.to_s
+ filmtypes.each_with_index do |filmtype, i|
+ map_input_arg[label][i + 1] = filmtype.to_s
end
end
# create hash: map_cec_to_iecc[CEC climate zone #] = ASHRAE climate zone #
- """
# reference map
- CEC1 - 4B
- CEC2 - 3C
- CEC3 - 3C
- CEC4 - 3C
- CEC5 - 3C
- CEC6 - 3C
- CEC7 - 3B
- CEC8 - 3B
- CEC9 - 3B
- CEC10 - 3B
- CEC11 - 3B
- CEC12 - 3B
- CEC13 - 3B
- CEC15 - 2B
- CEC16 - 5B
- """
+ # CEC1 - 4B
+ # CEC2 - 3C
+ # CEC3 - 3C
+ # CEC4 - 3C
+ # CEC5 - 3C
+ # CEC6 - 3C
+ # CEC7 - 3B
+ # CEC8 - 3B
+ # CEC9 - 3B
+ # CEC10 - 3B
+ # CEC11 - 3B
+ # CEC12 - 3B
+ # CEC13 - 3B
+ # CEC15 - 2B
+ # CEC16 - 5B
map_cec_to_iecc = {
- 1=>4,
- 2=>3,
- 3=>3,
- 4=>3,
- 5=>3,
- 6=>3,
- 7=>3,
- 8=>3,
- 9=>3,
- 10=>3,
- 11=>3,
- 12=>3,
- 13=>3,
- 14=>3,
- 15=>2,
- 16=>5
+ 1 => 4,
+ 2 => 3,
+ 3 => 3,
+ 4 => 3,
+ 5 => 3,
+ 6 => 3,
+ 7 => 3,
+ 8 => 3,
+ 9 => 3,
+ 10 => 3,
+ 11 => 3,
+ 12 => 3,
+ 13 => 3,
+ 14 => 3,
+ 15 => 2,
+ 16 => 5
}
################################################################
@@ -307,181 +305,181 @@ def run(model, runner, user_arguments)
# set hash: map_window[comstock glazing name][window film type (user input)] = [U-factor(SI), SHGC, VLT]
map_window = {
- "Simple Glazing Single - No LowE - Clear - Aluminum"=> {
- "panetype"=> "Single",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Single - No LowE - Clear - Aluminum' => {
+ 'panetype' => 'Single',
+ 'int. film / min. SHGC / min. VLT' => [
6.619,
0.239,
0.101
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
5.502,
0.248,
0.17
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
6.678,
0.332,
0.174
]
},
- "Simple Glazing Single - No LowE - Tinted/Reflective - Aluminum"=> {
- "panetype"=> "Single",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Single - No LowE - Tinted/Reflective - Aluminum' => {
+ 'panetype' => 'Single',
+ 'int. film / min. SHGC / min. VLT' => [
6.618,
0.283,
0.059
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
5.502,
0.259,
0.1
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
6.677,
0.3,
0.105
]
},
- "Simple Glazing Single - No LowE - Clear - Wood"=> {
- "panetype"=> "Single",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Single - No LowE - Clear - Wood' => {
+ 'panetype' => 'Single',
+ 'int. film / min. SHGC / min. VLT' => [
5.102,
0.199,
0.097
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
4.031,
0.208,
0.163
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
5.159,
0.289,
0.167
]
},
- "Simple Glazing Single - No LowE - Tinted/Reflective - Wood"=> {
- "panetype"=> "Single",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Single - No LowE - Tinted/Reflective - Wood' => {
+ 'panetype' => 'Single',
+ 'int. film / min. SHGC / min. VLT' => [
5.101,
0.242,
0.057
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
4.031,
0.219,
0.096
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
5.158,
0.258,
0.101
]
},
- "Simple Glazing Double - No LowE - Clear - Aluminum"=> {
- "panetype"=> "Double",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Double - No LowE - Clear - Aluminum' => {
+ 'panetype' => 'Double',
+ 'int. film / min. SHGC / min. VLT' => [
4.22,
0.33,
0.093
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
3.889,
0.324,
0.157
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
4.236,
0.246,
0.154
]
},
- "Simple Glazing Double - No LowE - Tinted/Reflective - Aluminum"=> {
- "panetype"=> "Double",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Double - No LowE - Tinted/Reflective - Aluminum' => {
+ 'panetype' => 'Double',
+ 'int. film / min. SHGC / min. VLT' => [
4.237,
0.27,
0.056
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
3.901,
0.26,
0.094
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
4.253,
0.214,
0.095
]
},
- "Simple Glazing Double - LowE - Clear - Aluminum"=> {
- "panetype"=> "Double",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Double - LowE - Clear - Aluminum' => {
+ 'panetype' => 'Double',
+ 'int. film / min. SHGC / min. VLT' => [
3.168,
0.237,
0.081
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
3.039,
0.242,
0.136
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
3.173,
0.154,
0.136
]
},
- "Simple Glazing Double - LowE - Clear - Thermally Broken Aluminum"=> {
- "panetype"=> "Double",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Double - LowE - Clear - Thermally Broken Aluminum' => {
+ 'panetype' => 'Double',
+ 'int. film / min. SHGC / min. VLT' => [
2.826,
0.229,
0.081
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
2.697,
0.233,
0.136
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
2.832,
0.146,
0.136
]
},
- "Simple Glazing Double - LowE - Tinted/Reflective - Aluminum"=> {
- "panetype"=> "Double",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Double - LowE - Tinted/Reflective - Aluminum' => {
+ 'panetype' => 'Double',
+ 'int. film / min. SHGC / min. VLT' => [
3.153,
0.185,
0.048
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
3.027,
0.185,
0.081
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
3.159,
0.129,
0.083
]
},
- "Simple Glazing Double - LowE - Tinted/Reflective - Thermally Broken Aluminum"=> {
- "panetype"=> "Double",
- "int. film / min. SHGC / min. VLT"=> [
+ 'Simple Glazing Double - LowE - Tinted/Reflective - Thermally Broken Aluminum' => {
+ 'panetype' => 'Double',
+ 'int. film / min. SHGC / min. VLT' => [
2.812,
0.176,
0.048
],
- "int. film / min. SHGC / min. U-factor / min. VLT"=> [
+ 'int. film / min. SHGC / min. U-factor / min. VLT' => [
2.685,
0.177,
0.081
],
- "ext. film / min. SHGC / min. VLT"=> [
+ 'ext. film / min. SHGC / min. VLT' => [
2.817,
0.121,
0.083
@@ -498,6 +496,7 @@ def run(model, runner, user_arguments)
constructions = []
model.getSubSurfaces.each do |sub_surface|
next unless sub_surface.subSurfaceType.include?('Window')
+
sub_surfaces << sub_surface
constructions << sub_surface.construction.get
puts "--- add window to the list: #{sub_surface.name} | #{sub_surface.construction.get.to_Construction.get.layers[0].name}"
@@ -514,7 +513,7 @@ def run(model, runner, user_arguments)
runner.registerAsNotApplicable('The building has no windows.')
return true
else
- runner.registerInitialCondition("Found #{sub_surfaces.length()} sub-surfaces that include window")
+ runner.registerInitialCondition("Found #{sub_surfaces.length} sub-surfaces that include window")
end
################################################################
@@ -534,13 +533,13 @@ def run(model, runner, user_arguments)
if climate_zone.empty?
runner.registerError('Unable to determine climate zone for model. Cannot apply window film without climate zone information.')
else
- if climate_zone.include?("CEC")
- climate_zone_num_ca = climate_zone.split("CEC")[-1]
+ if climate_zone.include?('CEC')
+ climate_zone_num_ca = climate_zone.split('CEC')[-1]
puts "--- climate_zone_num_ca = #{climate_zone_num_ca}"
climate_zone_num_iecc = map_cec_to_iecc[climate_zone_num_ca.to_i].to_i
puts "--- climate_zone_num_iecc = #{climate_zone_num_iecc}"
- elsif climate_zone.include?("ASHRAE")
- climate_zone_num_iecc = climate_zone.split("-")[-1][0].to_i
+ elsif climate_zone.include?('ASHRAE')
+ climate_zone_num_iecc = climate_zone.split('-')[-1][0].to_i
puts "--- climate_zone_num_iecc = #{climate_zone_num_iecc}"
else
runner.registerError('Unable to determine climate zone for model. Cannot apply window film without climate zone information.')
@@ -562,21 +561,19 @@ def run(model, runner, user_arguments)
# replace window performances in baseline windows
constructions.each do |construction|
-
# don't apply measure if specified in input
# break if apply_measure == false
simple_glazings.each do |simple_glazing|
-
simple_glazing_name = simple_glazing.name.get
construction_name = construction.name.get
- puts "--- ----------------------------------------------------------------------"
+ puts '--- ----------------------------------------------------------------------'
puts "--- construction = #{construction_name}"
puts "--- simple_glazing = #{simple_glazing_name}"
# check availability of simple glazing system name in filtered construction
- if not construction.to_Construction.get.layers[0].name.get == simple_glazing_name
+ if construction.to_Construction.get.layers[0].name.get != simple_glazing_name
puts "--- simple glazing object name (#{simple_glazing_name}) not available in construction in interest. skipping.."
next
end
@@ -597,7 +594,7 @@ def run(model, runner, user_arguments)
puts "--- found values in the map with climate zone #{climate_zone_num_iecc}"
# get old values
- puts "--- get existing window properties"
+ puts '--- get existing window properties'
old_simple_glazing_u = simple_glazing.uFactor
old_simple_glazing_shgc = simple_glazing.solarHeatGainCoefficient
if simple_glazing.visibleTransmittance.is_initialized
@@ -608,15 +605,15 @@ def run(model, runner, user_arguments)
# get correct pane type based on comstock glazing name
# TODO: maybe there's a more elegant way than this
- panetype = simple_glazing_name.split(" - ")[0].split("Simple Glazing ")[1]
+ panetype = simple_glazing_name.split(' - ')[0].split('Simple Glazing ')[1]
puts "--- pane type based on glazing system name = #{panetype}"
# get new values
# map_input_arg[window pane type][climate zone number] = window film type (user input)
# map_window[comstock glazing name][window film type (user input)] = [U-factor(SI), SHGC, VLT]
filmtype = map_input_arg[panetype][climate_zone_num_iecc]
- if filmtype == "no film"
- puts "--- film type is not selected for this pane type and climate zone. skipping.."
+ if filmtype == 'no film'
+ puts '--- film type is not selected for this pane type and climate zone. skipping..'
next
end
puts "--- film option type based on pane type and climate zone = #{filmtype}"
@@ -625,12 +622,12 @@ def run(model, runner, user_arguments)
new_simple_glazing_vlt = map_window[simple_glazing_name][filmtype][2]
# calculate relative differences
- puts "--- calculate performance changes for reporting"
- pct_u = ((new_simple_glazing_u-old_simple_glazing_u)/old_simple_glazing_u*100).round() # negative number meaning improvement
+ puts '--- calculate performance changes for reporting'
+ pct_u = ((new_simple_glazing_u - old_simple_glazing_u) / old_simple_glazing_u * 100).round # negative number meaning improvement
pct_us << pct_u
- pct_shgc = ((new_simple_glazing_shgc-old_simple_glazing_shgc)/old_simple_glazing_shgc*100).round() # negative number meaning improvement
+ pct_shgc = ((new_simple_glazing_shgc - old_simple_glazing_shgc) / old_simple_glazing_shgc * 100).round # negative number meaning improvement
pct_shgcs << pct_shgc
- pct_vlt = ((new_simple_glazing_vlt-old_simple_glazing_vlt)/old_simple_glazing_vlt*100).round()
+ pct_vlt = ((new_simple_glazing_vlt - old_simple_glazing_vlt) / old_simple_glazing_vlt * 100).round
pct_vlts << pct_vlt
# check if construction has been made
@@ -670,6 +667,7 @@ def run(model, runner, user_arguments)
puts "--- assigning new construction with the new glazing system to the surface (#{sub_surface.name.get}) with (exterior) windows"
# assign new construction to fenestration surfaces and add total area changed if construction names match
next unless sub_surface.construction.get.to_Construction.get.layers[0].name.get == construction.to_Construction.get.layers[0].name.get
+
sub_surface.setConstruction(new_construction)
area_changed_m2 += sub_surface.grossArea
# report
@@ -731,13 +729,13 @@ def run(model, runner, user_arguments)
puts '### check measure applicability'
################################################################
- if area_changed_m2 != 0
+ if area_changed_m2 == 0
+ runner.registerAsNotApplicable('No changes in U-factor/SHGC/VLT since window film is not added.')
+ return true
+ else
runner.registerInfo("U-factor changes (in %) on each window by adding window film = #{pct_us}")
runner.registerInfo("SHGC changes (in %) on each window by adding window film = #{pct_shgcs}")
runner.registerInfo("VLT changes (in %) on each window by adding window film = #{pct_vlts}")
- else area_changed_m2 == 0
- runner.registerAsNotApplicable("No changes in U-factor/SHGC/VLT since window film is not added.")
- return true
end
################################################################
@@ -757,15 +755,14 @@ def run(model, runner, user_arguments)
puts "--- pct_vlt_avg = #{pct_vlt_avg}"
area_changed_ft2 = OpenStudio.convert(area_changed_m2, 'm^2', 'ft^2').get
- if area_changed_ft2 != 0
- runner.registerFinalCondition("Added window film to a total of #{area_changed_ft2.round(2)} ft2 that changed U-factor by #{pct_u_avg}%, SHGC by #{pct_shgc_avg}%, and VLT by #{pct_vlt_avg}%.")
+ if area_changed_ft2 == 0
+ runner.registerFinalCondition('Window film is not added.')
else
- runner.registerFinalCondition("Window film is not added.")
+ runner.registerFinalCondition("Added window film to a total of #{area_changed_ft2.round(2)} ft2 that changed U-factor by #{pct_u_avg}%, SHGC by #{pct_shgc_avg}%, and VLT by #{pct_vlt_avg}%.")
end
runner.registerValue('env_window_film_fen_area_ft2', area_changed_ft2.round(2), 'ft2')
return true
-
end
end
diff --git a/resources/measures/upgrade_env_window_film/tests/env_window_film_test.rb b/resources/measures/upgrade_env_window_film/tests/env_window_film_test.rb
index 51ccb66b7..4055f0d58 100644
--- a/resources/measures/upgrade_env_window_film/tests/env_window_film_test.rb
+++ b/resources/measures/upgrade_env_window_film/tests/env_window_film_test.rb
@@ -64,9 +64,9 @@ def test_number_of_arguments_and_argument_names
# get arguments and test that they are what we are expecting
arguments = measure.arguments(model)
- assert_equal(2, arguments.size)
- assert_equal('pct_shgc_reduct', arguments[0].name)
- assert_equal('pct_vlt_reduct', arguments[1].name)
+ assert_equal(0, arguments.size)
+ #assert_equal('pct_shgc_reduct', arguments[0].name)
+ #assert_equal('pct_vlt_reduct', arguments[1].name)
end
# return file paths to test models in test directory
@@ -84,6 +84,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -173,8 +174,8 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
def models_to_test
test_sets = []
test_sets << { model: 'Warehouse_5A', weather: 'MI_DETROIT_725375_12', result: 'Success' }
- test_sets << { model: 'Retail_7', weather: 'MN_Cloquet_Carlton_Co_726558_16', result: 'Success' }
- test_sets << { model: 'Small_Office_2A', weather: 'TX_Port_Arthur_Jeffers_722410_16', result: 'Success' }
+ #test_sets << { model: 'Retail_7', weather: 'MN_Cloquet_Carlton_Co_726558_16', result: 'Success' }
+ #test_sets << { model: 'Small_Office_2A', weather: 'TX_Port_Arthur_Jeffers_722410_16', result: 'Success' }
return test_sets
end
@@ -213,14 +214,14 @@ def test_doe_models
################ END CUSTOMIZE ####################
# set arguments here; will vary by measure
- pct_shgc_reduct = arguments[0].clone
- assert(pct_shgc_reduct.setValue(0.535))
- argument_map['pct_shgc_reduct'] = pct_shgc_reduct
+ #pct_shgc_reduct = arguments[0].clone
+ #assert(pct_shgc_reduct.setValue(0.535))
+ #argument_map['pct_shgc_reduct'] = pct_shgc_reduct
# set percent VLT reduction argument
- pct_vlt_reduct = arguments[1].clone
- assert(pct_vlt_reduct.setValue(0.53))
- argument_map['pct_vlt_reduct'] = pct_vlt_reduct
+ #pct_vlt_reduct = arguments[1].clone
+ #assert(pct_vlt_reduct.setValue(0.53))
+ #argument_map['pct_vlt_reduct'] = pct_vlt_reduct
# apply the measure to the model and optionally run the model
result = apply_measure_and_run(instance_test_name, measure, argument_map, osm_path, epw_path, run_model: false)
@@ -232,8 +233,8 @@ def test_doe_models
new_simple_glazing_obj = sub_surface.construction.get.to_Construction.get.layers[0].to_SimpleGlazing.get
model_shgc = new_simple_glazing_obj.solarHeatGainCoefficient
model_vlt = new_simple_glazing_obj.visibleTransmittance.get
- expected_shgc = (1 - 0.535) * old_shgc
- expected_vlt = (1 - 0.53) * old_vlt
+ expected_shgc = (1 - 0.60) * old_shgc
+ expected_vlt = (1 - 0.77) * old_vlt
assert((expected_shgc - model_shgc).abs < 0.001)
assert((expected_vlt - model_vlt).abs < 0.001)
end
diff --git a/resources/measures/upgrade_hvac_add_heat_pump_rtu/measure.rb b/resources/measures/upgrade_hvac_add_heat_pump_rtu/measure.rb
index 41b7963a2..83b93d6ec 100644
--- a/resources/measures/upgrade_hvac_add_heat_pump_rtu/measure.rb
+++ b/resources/measures/upgrade_hvac_add_heat_pump_rtu/measure.rb
@@ -6,7 +6,7 @@
# see the URL below for information on how to write OpenStudio measures
# http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/
require 'openstudio-standards'
-Dir[File.dirname(__FILE__) + '/resources/*.rb'].each { |file| require file }
+Dir["#{File.dirname(__FILE__)}/resources/*.rb"].sort.each { |file| require file }
# start the measure
class AddHeatPumpRtu < OpenStudio::Measure::ModelMeasure
@@ -40,7 +40,7 @@ def arguments(_model)
args = OpenStudio::Measure::OSArgumentVector.new
# make list of backup heat options
- li_backup_heat_options = %w[match_original_primary_heating_fuel electric_resistance_backup]
+ li_backup_heat_options = ['match_original_primary_heating_fuel', 'electric_resistance_backup']
v_backup_heat_options = OpenStudio::StringVector.new
li_backup_heat_options.each do |option|
v_backup_heat_options << option
@@ -97,7 +97,7 @@ def arguments(_model)
args << hp_min_comp_lockout_temp_f
# make list of cchpc scenarios
- li_hprtu_scenarios = %w[two_speed_standard_eff two_speed_lab_data variable_speed_high_eff cchpc_2027_spec]
+ li_hprtu_scenarios = ['two_speed_standard_eff', 'two_speed_lab_data', 'variable_speed_high_eff', 'cchpc_2027_spec']
v_li_hprtu_scenarios = OpenStudio::StringVector.new
li_hprtu_scenarios.each do |option|
v_li_hprtu_scenarios << option
@@ -583,7 +583,7 @@ def adjust_cfm_per_ton_per_limits(stage_cap_fractions, stage_flows, stage_flow_f
runner.registerInfo("stage summary: dx_rated_cap_applied: #{dx_rated_cap_applied}")
end
- ratio_allowance_50_pct = ratio + (stage_cap_fractions[stage + 1] - ratio) * 0.65
+ ratio_allowance_50_pct = ratio + ((stage_cap_fractions[stage + 1] - ratio) * 0.65)
required_stage_cap_ratio = airflow / m_3_per_s_per_w_max / (stage_cap_fractions[rated_stage_num] * dx_rated_cap_applied)
stage_airflow_limit_max = m_3_per_s_per_w_max * stage_capacity
# if not violating min airflow requirement
@@ -672,7 +672,7 @@ def set_cooling_coil_stages(model, runner, stage_flows_cooling, stage_caps_cooli
new_dx_cooling_coil.setLatentCapacityTimeConstant(45)
# For crankcase heater, conversion is watts to tons
# methods from "TECHNICAL SUPPORT DOCUMENT: ENERGY EFFICIENCY PROGRAM FOR CONSUMER PRODUCTS AND COMMERCIAL AND INDUSTRIAL EQUIPMENT AIR-COOLED COMMERCIAL UNITARY AIR CONDITIONERS AND COMMERCIAL UNITARY HEAT PUMPS"
- crankcase_heater_power = ((60 * (stage_caps_cooling[rated_stage_num_cooling] * 0.0002843451 / 10)**0.67))
+ crankcase_heater_power = ((60 * ((stage_caps_cooling[rated_stage_num_cooling] * 0.0002843451 / 10)**0.67)))
new_dx_cooling_coil.setCrankcaseHeaterCapacity(crankcase_heater_power)
new_dx_cooling_coil.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(-25)
@@ -690,7 +690,7 @@ def set_cooling_coil_stages(model, runner, stage_flows_cooling, stage_caps_cooli
new_dx_cooling_coil.setFuelType('Electricity')
new_dx_cooling_coil.setMaximumOutdoorDryBulbTemperatureforCrankcaseHeaterOperation(4.4)
# methods from "TECHNICAL SUPPORT DOCUMENT: ENERGY EFFICIENCY PROGRAM FOR CONSUMER PRODUCTS AND COMMERCIAL AND INDUSTRIAL EQUIPMENT AIR-COOLED COMMERCIAL UNITARY AIR CONDITIONERS AND COMMERCIAL UNITARY HEAT PUMPS"
- crankcase_heater_power = ((60 * (stage_caps_cooling[rated_stage_num_cooling] * 0.0002843451 / 10)**0.67))
+ crankcase_heater_power = ((60 * ((stage_caps_cooling[rated_stage_num_cooling] * 0.0002843451 / 10)**0.67)))
new_dx_cooling_coil.setCrankcaseHeaterCapacity(crankcase_heater_power)
new_dx_cooling_coil.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(-25)
@@ -764,7 +764,7 @@ def set_heating_coil_stages(model, runner, stage_flows_heating, stage_caps_heati
new_dx_heating_coil.setPartLoadFractionCorrelationCurve(heat_plf_fplr1)
# For crankcase heater, conversion is watts to tons
# methods from "TECHNICAL SUPPORT DOCUMENT: ENERGY EFFICIENCY PROGRAM FOR CONSUMER PRODUCTS AND COMMERCIAL AND INDUSTRIAL EQUIPMENT AIR-COOLED COMMERCIAL UNITARY AIR CONDITIONERS AND COMMERCIAL UNITARY HEAT PUMPS"
- crankcase_heater_power = ((60 * (stage_caps_heating[rated_stage_num_heating] * 0.0002843451 / 10)**0.67))
+ crankcase_heater_power = ((60 * ((stage_caps_heating[rated_stage_num_heating] * 0.0002843451 / 10)**0.67)))
new_dx_heating_coil.setCrankcaseHeaterCapacity(crankcase_heater_power)
new_dx_heating_coil.setMaximumOutdoorDryBulbTemperatureforCrankcaseHeaterOperation(4.4)
new_dx_heating_coil.setDefrostEnergyInputRatioFunctionofTemperatureCurve(defrost_eir)
@@ -785,7 +785,7 @@ def set_heating_coil_stages(model, runner, stage_flows_heating, stage_caps_heati
new_dx_heating_coil.setApplyPartLoadFractiontoSpeedsGreaterthan1(enable_cycling_losses_above_lowest_speed)
new_dx_heating_coil.setFuelType('Electricity')
# methods from "TECHNICAL SUPPORT DOCUMENT: ENERGY EFFICIENCY PROGRAM FOR CONSUMER PRODUCTS AND COMMERCIAL AND INDUSTRIAL EQUIPMENT AIR-COOLED COMMERCIAL UNITARY AIR CONDITIONERS AND COMMERCIAL UNITARY HEAT PUMPS"
- crankcase_heater_power = ((60 * (stage_caps_heating[rated_stage_num_heating] * 0.0002843451 / 10)**0.67))
+ crankcase_heater_power = ((60 * ((stage_caps_heating[rated_stage_num_heating] * 0.0002843451 / 10)**0.67)))
new_dx_heating_coil.setCrankcaseHeaterCapacity(crankcase_heater_power)
new_dx_heating_coil.setMaximumOutdoorDryBulbTemperatureforCrankcaseHeaterOperation(4.4)
new_dx_heating_coil.setDefrostEnergyInputRatioFunctionofTemperatureCurve(defrost_eir)
@@ -881,10 +881,10 @@ def self.get_dep_var_from_lookup_table_with_interpolation(runner, lookup_table,
y2 = ind_var_2[i2_upper]
# Get dependent variable values for bilinear interpolation
- v11 = dep_var[i1_lower * ind_var_2.size + i2_lower] # (x1, y1)
- v12 = dep_var[i1_lower * ind_var_2.size + i2_upper] # (x1, y2)
- v21 = dep_var[i1_upper * ind_var_2.size + i2_lower] # (x2, y1)
- v22 = dep_var[i1_upper * ind_var_2.size + i2_upper] # (x2, y2)
+ v11 = dep_var[(i1_lower * ind_var_2.size) + i2_lower] # (x1, y1)
+ v12 = dep_var[(i1_lower * ind_var_2.size) + i2_upper] # (x1, y2)
+ v21 = dep_var[(i1_upper * ind_var_2.size) + i2_lower] # (x2, y1)
+ v22 = dep_var[(i1_upper * ind_var_2.size) + i2_upper] # (x2, y2)
# If exact match, return directly
if input1 == x1 && input2 == y1
@@ -901,15 +901,15 @@ def self.get_dep_var_from_lookup_table_with_interpolation(runner, lookup_table,
dx = x2 - x1
dy = y2 - y1
return v11 if dx == 0 && dy == 0
- return v11 + (v21 - v11) * (input1 - x1) / dx if dy == 0
- return v11 + (v12 - v11) * (input2 - y1) / dy if dx == 0
+ return v11 + ((v21 - v11) * (input1 - x1) / dx) if dy == 0
+ return v11 + ((v12 - v11) * (input2 - y1) / dy) if dx == 0
# Bilinear interpolation
interpolated_value =
- v11 * (x2 - input1) * (y2 - input2) +
- v21 * (input1 - x1) * (y2 - input2) +
- v12 * (x2 - input1) * (input2 - y1) +
- v22 * (input1 - x1) * (input2 - y1)
+ (v11 * (x2 - input1) * (y2 - input2)) +
+ (v21 * (input1 - x1) * (y2 - input2)) +
+ (v12 * (x2 - input1) * (input2 - y1)) +
+ (v22 * (input1 - x1) * (input2 - y1))
interpolated_value /= (x2 - x1) * (y2 - y1)
@@ -991,9 +991,9 @@ def run(model, runner, user_arguments)
air_loop_hvac.supplyComponents.each do |component|
obj_type = component.iddObjectType.valueName.to_s
# flag system if contains water coil; this will cause air loop to be skipped
- is_water_coil = true if %w[Coil_Heating_Water Coil_Cooling_Water].any? { |word| obj_type.include?(word) }
+ is_water_coil = true if ['Coil_Heating_Water', 'Coil_Cooling_Water'].any? { |word| obj_type.include?(word) }
# flag gas heating as true if gas coil is found in any airloop
- prim_ht_fuel_type = 'gas' if %w[Gas GAS gas].any? { |word| obj_type.include?(word) }
+ prim_ht_fuel_type = 'gas' if ['Gas', 'GAS', 'gas'].any? { |word| obj_type.include?(word) }
# check unitary systems for DX heating or water coils
if obj_type == 'OS_AirLoopHVAC_UnitarySystem'
unitary_sys = component.to_AirLoopHVACUnitarySystem.get
@@ -1008,7 +1008,7 @@ def run(model, runner, user_arguments)
elsif ['Water'].any? { |word| htg_coil.include?(word) }
is_water_coil = true
# check for gas heating
- elsif %w[Gas GAS gas].any? { |word| htg_coil.include?(word) }
+ elsif ['Gas', 'GAS', 'gas'].any? { |word| htg_coil.include?(word) }
prim_ht_fuel_type = 'gas'
end
else
@@ -1037,9 +1037,9 @@ def run(model, runner, user_arguments)
air_loop_hvac.name.get.include?(word)
end
# skip kitchens
- next if %w[Kitchen KITCHEN Kitchen].any? { |word| air_loop_hvac.name.get.include?(word) }
+ next if ['Kitchen', 'KITCHEN', 'Kitchen'].any? { |word| air_loop_hvac.name.get.include?(word) }
# skip VAV sysems
- next if %w[VAV PVAV].any? { |word| air_loop_hvac.name.get.include?(word) }
+ next if ['VAV', 'PVAV'].any? { |word| air_loop_hvac.name.get.include?(word) }
# skip if residential system
next if air_loop_res?(air_loop_hvac)
# skip if system has no outdoor air, also indication of residential system
@@ -1290,7 +1290,7 @@ def run(model, runner, user_arguments)
# building type not applicable to ERVs as part of this measure will receive no additional or modification of ERV systems
# this is only relevant if the user selected to add ERVs
# space type applicability is handled later in the code when looping through individual air loops
- building_types_to_exclude = %w[RFF RSD QuickServiceRestaurant FullServiceRestaurant]
+ building_types_to_exclude = ['RFF', 'RSD', 'QuickServiceRestaurant', 'FullServiceRestaurant']
# determine building type applicability for ERV
btype_erv_applicable = true
building_types_to_exclude = building_types_to_exclude.map(&:downcase)
@@ -1315,7 +1315,7 @@ def run(model, runner, user_arguments)
# Get ER/HR type from climate zone
_, _, doas_type =
- if %w[1A 2A 3A 4A 5A 6A 7 7A 8 8A].include?(climate_zone_classification)
+ if ['1A', '2A', '3A', '4A', '5A', '6A', '7', '7A', '8', '8A'].include?(climate_zone_classification)
[12.7778, 19.4444, 'ERV']
else
[15.5556, 19.4444, 'HRV']
@@ -1350,97 +1350,57 @@ def run(model, runner, user_arguments)
# ---------------------------------------------------------
# Curve Import - Cooling capacity as a function of temperature
case hprtu_scenario
- when 'variable_speed_high_eff'
+ when 'variable_speed_high_eff', 'cchpc_2027_spec'
cool_cap_ft1 = model_add_curve(model, 'cool_cap_ft1', custom_data_json, std)
cool_cap_ft2 = model_add_curve(model, 'cool_cap_ft2', custom_data_json, std)
cool_cap_ft3 = model_add_curve(model, 'cool_cap_ft3', custom_data_json, std)
cool_cap_ft4 = model_add_curve(model, 'cool_cap_ft4', custom_data_json, std)
cool_cap_ft_curve_stages = { 1 => cool_cap_ft1, 2 => cool_cap_ft2, 3 => cool_cap_ft3, 4 => cool_cap_ft4 }
- when 'two_speed_standard_eff'
- cool_cap_ft1 = model_add_curve(model, 'c_cap_low_T', custom_data_json, std)
- cool_cap_ft2 = model_add_curve(model, 'c_cap_high_T', custom_data_json, std)
- cool_cap_ft_curve_stages = { 1 => cool_cap_ft1, 2 => cool_cap_ft2 }
- when 'two_speed_lab_data'
+ when 'two_speed_standard_eff', 'two_speed_lab_data'
cool_cap_ft1 = model_add_curve(model, 'c_cap_low_T', custom_data_json, std)
cool_cap_ft2 = model_add_curve(model, 'c_cap_high_T', custom_data_json, std)
cool_cap_ft_curve_stages = { 1 => cool_cap_ft1, 2 => cool_cap_ft2 }
- when 'cchpc_2027_spec'
- cool_cap_ft1 = model_add_curve(model, 'cool_cap_ft1', custom_data_json, std)
- cool_cap_ft2 = model_add_curve(model, 'cool_cap_ft2', custom_data_json, std)
- cool_cap_ft3 = model_add_curve(model, 'cool_cap_ft3', custom_data_json, std)
- cool_cap_ft4 = model_add_curve(model, 'cool_cap_ft4', custom_data_json, std)
- cool_cap_ft_curve_stages = { 1 => cool_cap_ft1, 2 => cool_cap_ft2, 3 => cool_cap_ft3, 4 => cool_cap_ft4 }
end
# Curve Import - Cooling efficiency as a function of temperature
case hprtu_scenario
- when 'variable_speed_high_eff'
+ when 'variable_speed_high_eff', 'cchpc_2027_spec'
cool_eir_ft1 = model_add_curve(model, 'cool_eir_ft1', custom_data_json, std)
cool_eir_ft2 = model_add_curve(model, 'cool_eir_ft2', custom_data_json, std)
cool_eir_ft3 = model_add_curve(model, 'cool_eir_ft3', custom_data_json, std)
cool_eir_ft4 = model_add_curve(model, 'cool_eir_ft4', custom_data_json, std)
cool_eir_ft_curve_stages = { 1 => cool_eir_ft1, 2 => cool_eir_ft2, 3 => cool_eir_ft3, 4 => cool_eir_ft4 }
- when 'two_speed_standard_eff'
- cool_eir_ft1 = model_add_curve(model, 'c_eir_low_T', custom_data_json, std)
- cool_eir_ft2 = model_add_curve(model, 'c_eir_high_T', custom_data_json, std)
- cool_eir_ft_curve_stages = { 1 => cool_eir_ft1, 2 => cool_eir_ft2 }
- when 'two_speed_lab_data'
+ when 'two_speed_standard_eff', 'two_speed_lab_data'
cool_eir_ft1 = model_add_curve(model, 'c_eir_low_T', custom_data_json, std)
cool_eir_ft2 = model_add_curve(model, 'c_eir_high_T', custom_data_json, std)
cool_eir_ft_curve_stages = { 1 => cool_eir_ft1, 2 => cool_eir_ft2 }
- when 'cchpc_2027_spec'
- cool_eir_ft1 = model_add_curve(model, 'cool_eir_ft1', custom_data_json, std)
- cool_eir_ft2 = model_add_curve(model, 'cool_eir_ft2', custom_data_json, std)
- cool_eir_ft3 = model_add_curve(model, 'cool_eir_ft3', custom_data_json, std)
- cool_eir_ft4 = model_add_curve(model, 'cool_eir_ft4', custom_data_json, std)
- cool_eir_ft_curve_stages = { 1 => cool_eir_ft1, 2 => cool_eir_ft2, 3 => cool_eir_ft3, 4 => cool_eir_ft4 }
end
# Curve Import - Cooling capacity as a function of flow rate
case hprtu_scenario
- when 'variable_speed_high_eff'
+ when 'variable_speed_high_eff', 'cchpc_2027_spec'
cool_cap_ff1 = model_add_curve(model, 'cool_cap_ff1', custom_data_json, std)
cool_cap_ff_curve_stages = { 1 => cool_cap_ff1, 2 => cool_cap_ff1, 3 => cool_cap_ff1, 4 => cool_cap_ff1 }
- when 'two_speed_standard_eff'
+ when 'two_speed_standard_eff', 'two_speed_lab_data'
cool_cap_ff1 = model_add_curve(model, 'c_cap_low_ff', custom_data_json, std)
cool_cap_ff2 = model_add_curve(model, 'c_cap_high_ff', custom_data_json, std)
cool_cap_ff_curve_stages = { 1 => cool_cap_ff1, 2 => cool_cap_ff2 }
- when 'two_speed_lab_data'
- cool_cap_ff1 = model_add_curve(model, 'c_cap_low_ff', custom_data_json, std)
- cool_cap_ff2 = model_add_curve(model, 'c_cap_high_ff', custom_data_json, std)
- cool_cap_ff_curve_stages = { 1 => cool_cap_ff1, 2 => cool_cap_ff2 }
- when 'cchpc_2027_spec'
- cool_cap_ff1 = model_add_curve(model, 'cool_cap_ff1', custom_data_json, std)
- cool_cap_ff_curve_stages = { 1 => cool_cap_ff1, 2 => cool_cap_ff1, 3 => cool_cap_ff1, 4 => cool_cap_ff1 }
end
# Curve Import - Cooling efficiency as a function of flow rate
case hprtu_scenario
- when 'variable_speed_high_eff'
+ when 'variable_speed_high_eff', 'cchpc_2027_spec'
cool_eir_ff1 = model_add_curve(model, 'cool_eir_ff1', custom_data_json, std)
cool_eir_ff_curve_stages = { 1 => cool_eir_ff1, 2 => cool_eir_ff1, 3 => cool_eir_ff1, 4 => cool_eir_ff1 }
- when 'two_speed_standard_eff'
- cool_eir_ff1 = model_add_curve(model, 'c_eir_low_ff', custom_data_json, std)
- cool_eir_ff2 = model_add_curve(model, 'c_eir_high_ff', custom_data_json, std)
- cool_eir_ff_curve_stages = { 1 => cool_eir_ff1, 2 => cool_eir_ff2 }
- when 'two_speed_lab_data'
+ when 'two_speed_standard_eff', 'two_speed_lab_data'
cool_eir_ff1 = model_add_curve(model, 'c_eir_low_ff', custom_data_json, std)
cool_eir_ff2 = model_add_curve(model, 'c_eir_high_ff', custom_data_json, std)
cool_eir_ff_curve_stages = { 1 => cool_eir_ff1, 2 => cool_eir_ff2 }
- when 'cchpc_2027_spec'
- cool_eir_ff1 = model_add_curve(model, 'cool_eir_ff1', custom_data_json, std)
- cool_eir_ff_curve_stages = { 1 => cool_eir_ff1, 2 => cool_eir_ff1, 3 => cool_eir_ff1, 4 => cool_eir_ff1 }
end
# Curve Import - Cooling efficiency as a function of part load ratio
case hprtu_scenario
- when 'variable_speed_high_eff'
- cool_plf_fplr1 = model_add_curve(model, 'cool_plf_plr1', custom_data_json, std)
- when 'two_speed_standard_eff'
- cool_plf_fplr1 = model_add_curve(model, 'cool_plf_plr1', custom_data_json, std)
- when 'two_speed_lab_data'
- cool_plf_fplr1 = model_add_curve(model, 'cool_plf_plr1', custom_data_json, std)
- when 'cchpc_2027_spec'
+ when 'variable_speed_high_eff', 'two_speed_standard_eff', 'two_speed_lab_data', 'cchpc_2027_spec'
cool_plf_fplr1 = model_add_curve(model, 'cool_plf_plr1', custom_data_json, std)
end
@@ -1455,10 +1415,7 @@ def run(model, runner, user_arguments)
heat_cap_ft3 = model_add_curve(model, 'heat_cap_ft3', custom_data_json, std)
heat_cap_ft4 = model_add_curve(model, 'heat_cap_ft4', custom_data_json, std)
heat_cap_ft_curve_stages = { 1 => heat_cap_ft1, 2 => heat_cap_ft2, 3 => heat_cap_ft3, 4 => heat_cap_ft4 }
- when 'two_speed_standard_eff'
- heat_cap_ft1 = model_add_curve(model, 'h_cap_T', custom_data_json, std)
- heat_cap_ft_curve_stages = { 1 => heat_cap_ft1 }
- when 'two_speed_lab_data'
+ when 'two_speed_standard_eff', 'two_speed_lab_data'
heat_cap_ft1 = model_add_curve(model, 'h_cap_T', custom_data_json, std)
heat_cap_ft_curve_stages = { 1 => heat_cap_ft1 }
when 'cchpc_2027_spec'
@@ -1477,10 +1434,7 @@ def run(model, runner, user_arguments)
heat_eir_ft3 = model_add_curve(model, 'heat_eir_ft3', custom_data_json, std)
heat_eir_ft4 = model_add_curve(model, 'heat_eir_ft4', custom_data_json, std)
heat_eir_ft_curve_stages = { 1 => heat_eir_ft1, 2 => heat_eir_ft2, 3 => heat_eir_ft3, 4 => heat_eir_ft4 }
- when 'two_speed_standard_eff'
- heat_eir_ft1 = model_add_curve(model, 'h_eir_T', custom_data_json, std)
- heat_eir_ft_curve_stages = { 1 => heat_eir_ft1 }
- when 'two_speed_lab_data'
+ when 'two_speed_standard_eff', 'two_speed_lab_data'
heat_eir_ft1 = model_add_curve(model, 'h_eir_T', custom_data_json, std)
heat_eir_ft_curve_stages = { 1 => heat_eir_ft1 }
when 'cchpc_2027_spec'
@@ -1496,10 +1450,7 @@ def run(model, runner, user_arguments)
when 'variable_speed_high_eff'
heat_cap_ff1 = model_add_curve(model, 'heat_cap_ff1', custom_data_json, std)
heat_cap_ff_curve_stages = { 1 => heat_cap_ff1, 2 => heat_cap_ff1, 3 => heat_cap_ff1, 4 => heat_cap_ff1 }
- when 'two_speed_standard_eff'
- heat_cap_ff1 = model_add_curve(model, 'h_cap_allstages_ff', custom_data_json, std)
- heat_cap_ff_curve_stages = { 1 => heat_cap_ff1 }
- when 'two_speed_lab_data'
+ when 'two_speed_standard_eff', 'two_speed_lab_data'
heat_cap_ff1 = model_add_curve(model, 'h_cap_allstages_ff', custom_data_json, std)
heat_cap_ff_curve_stages = { 1 => heat_cap_ff1 }
when 'cchpc_2027_spec'
@@ -1512,10 +1463,7 @@ def run(model, runner, user_arguments)
when 'variable_speed_high_eff'
heat_eir_ff1 = model_add_curve(model, 'heat_eir_ff1', custom_data_json, std)
heat_eir_ff_curve_stages = { 1 => heat_eir_ff1, 2 => heat_eir_ff1, 3 => heat_eir_ff1, 4 => heat_eir_ff1 }
- when 'two_speed_standard_eff'
- heat_eir_ff1 = model_add_curve(model, 'h_eir_allstages_ff', custom_data_json, std)
- heat_eir_ff_curve_stages = { 1 => heat_eir_ff1 }
- when 'two_speed_lab_data'
+ when 'two_speed_standard_eff', 'two_speed_lab_data'
heat_eir_ff1 = model_add_curve(model, 'h_eir_allstages_ff', custom_data_json, std)
heat_eir_ff_curve_stages = { 1 => heat_eir_ff1 }
when 'cchpc_2027_spec'
@@ -1526,26 +1474,14 @@ def run(model, runner, user_arguments)
# Curve Import - Heating efficiency as a function of part load ratio
heat_plf_fplr1 = nil
case hprtu_scenario
- when 'variable_speed_high_eff'
- heat_plf_fplr1 = model_add_curve(model, 'heat_plf_plr1', custom_data_json, std)
- when 'two_speed_standard_eff'
- heat_plf_fplr1 = model_add_curve(model, 'heat_plf_plr1', custom_data_json, std)
- when 'two_speed_lab_data'
- heat_plf_fplr1 = model_add_curve(model, 'heat_plf_plr1', custom_data_json, std)
- when 'cchpc_2027_spec'
+ when 'variable_speed_high_eff', 'two_speed_standard_eff', 'two_speed_lab_data', 'cchpc_2027_spec'
heat_plf_fplr1 = model_add_curve(model, 'heat_plf_plr1', custom_data_json, std)
end
# Curve Import - Defrost energy as a function of temperature
defrost_eir = nil
case hprtu_scenario
- when 'variable_speed_high_eff'
- defrost_eir = model_add_curve(model, 'defrost_eir', custom_data_json, std)
- when 'two_speed_standard_eff'
- defrost_eir = model_add_curve(model, 'defrost_eir', custom_data_json, std)
- when 'two_speed_lab_data'
- defrost_eir = model_add_curve(model, 'defrost_eir', custom_data_json, std)
- when 'cchpc_2027_spec'
+ when 'variable_speed_high_eff', 'two_speed_standard_eff', 'two_speed_lab_data', 'cchpc_2027_spec'
defrost_eir = model_add_curve(model, 'defrost_eir', custom_data_json, std)
end
@@ -1744,7 +1680,7 @@ def run(model, runner, user_arguments)
# convert component to string name
obj_type = component.iddObjectType.valueName.to_s
# skip unless component is of relevant type
- next unless %w[Fan Unitary Coil].any? { |word| obj_type.include?(word) }
+ next unless ['Fan', 'Unitary', 'Coil'].any? { |word| obj_type.include?(word) }
# make list of equipment to delete
equip_to_delete << component
@@ -1782,7 +1718,7 @@ def run(model, runner, user_arguments)
if supply_fan_avail_sched.to_ScheduleConstant.is_initialized
supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleConstant.get
elsif supply_fan_avail_sched.to_ScheduleRuleset.is_initialized
- supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleConstant.get
+ supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleRuleset.get
else
runner.registerError("Supply fan availability schedule type for #{supply_fan.name} not supported.")
return false
@@ -1849,7 +1785,7 @@ def run(model, runner, user_arguments)
# convert component to string name
obj_type = component.iddObjectType.valueName.to_s
# skip unless component is of relevant type
- next unless %w[Fan Unitary Coil].any? { |word| obj_type.include?(word) }
+ next unless ['Fan', 'Unitary', 'Coil'].any? { |word| obj_type.include?(word) }
# make list of equipment to delete
equip_to_delete << component
@@ -1872,7 +1808,7 @@ def run(model, runner, user_arguments)
if supply_fan_avail_sched.to_ScheduleConstant.is_initialized
supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleConstant.get
elsif supply_fan_avail_sched.to_ScheduleRuleset.is_initialized
- supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleConstant.get
+ supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleRuleset.get
else
runner.registerError("Supply fan availability schedule type for #{supply_fan.name} not supported.")
return false
@@ -2081,7 +2017,7 @@ def run(model, runner, user_arguments)
# user-specified design
oa_temp_c = hp_sizing_temp_c
- dns_htg_load_at_user_dsn_temp = htg_load_slope * hp_sizing_temp_c + htg_load_intercept
+ dns_htg_load_at_user_dsn_temp = (htg_load_slope * hp_sizing_temp_c) + htg_load_intercept
if heat_cap_ft_curve_stages[rated_stage_num_heating].to_TableLookup.is_initialized
table_lookup_obj = heat_cap_ft_curve_stages[rated_stage_num_heating].to_TableLookup.get
hp_derate_factor_at_user_dsn = AddHeatPumpRtu.get_dep_var_from_lookup_table_with_interpolation(runner, table_lookup_obj,
@@ -2199,11 +2135,7 @@ def run(model, runner, user_arguments)
end
# set airloop design airflow based on the maximum of heating and cooling design flow
- design_airflow_for_sizing_m_3_per_s = if design_cooling_airflow_m_3_per_s < design_heating_airflow_m_3_per_s
- design_heating_airflow_m_3_per_s
- else
- design_cooling_airflow_m_3_per_s
- end
+ design_airflow_for_sizing_m_3_per_s = [design_cooling_airflow_m_3_per_s, design_heating_airflow_m_3_per_s].max
# reset supply airflow if less than minimum OA
design_airflow_for_sizing_m_3_per_s = oa_flow_m3_per_s if oa_flow_m3_per_s > design_airflow_for_sizing_m_3_per_s
@@ -2240,12 +2172,12 @@ def run(model, runner, user_arguments)
# if oversizing is not specified (upsize_factor = 0.0), then use cooling design airflow
stage_flows_heating = {}
stage_flow_fractions_heating.each do |stage, ratio|
- airflow = if upsize_factor == 0.0
+ airflow = if upsize_factor.zero?
ratio * design_cooling_airflow_m_3_per_s
else
ratio * design_heating_airflow_m_3_per_s
end
- stage_flows_heating[stage] = airflow >= min_airflow_m3_per_s ? airflow : min_airflow_m3_per_s
+ stage_flows_heating[stage] = [airflow, min_airflow_m3_per_s].max
end
# determine airflows for each stage of cooling
@@ -2254,7 +2186,7 @@ def run(model, runner, user_arguments)
stage_flows_cooling = {}
stage_flow_fractions_cooling.sort.each do |stage, ratio|
airflow = ratio * design_cooling_airflow_m_3_per_s
- stage_flows_cooling[stage] = airflow >= min_airflow_m3_per_s ? airflow : min_airflow_m3_per_s
+ stage_flows_cooling[stage] = [airflow, min_airflow_m3_per_s].max
end
if debug_verbose
@@ -2508,7 +2440,7 @@ def run(model, runner, user_arguments)
next unless (hr == true) && (btype_erv_applicable == true)
# check for space type applicability
- thermal_zone_names_to_exclude = %w[Kitchen kitchen KITCHEN Dining dining DINING]
+ thermal_zone_names_to_exclude = ['Kitchen', 'kitchen', 'KITCHEN', 'Dining', 'dining', 'DINING']
# skip air loops that serve non-applicable space types and warn user
if thermal_zone_names_to_exclude.any? { |word| thermal_zone.name.to_s.include?(word) }
runner.registerWarning("The user selected to add energy recovery to the HP-RTUs, but thermal zone #{thermal_zone.name} is a non-applicable space type for energy recovery. Any existing energy recovery will remain for consistancy, but no new energy recovery will be added.")
diff --git a/resources/measures/upgrade_hvac_add_heat_pump_rtu/resources/sched_methods.rb b/resources/measures/upgrade_hvac_add_heat_pump_rtu/resources/sched_methods.rb
index 79262bf26..7fc014d52 100644
--- a/resources/measures/upgrade_hvac_add_heat_pump_rtu/resources/sched_methods.rb
+++ b/resources/measures/upgrade_hvac_add_heat_pump_rtu/resources/sched_methods.rb
@@ -156,9 +156,9 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
# Build array of arrays: each top element is a week, each sub element is an hour of week
all_week_values = []
hr_of_yr = -1
- (0..51).each do |_iweek|
+ 52.times do |_iweek|
week_values = []
- (0..167).each do |hr_of_wk|
+ 168.times do |hr_of_wk|
hr_of_yr += 1
week_values[hr_of_wk] = values[hr_of_yr]
end
@@ -199,9 +199,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
all_week_rules = { iweek_previous_week_rule: week_1_rules }
# temporary loop for debugging
- week_n_rules.each do |sch_rule|
- sch_rule.daySchedule
- end
+ week_n_rules.each(&:daySchedule)
# For each subsequent week, check if it is same as previous
# If same, then append to Schedule:Rule of previous week
@@ -210,7 +208,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
is_a_match = true
start_date = end_date + one_day
end_date += seven_days
- (0..167).each do |ihr|
+ 168.times do |ihr|
if all_week_values[iweek][ihr] != all_week_values[iweek_previous_week_rule][ihr]
is_a_match = false
break
@@ -223,7 +221,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
else
# Create a new week schedule for this week
num_week_scheds += 1
- week_sch_name = sch_name + '_ws' + num_week_scheds.to_s
+ week_sch_name = "#{sch_name}_ws#{num_week_scheds}"
week_n_rules = std.make_week_ruleset_sched_from_168(model, sch_ruleset, all_week_values[iweek], start_date,
end_date, week_sch_name)
all_week_rules[:iweek_previous_week_rule] = week_n_rules
@@ -233,9 +231,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
end
# temporary loop for debugging
- week_n_rules.each do |sch_rule|
- sch_rule.daySchedule
- end
+ week_n_rules.each(&:daySchedule)
# Need to handle week 52 with days 365 and 366
# For each of these days, check if it matches a day from the previous week
@@ -258,7 +254,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
day_values << now_value
end
end
- (0..23).each do |ihr|
+ 24.times do |ihr|
next unless day_values[ihr] != all_week_values[iweek][ihr + ihr_start]
# not matching for this day_rule
@@ -278,7 +274,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
day_names = [day_of_week]
day_sch_name = "#{sch_name}_Day_365"
day_sch_values = []
- (0..23).each do |ihr|
+ 24.times do |ihr|
day_sch_values << all_week_values[iweek][ihr]
end
# sch_rule is a sub-component of the ScheduleRuleset
@@ -361,10 +357,10 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
hr_of_yr = -1
max_eflh = 0
ihr_max = -1
- (0..364).each do |_iday|
+ 365.times do |_iday|
eflh = 0
ihr_start = hr_of_yr + 1
- (0..23).each do |_ihr|
+ 24.times do |_ihr|
hr_of_yr += 1
eflh += 1 if values[hr_of_yr] > 0
end
@@ -377,7 +373,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
# Create the schedules for the design days
day_sch = OpenStudio::Model::ScheduleDay.new(model)
day_sch.setName("#{sch_name} Winter Design Day")
- (0..23).each do |ihr|
+ 24.times do |ihr|
hr_of_yr = ihr_max + ihr
next if values[hr_of_yr] == values[hr_of_yr + 1]
@@ -387,7 +383,7 @@ def make_ruleset_sched_from_8760(model, _runner, values, sch_name, sch_type_limi
day_sch = OpenStudio::Model::ScheduleDay.new(model)
day_sch.setName("#{sch_name} Summer Design Day")
- (0..23).each do |ihr|
+ 24.times do |ihr|
hr_of_yr = ihr_max + ihr
next if values[hr_of_yr] == values[hr_of_yr + 1]
diff --git a/resources/measures/upgrade_hvac_add_heat_pump_rtu/tests/measure_test.rb b/resources/measures/upgrade_hvac_add_heat_pump_rtu/tests/measure_test.rb
index d055289de..f3aabdd6f 100644
--- a/resources/measures/upgrade_hvac_add_heat_pump_rtu/tests/measure_test.rb
+++ b/resources/measures/upgrade_hvac_add_heat_pump_rtu/tests/measure_test.rb
@@ -61,6 +61,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -1374,7 +1375,7 @@ def test_380_Small_Office_PSZ_Gas_2A
end
end
test_result = verify_hp_rtu(test_name, model, measure, argument_map, osm_path, epw_path)
-
+
# check roof/window measure implementation
roof_measure_implemented = false
window_measure_implemented = false
@@ -2232,11 +2233,11 @@ def test_confirm_heating_setback_change_square_wave
# Make sure no deltas are greater than the expected setback value
deltas_out_of_range = schedule_deltas.any? { |x| x > setback_value_c }
-
+
puts("Temperature deltas in schedule match expected values: #{(deltas_out_of_range == false)}")
assert_equal(deltas_out_of_range, false)
-
+
true
end
@@ -2330,12 +2331,23 @@ def test_confirm_heating_setback_change_opt_start
# Make sure no deltas are greater than the expected setback value
deltas_out_of_range = schedule_deltas.any? { |x| x > setback_value_c }
-
-
puts("Temperature deltas in schedule match expected values: #{(deltas_out_of_range == false)}")
-
assert_equal(deltas_out_of_range, false)
-
true
end
+
+ def possible_opt_start(i, tstat_profile, tstat_profile_min)
+ # Check if this could be optimum start
+ # Optimum start typically ramps up temperature before occupied period
+ # Look for pattern: minimum temp, then gradual increase
+ values = tstat_profile.values
+ if values[i] == tstat_profile_min && i < values.length - 1
+ # Check if subsequent values are increasing
+ (i+1...values.length).each do |j|
+ return true if values[j] > values[i]
+ end
+ end
+ false
+ end
+end
end
diff --git a/resources/measures/upgrade_hvac_chiller/tests/upgrade_hvac_chiller_test.rb b/resources/measures/upgrade_hvac_chiller/tests/upgrade_hvac_chiller_test.rb
index 228319648..56410f505 100644
--- a/resources/measures/upgrade_hvac_chiller/tests/upgrade_hvac_chiller_test.rb
+++ b/resources/measures/upgrade_hvac_chiller/tests/upgrade_hvac_chiller_test.rb
@@ -58,6 +58,7 @@ def epws_for_tests
# supporting method: load model from osm path
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_condensing_boiler/tests/condensing_boiler_test.rb b/resources/measures/upgrade_hvac_condensing_boiler/tests/condensing_boiler_test.rb
index 3120df920..3d5ac5800 100644
--- a/resources/measures/upgrade_hvac_condensing_boiler/tests/condensing_boiler_test.rb
+++ b/resources/measures/upgrade_hvac_condensing_boiler/tests/condensing_boiler_test.rb
@@ -57,6 +57,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_console_gshp/measure.xml b/resources/measures/upgrade_hvac_console_gshp/measure.xml
index 72fe83ed0..3676e2ee5 100644
--- a/resources/measures/upgrade_hvac_console_gshp/measure.xml
+++ b/resources/measures/upgrade_hvac_console_gshp/measure.xml
@@ -2,9 +2,9 @@
3.1
add_console_gshp
- e7cff13c-fffa-4c6c-9494-938e27f7f335
- 33233145-3dfe-4820-b2f4-13bee66b158d
- 2025-07-23T23:00:35Z
+ a6568f73-6e8a-4749-82e4-640c8acdd68b
+ 0380dad8-0ee4-4c1d-9a7a-882ff78aad41
+ 2026-01-13T17:23:00Z
5E2576E4
AddConsoleGSHP
add_console_gshp
diff --git a/resources/measures/upgrade_hvac_console_gshp/tests/console_gthp_test.rb b/resources/measures/upgrade_hvac_console_gshp/tests/console_gthp_test.rb
index 59dfcb001..a7087dd05 100644
--- a/resources/measures/upgrade_hvac_console_gshp/tests/console_gthp_test.rb
+++ b/resources/measures/upgrade_hvac_console_gshp/tests/console_gthp_test.rb
@@ -54,7 +54,7 @@ def setup
Open3.capture3(command)
rescue StandardError
msg = 'GHEDesigner python package not found in this test environment, pip install GHEDesigner and retry'
- raise LoadError.new msg
+ raise LoadError, msg
end
end
@@ -71,6 +71,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_dcv/tests/hvac_dcv_test.rb b/resources/measures/upgrade_hvac_dcv/tests/hvac_dcv_test.rb
index 248095657..1d90515c5 100644
--- a/resources/measures/upgrade_hvac_dcv/tests/hvac_dcv_test.rb
+++ b/resources/measures/upgrade_hvac_dcv/tests/hvac_dcv_test.rb
@@ -82,6 +82,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_doas_hp_minisplits/measure.rb b/resources/measures/upgrade_hvac_doas_hp_minisplits/measure.rb
index 61924db95..e120c7ecd 100644
--- a/resources/measures/upgrade_hvac_doas_hp_minisplits/measure.rb
+++ b/resources/measures/upgrade_hvac_doas_hp_minisplits/measure.rb
@@ -10,17 +10,17 @@
class HvacDoasHpMinisplits < OpenStudio::Measure::ModelMeasure
# human readable name
def name
- return "hvac_doas_hp_minisplits"
+ return 'hvac_doas_hp_minisplits'
end
# human readable description
def description
- return "TODO"
+ return 'TODO'
end
# human readable description of modeling approach
def modeler_description
- return "TODO"
+ return 'TODO'
end
# define the arguments that the user will input
@@ -35,7 +35,7 @@ def arguments(model)
args << area_limit_sf
# make list of backup heat options
- li_doas_heat_options = ["gas_furnace", "electric_resistance"]
+ li_doas_heat_options = ['gas_furnace', 'electric_resistance']
v_doas_heat_options = OpenStudio::StringVector.new
li_doas_heat_options.each do |option|
v_doas_heat_options << option
@@ -44,7 +44,7 @@ def arguments(model)
doas_htg_fuel = OpenStudio::Measure::OSArgument.makeChoiceArgument('doas_htg_fuel', v_doas_heat_options, true)
doas_htg_fuel.setDisplayName('DOAS Heating Fuel Source')
doas_htg_fuel.setDescription('Heating fuel source for DOAS, either gas furnace or electric resistance. DOAS will provide minimal preheating to provide reasonable nuetral air supplied to zone. The ERV/HRV will first try to accomodate this, with the heating coil addressing any additional load. Note that the zone heat pumps are still responsible for maintaining thermostat setpoints.')
- doas_htg_fuel.setDefaultValue("electric_resistance")
+ doas_htg_fuel.setDefaultValue('electric_resistance')
args << doas_htg_fuel
# add RTU oversizing factor for heating
@@ -59,7 +59,6 @@ def arguments(model)
# define the outputs that the measure will create
def outputs
-
# outs = OpenStudio::Measure::OSOutputVector.new
output_names = []
@@ -140,20 +139,20 @@ def run(model, runner, user_arguments)
# DOAS temperature supply settings - colder cooling discharge air for humid climates
- doas_dat_clg_c=nil
- doas_dat_htg_c=nil
- doas_type=nil
- doas_includes_clg=nil
- if ['1A', '2A', '3A', '4A', '5A', '6A', '7', '7A', '8', '8A'].any? { |word| (climate_zone_classification).include?(word) }
+ doas_dat_clg_c = nil
+ doas_dat_htg_c = nil
+ doas_type = nil
+ doas_includes_clg = nil
+ if ['1A', '2A', '3A', '4A', '5A', '6A', '7', '7A', '8', '8A'].any? { |word| climate_zone_classification.include?(word) }
doas_dat_clg_c = 12.7778
doas_dat_htg_c = 19.4444
doas_type = 'ERV'
- doas_includes_clg=true
+ doas_includes_clg = true
else
doas_dat_clg_c = 15.5556
doas_dat_htg_c = 19.4444
doas_type = 'HRV'
- doas_includes_clg=false
+ doas_includes_clg = false
end
# heat pump discharge air temperatures
@@ -174,34 +173,35 @@ def run(model, runner, user_arguments)
total_area_m2 += thermal_zone.floorArea * thermal_zone.multiplier
# skip DOAS units; check sizing for all OA and for DOAS in name
sizing_system = air_loop_hvac.sizingSystem
- next if sizing_system.allOutdoorAirinCooling && sizing_system.allOutdoorAirinHeating && (air_loop_res?(air_loop_hvac) == false) && (air_loop_hvac.name.to_s.include?("DOAS") || air_loop_hvac.name.to_s.include?("doas"))
+ next if sizing_system.allOutdoorAirinCooling && sizing_system.allOutdoorAirinHeating && (air_loop_res?(air_loop_hvac) == false) && (air_loop_hvac.name.to_s.include?('DOAS') || air_loop_hvac.name.to_s.include?('doas'))
+
# skip if already heat pump RTU
# loop throug air loop components to check for heat pump or water coils
- is_hp=false
- is_water_coil=false
- has_heating_coil=true
+ is_hp = false
+ is_water_coil = false
+ has_heating_coil = true
air_loop_hvac.supplyComponents.each do |component|
obj_type = component.iddObjectType.valueName.to_s
# flag system if contains water coil; this will cause air loop to be skipped
- is_water_coil=true if ['Coil_Heating_Water', 'Coil_Cooling_Water'].any? { |word| (obj_type).include?(word) }
+ is_water_coil = true if ['Coil_Heating_Water', 'Coil_Cooling_Water'].any? { |word| obj_type.include?(word) }
# flag gas heating as true if gas coil is found in any airloop
- prim_ht_fuel_type= 'gas' if ['Gas', 'GAS', 'gas'].any? { |word| (obj_type).include?(word) }
+ prim_ht_fuel_type = 'gas' if ['Gas', 'GAS', 'gas'].any? { |word| obj_type.include?(word) }
# check unitary systems for DX heating or water coils
- if obj_type=='OS_AirLoopHVAC_UnitarySystem'
+ if obj_type == 'OS_AirLoopHVAC_UnitarySystem'
unitary_sys = component.to_AirLoopHVACUnitarySystem.get
# check if heating coil is DX or water-based; if so, flag the air loop to be skipped
if unitary_sys.heatingCoil.is_initialized
htg_coil = unitary_sys.heatingCoil.get.iddObjectType.valueName.to_s
# check for DX heating coil
- if ['Heating_DX'].any? { |word| (htg_coil).include?(word) }
- is_hp=true
+ if ['Heating_DX'].any? { |word| htg_coil.include?(word) }
+ is_hp = true
# check for water heating coil
- elsif ['Water'].any? { |word| (htg_coil).include?(word) }
- is_water_coil=true
+ elsif ['Water'].any? { |word| htg_coil.include?(word) }
+ is_water_coil = true
# check for gas heating
- elsif ['Gas', 'GAS', 'gas'].any? { |word| (htg_coil).include?(word) }
- prim_ht_fuel_type='gas'
+ elsif ['Gas', 'GAS', 'gas'].any? { |word| htg_coil.include?(word) }
+ prim_ht_fuel_type = 'gas'
end
else
runner.registerWarning("No heating coil was found for air loop: #{air_loop_hvac.name} - this equipment will be skipped.")
@@ -211,22 +211,23 @@ def run(model, runner, user_arguments)
if unitary_sys.coolingCoil.is_initialized
clg_coil = unitary_sys.coolingCoil.get.iddObjectType.valueName.to_s
# skip unless coil is water based
- next unless ['Water'].any? { |word| (clg_coil).include?(word) }
- is_water_coil=true
+ next unless ['Water'].any? { |word| clg_coil.include?(word) }
+
+ is_water_coil = true
end
# flag as hp if air loop contains a heating dx coil
- elsif ['Heating_DX'].any? { |word| (obj_type).include?(word) }
- is_hp=true
+ elsif ['Heating_DX'].any? { |word| obj_type.include?(word) }
+ is_hp = true
end
end
# also skip based on string match, or if dx heating component existed
- next if (is_hp==true) | (((air_loop_hvac.name.to_s.include?("HP")) || (air_loop_hvac.name.to_s.include?("hp")) || (air_loop_hvac.name.to_s.include?("heat pump")) || (air_loop_hvac.name.to_s.include?("Heat Pump"))))
+ next if (is_hp == true) | ((air_loop_hvac.name.to_s.include?('HP') || air_loop_hvac.name.to_s.include?('hp') || air_loop_hvac.name.to_s.include?('heat pump') || air_loop_hvac.name.to_s.include?('Heat Pump')))
# skip data centers
- next if ['Data Center', 'DataCenter', 'data center', 'datacenter', 'DATACENTER', 'DATA CENTER'].any? { |word| (air_loop_hvac.name.get).include?(word) }
+ next if ['Data Center', 'DataCenter', 'data center', 'datacenter', 'DATACENTER', 'DATA CENTER'].any? { |word| air_loop_hvac.name.get.include?(word) }
# skip kitchens
- next if ['Kitchen', 'KITCHEN', 'Kitchen'].any? { |word| (air_loop_hvac.name.get).include?(word) }
+ next if ['Kitchen', 'KITCHEN', 'Kitchen'].any? { |word| air_loop_hvac.name.get.include?(word) }
# skip VAV sysems
- next if ['VAV', 'PVAV'].any? { |word| (air_loop_hvac.name.get).include?(word) }
+ next if ['VAV', 'PVAV'].any? { |word| air_loop_hvac.name.get.include?(word) }
# skip if residential system
next if air_loop_res?(air_loop_hvac)
# skip if system has no outdoor air, also indication of residential system
@@ -234,11 +235,12 @@ def run(model, runner, user_arguments)
# skip if evaporative cooling systems
next if air_loop_evaporative_cooler?(air_loop_hvac)
# skip if water heating or cooled system
- next if is_water_coil==true
+ next if is_water_coil == true
# skip if space is not heated and cooled
- next unless (OpenstudioStandards::ThermalZone.thermal_zone_heated?(air_loop_hvac.thermalZones[0])) && (OpenstudioStandards::ThermalZone.thermal_zone_cooled?(air_loop_hvac.thermalZones[0]))
+ next unless OpenstudioStandards::ThermalZone.thermal_zone_heated?(air_loop_hvac.thermalZones[0]) && OpenstudioStandards::ThermalZone.thermal_zone_cooled?(air_loop_hvac.thermalZones[0])
# next if no heating coil
next if has_heating_coil == false
+
# add applicable air loop to list
selected_air_loops << air_loop_hvac
# add area served by air loop
@@ -247,7 +249,7 @@ def run(model, runner, user_arguments)
end
# fraction of conditioned floorspace
- if total_area_m2 >0
+ if total_area_m2 > 0
applicable_floorspace_frac = applicable_area_m2 / total_area_m2
else
applicable_floorspace_frac = 0
@@ -257,22 +259,21 @@ def run(model, runner, user_arguments)
total_area_ft2 = OpenStudio.convert(total_area_m2, 'm^2', 'ft^2').get
# check if any air loops are applicable to measure
- if (selected_air_loops.empty?)
+ if selected_air_loops.empty?
runner.registerAsNotApplicable('No applicable air loops in model. No changes will be made.')
return false
- elsif (applicable_area_m2 > area_limit_m2)
- runner.registerAsNotApplicable("Applicable building area of #{total_area_ft2.round()} exceeds user-defined maximum limit of #{area_limit_sf} square feet. Measure will not be applied.")
+ elsif applicable_area_m2 > area_limit_m2
+ runner.registerAsNotApplicable("Applicable building area of #{total_area_ft2.round} exceeds user-defined maximum limit of #{area_limit_sf} square feet. Measure will not be applied.")
return false
end
# report initial condition of model
- runner.registerInitialCondition("The building has #{selected_air_loops.size} applicable air loops that will be replaced with a DOAS-ERV/HRV and heat pump ductless minisplits, representing #{(applicable_floorspace_frac*100).round(2)}% of the building floor area.")
+ runner.registerInitialCondition("The building has #{selected_air_loops.size} applicable air loops that will be replaced with a DOAS-ERV/HRV and heat pump ductless minisplits, representing #{(applicable_floorspace_frac * 100).round(2)}% of the building floor area.")
thermal_zones = []
# replace existing applicable air loops with new heat pump rtu air loops
selected_air_loops.sort.each do |air_loop_hvac|
-
# first update existing RTU to be DOAS
# start with unitary systems
@@ -280,15 +281,14 @@ def run(model, runner, user_arguments)
# loop through air loop supply side components
air_loop_hvac.supplyComponents.each do |component|
-
# convert component to string name
obj_type = component.iddObjectType.valueName.to_s
# skip unless component is of relevant type
- next unless ['Fan', 'Unitary', 'Coil'].any? { |word| (obj_type).include?(word) }
+ next unless ['Fan', 'Unitary', 'Coil'].any? { |word| obj_type.include?(word) }
# remove any existing coils or fans
- if ['Fan', 'Coil'].any? { |word| (obj_type).include?(word) }
+ if ['Fan', 'Coil'].any? { |word| obj_type.include?(word) }
model.removeObject(component.handle)
end
@@ -337,19 +337,19 @@ def run(model, runner, user_arguments)
# add new cooling coil
clg_coil = std.create_coil_cooling_dx_single_speed(model,
- air_loop_node: supply_outlet_node,
- name: "#{air_loop_hvac.name} 1spd DX AC Clg Coil",
- type: 'PSZ-AC')
+ air_loop_node: supply_outlet_node,
+ name: "#{air_loop_hvac.name} 1spd DX AC Clg Coil",
+ type: 'PSZ-AC')
# add new electric heating coil
htg_coil = std.create_coil_heating_electric(model,
- air_loop_node: supply_outlet_node,
- name: "#{air_loop_hvac.name} Electric Htg Coil")
+ air_loop_node: supply_outlet_node,
+ name: "#{air_loop_hvac.name} Electric Htg Coil")
# add new fan
fan = std.create_fan_constant_volume(model,
- fan_name: "#{air_loop_hvac.name} Constant Volume Supply Fan",
- pressure_rise: fan_static_pressure)
+ fan_name: "#{air_loop_hvac.name} Constant Volume Supply Fan",
+ pressure_rise: fan_static_pressure)
fan.addToNode(supply_outlet_node) unless supply_outlet_node.nil?
# set airloop name
@@ -380,11 +380,11 @@ def run(model, runner, user_arguments)
air_loop_sizing.setCentralHeatingDesignSupplyAirTemperature(doas_dat_htg_c) # 52F as per ASHRAE DOAS design guide
air_loop_sizing.setAllOutdoorAirinCooling(true)
air_loop_sizing.setAllOutdoorAirinHeating(true)
- #air_loop_sizing.setMinimumSystemAirFlowRatio(1)
- air_loop_sizing.autosizeCoolingDesignCapacity() # for hardsized baseline
- air_loop_sizing.autosizeHeatingDesignCapacity() # for hardsized baseline
- air_loop_sizing.resetDesignOutdoorAirFlowRate() # for hardsized baseline
- air_loop_hvac.autosizeDesignSupplyAirFlowRate() # for hardsized baseline
+ # air_loop_sizing.setMinimumSystemAirFlowRatio(1)
+ air_loop_sizing.autosizeCoolingDesignCapacity # for hardsized baseline
+ air_loop_sizing.autosizeHeatingDesignCapacity # for hardsized baseline
+ air_loop_sizing.resetDesignOutdoorAirFlowRate # for hardsized baseline
+ air_loop_hvac.autosizeDesignSupplyAirFlowRate # for hardsized baseline
# modify zone sizing settings for DOAS operation
zone_sizing = air_loop_hvac.thermalZones[0].sizingZone
@@ -414,7 +414,7 @@ def run(model, runner, user_arguments)
# get old terminal box - ensure autosized flow rate
if thermal_zone.airLoopHVACTerminal.get.to_AirTerminalSingleDuctConstantVolumeNoReheat.is_initialized
old_terminal = thermal_zone.airLoopHVACTerminal.get.to_AirTerminalSingleDuctConstantVolumeNoReheat.get
- old_terminal.autosizeMaximumAirFlowRate()
+ old_terminal.autosizeMaximumAirFlowRate
else
runner.registerError("Terminal box type for air loop #{air_loop_hvac.name} not supported.")
return false
@@ -427,13 +427,14 @@ def run(model, runner, user_arguments)
# If ERV flag was not selected, ERV equipment will remain in place as-is
erv_components = []
air_loop_hvac.oaComponents.each do |component|
- component_name = component.name.to_s
- next if component_name.include? "Node"
- if component_name.include? "ERV"
- erv_components << component
- erv_components = erv_components.uniq
- end
+ component_name = component.name.to_s
+ next if component_name.include? 'Node'
+
+ if component_name.include? 'ERV'
+ erv_components << component
+ erv_components = erv_components.uniq
end
+ end
# # if there was not previosuly an ERV, add 0.5" (124.42 pascals) static to supply fan
# new_fan.setPressureRise(fan_static_pressure + 124.42) if erv_components.empty?
# remove existing ERV; these will be replaced with new ERV equipment
@@ -480,7 +481,7 @@ def run(model, runner, user_arguments)
end
# modify outdoor air object for 100% outdoor air operation
- oa_controller= oa_system.getControllerOutdoorAir
+ oa_controller = oa_system.getControllerOutdoorAir
oa_controller.setMinimumFractionofOutdoorAirSchedule(model.alwaysOnDiscreteSchedule)
oa_controller.resetMaximumFractionofOutdoorAirSchedule
# remove economizer - not applicable with DOAS
@@ -504,7 +505,7 @@ def run(model, runner, user_arguments)
# add base unitary system properties
zone_unitary_hvac.setControlType('Load')
zone_unitary_hvac.setControllingZoneorThermostatLocation(thermal_zone)
- zone_unitary_hvac.setDehumidificationControlType("None")
+ zone_unitary_hvac.setDehumidificationControlType('None')
zone_unitary_hvac.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule)
# add supply fan
@@ -517,7 +518,7 @@ def run(model, runner, user_arguments)
new_fan.setFanPowerCoefficient3(4.496391)
new_fan.setFanPowerCoefficient4(-3.6426)
new_fan.setFanPowerCoefficient5(1.301203)
- new_fan.setFanPowerMinimumFlowRateInputMethod("Fraction")
+ new_fan.setFanPowerMinimumFlowRateInputMethod('Fraction')
new_fan.setName("#{air_loop_hvac.name} Zone Mini Split Heat Pump Supply Fan")
zone_unitary_hvac.setSupplyFan(new_fan)
zone_unitary_hvac.setFanPlacement('BlowThrough')
@@ -558,7 +559,7 @@ def run(model, runner, user_arguments)
# get zone design people
num_people = thermal_zone.numberOfPeople * thermal_zone.multiplier
- dsn_oa_m3_per_s= 0
+ dsn_oa_m3_per_s = 0
if space.designSpecificationOutdoorAir.is_initialized
dsn_spec_oa = space.designSpecificationOutdoorAir.get
@@ -576,6 +577,7 @@ def run(model, runner, user_arguments)
end
# delete air loop if less than minimum flow rate
next unless dsn_oa_m3_per_s < 1.0000E-003
+
runner.registerWarning("#{air_loop_hvac.name} has an outdoor air flow rate of #{dsn_oa_m3_per_s.round(3)} m3/s which is less than the required 0.001 m3/s. This DOAS will be deleted, but these zone equipment will remain.")
selected_air_loops.delete(air_loop_hvac)
air_loop_hvac.remove
@@ -590,7 +592,6 @@ def run(model, runner, user_arguments)
# loop through airloops to set multispeed coil objects and other parameters that require sizing values
selected_air_loops.sort.each do |air_loop_hvac|
-
# set DOAS wheel power
# get DOAS outdoor air flow rate; this will be used to set heat recovery wheel power
oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get
@@ -609,8 +610,9 @@ def run(model, runner, user_arguments)
erv_components = []
air_loop_hvac.oaComponents.each do |component|
component_name = component.name.to_s
- next if component_name.include? "Node"
- if ['ERV', 'HRV'].any? { |word| (component_name).include?(word) }
+ next if component_name.include? 'Node'
+
+ if ['ERV', 'HRV'].any? { |word| component_name.include?(word) }
new_hr = component.to_HeatExchangerAirToAirSensibleAndLatent.get
# for HRV systems
if doas_type == 'HRV'
@@ -647,44 +649,45 @@ def run(model, runner, user_arguments)
# loop through thermal zones for zone equipment
thermal_zones.each do |thermal_zone|
# get zone unitary system in thermal zone equipment
- unitary_sys=nil
+ unitary_sys = nil
# thermal_zone = air_loop_hvac.thermalZones[0]
thermal_zone.equipment.each do |zone_equipment|
# skip zone equipment that's not unitary HVAC system
next unless zone_equipment.to_AirLoopHVACUnitarySystem.is_initialized
+
unitary_sys = zone_equipment.to_AirLoopHVACUnitarySystem.get
end
# get design airflow of zone unitary system object
- dsn_clg_airflow=nil
- dsn_htg_airflow=nil
+ dsn_clg_airflow = nil
+ dsn_htg_airflow = nil
# get cooling airflow
if unitary_sys.autosizedSupplyAirFlowRateDuringCoolingOperation.is_initialized
- dsn_clg_airflow=unitary_sys.autosizedSupplyAirFlowRateDuringCoolingOperation.get
+ dsn_clg_airflow = unitary_sys.autosizedSupplyAirFlowRateDuringCoolingOperation.get
elsif unitary_sys.supplyAirFlowRateDuringCoolingOperation.is_initialized
- dsn_clg_airflow=unitary_sys.supplyAirFlowRateDuringCoolingOperation.get
+ dsn_clg_airflow = unitary_sys.supplyAirFlowRateDuringCoolingOperation.get
else
runner.registerError("Unitary system cooling airflow rates not found for #{unitary_sys}")
return false
end
# get heating airflow
if unitary_sys.autosizedSupplyAirFlowRateDuringHeatingOperation.is_initialized
- dsn_htg_airflow=unitary_sys.autosizedSupplyAirFlowRateDuringHeatingOperation.get
+ dsn_htg_airflow = unitary_sys.autosizedSupplyAirFlowRateDuringHeatingOperation.get
elsif unitary_sys.supplyAirFlowRateDuringHeatingOperation.is_initialized
- dsn_htg_airflow=unitary_sys.supplyAirFlowRateDuringHeatingOperation.get
+ dsn_htg_airflow = unitary_sys.supplyAirFlowRateDuringHeatingOperation.get
else
runner.registerError("Unitary system heating airflow rates not found for #{unitary_sys}")
return false
end
# design airflow will be max of heating and cooling airflows
- dsn_airflow = [dsn_clg_airflow, dsn_htg_airflow].max()
+ dsn_airflow = [dsn_clg_airflow, dsn_htg_airflow].max
# get design cooling and heating load of zone unitary system; this will set multispeed coil stage parameters
- dsn_clg_load=nil
- dsn_htg_load=nil
+ dsn_clg_load = nil
+ dsn_htg_load = nil
# for cooling
- dummy_clg_coil= unitary_sys.coolingCoil.get.to_CoilCoolingDXSingleSpeed.get
+ dummy_clg_coil = unitary_sys.coolingCoil.get.to_CoilCoolingDXSingleSpeed.get
if dummy_clg_coil.autosizedRatedTotalCoolingCapacity.is_initialized
dsn_clg_load = dummy_clg_coil.autosizedRatedTotalCoolingCapacity.get
model.removeObject(dummy_clg_coil.handle)
@@ -696,7 +699,7 @@ def run(model, runner, user_arguments)
return false
end
# for heating
- dummy_htg_coil= unitary_sys.heatingCoil.get.to_CoilHeatingElectric.get
+ dummy_htg_coil = unitary_sys.heatingCoil.get.to_CoilHeatingElectric.get
if dummy_htg_coil.autosizedNominalCapacity.is_initialized
dsn_htg_load = dummy_htg_coil.autosizedNominalCapacity.get
model.removeObject(dummy_htg_coil.handle)
@@ -721,13 +724,14 @@ def run(model, runner, user_arguments)
day_type = dd.dayType
# add design day drybulb temperature if winter design day
next unless day_type == 'WinterDesignDay'
+
li_htg_dsgn_day_temps << dd.maximumDryBulbTemperature
end
# get coldest design day temp for manual sizing
- wntr_design_day_temp_c = li_htg_dsgn_day_temps.min()
+ wntr_design_day_temp_c = li_htg_dsgn_day_temps.min
# set heat pump sizing temp based on user-input value and design day
- hp_sizing_temp_c=nil
+ hp_sizing_temp_c = nil
if wntr_design_day_temp_c < min_comp_lockout_temp
hp_sizing_temp_c = min_comp_lockout_temp
runner.registerInfo("For heat pump sizing, heating design day temperature is #{OpenStudio.convert(wntr_design_day_temp_c, 'C', 'F').get.round(0)}F while the minimum compressor lockout temperature of the heat pumps is #{OpenStudio.convert(min_comp_lockout_temp, 'C', 'F').get.round(0)}F. Since the design day temperature is lower than the compressor lockout temperature, the compressor lockout temperature will be used for sizing the heat pump. Backup electric resistance heating will accomodate the remaining load.")
@@ -744,7 +748,7 @@ def run(model, runner, user_arguments)
# determine heating load curve; y=mx+b
# assumes 0 load at 60F (15.556 C)
- htg_load_slope = (0-dsn_htg_load) / (15.5556-wntr_design_day_temp_c)
+ htg_load_slope = (0 - dsn_htg_load) / (15.5556 - wntr_design_day_temp_c)
htg_load_intercept = dsn_htg_load - (htg_load_slope * wntr_design_day_temp_c)
# calculate heat pump design load, derate factors, and required rated capacities (at stage 4) for different OA temperatures; assumes 75F interior temp (23.8889C)
@@ -752,32 +756,32 @@ def run(model, runner, user_arguments)
# design - temperature determined by design days in specified weather file
oa_temp_c = wntr_design_day_temp_c
dns_htg_load_at_dsn_temp = dsn_htg_load
- hp_derate_factor_at_dsn = 1.09830653306452 + -0.010386676170938*ia_temp_c + 0*ia_temp_c**2 + 0.0145161290322581*oa_temp_c + 0*oa_temp_c**2 + 0*ia_temp_c*oa_temp_c
+ hp_derate_factor_at_dsn = 1.09830653306452 + (-0.010386676170938 * ia_temp_c) + (0 * (ia_temp_c**2)) + (0.0145161290322581 * oa_temp_c) + (0 * (oa_temp_c**2)) + (0 * ia_temp_c * oa_temp_c)
req_rated_hp_cap_at_47f_to_meet_load_at_dsn = dns_htg_load_at_dsn_temp / hp_derate_factor_at_dsn
# 0F
oa_temp_c = -17.7778
- dns_htg_load_at_0f = htg_load_slope*(-17.7778) + htg_load_intercept
- hp_derate_factor_at_0f = 1.09830653306452 + -0.010386676170938*ia_temp_c + 0*ia_temp_c**2 + 0.0145161290322581*oa_temp_c + 0*oa_temp_c**2 + 0*ia_temp_c*oa_temp_c
+ dns_htg_load_at_0f = (htg_load_slope * -17.7778) + htg_load_intercept
+ hp_derate_factor_at_0f = 1.09830653306452 + (-0.010386676170938 * ia_temp_c) + (0 * (ia_temp_c**2)) + (0.0145161290322581 * oa_temp_c) + (0 * (oa_temp_c**2)) + (0 * ia_temp_c * oa_temp_c)
req_rated_hp_cap_at_47f_to_meet_load_at_0f = dns_htg_load_at_0f / hp_derate_factor_at_0f
# 17F
oa_temp_c = -8.33333
- dns_htg_load_at_17f = htg_load_slope*(-8.33333) + htg_load_intercept
- hp_derate_factor_at_17f = 1.09830653306452 + -0.010386676170938*ia_temp_c + 0*ia_temp_c**2 + 0.0145161290322581*oa_temp_c + 0*oa_temp_c**2 + 0*ia_temp_c*oa_temp_c
+ dns_htg_load_at_17f = (htg_load_slope * -8.33333) + htg_load_intercept
+ hp_derate_factor_at_17f = 1.09830653306452 + (-0.010386676170938 * ia_temp_c) + (0 * (ia_temp_c**2)) + (0.0145161290322581 * oa_temp_c) + (0 * (oa_temp_c**2)) + (0 * ia_temp_c * oa_temp_c)
req_rated_hp_cap_at_47f_to_meet_load_at_17f = dns_htg_load_at_17f / hp_derate_factor_at_17f
# 47F - note that this is rated conditions, so "derate" factor is either 1 from the curve, or will be normlized to 1 by E+ during simulation
oa_temp_c = 8.33333
- dns_htg_load_at_47f = htg_load_slope*(-8.33333) + htg_load_intercept
+ dns_htg_load_at_47f = (htg_load_slope * -8.33333) + htg_load_intercept
hp_derate_factor_at_47f = 1
req_rated_hp_cap_at_47f_to_meet_load_at_47f = dns_htg_load_at_47f / hp_derate_factor_at_47f
# user-specified design
oa_temp_c = hp_sizing_temp_c
- dns_htg_load_at_user_dsn_temp = htg_load_slope*hp_sizing_temp_c + htg_load_intercept
- hp_derate_factor_at_user_dsn = 1.09830653306452 + -0.010386676170938*ia_temp_c + 0*ia_temp_c**2 + 0.0145161290322581*oa_temp_c + 0*oa_temp_c**2 + 0*ia_temp_c*oa_temp_c
+ dns_htg_load_at_user_dsn_temp = (htg_load_slope * hp_sizing_temp_c) + htg_load_intercept
+ hp_derate_factor_at_user_dsn = 1.09830653306452 + (-0.010386676170938 * ia_temp_c) + (0 * (ia_temp_c**2)) + (0.0145161290322581 * oa_temp_c) + (0 * (oa_temp_c**2)) + (0 * ia_temp_c * oa_temp_c)
req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn = dns_htg_load_at_user_dsn_temp / hp_derate_factor_at_user_dsn
# determine heat pump system sizing based on user-specified sizing temperature and user-specified maximum upsizing limits
# get maximum cooling capacity with user-specified upsizing
- max_cool_cap_w_upsize = dsn_clg_load * (performance_oversizing_factor+1)
+ max_cool_cap_w_upsize = dsn_clg_load * (performance_oversizing_factor + 1)
max_heat_cap_w_upsize = max_cool_cap_w_upsize
# set derate factor to 0 if less than -13F (-25 C)
@@ -786,11 +790,11 @@ def run(model, runner, user_arguments)
end
# cooling capacity
- cool_cap_oversize_pct_actual=nil
+ cool_cap_oversize_pct_actual = nil
if req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn < dsn_clg_load
cool_cap_oversize_pct_actual = 0
else
- cool_cap_oversize_pct_actual = (((dsn_clg_load - req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn) / dsn_clg_load).abs() * 100).round(2)
+ cool_cap_oversize_pct_actual = (((dsn_clg_load - req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn) / dsn_clg_load).abs * 100).round(2)
end
# set heat pump heating and cooling capacities based on design loads, user-specified backup heating, and design oversizing.
@@ -806,18 +810,18 @@ def run(model, runner, user_arguments)
runner.registerInfo("For air loop #{thermal_zone.name}:
Design Heating Load: #{OpenStudio.convert(dsn_htg_load, 'W', 'ton').get.round(1)} tons
Design Cooling Load: #{OpenStudio.convert(dsn_clg_load, 'W', 'ton').get.round(1)} tons
- Heating to Cooling Load Ratio: #{(dsn_htg_load/dsn_clg_load).round(2)}
+ Heating to Cooling Load Ratio: #{(dsn_htg_load / dsn_clg_load).round(2)}
Weather File Design Day Temperature: #{OpenStudio.convert(wntr_design_day_temp_c, 'C', 'F').get.round(0)}F
Design Sizing Temperature Accounting for -15F Compressor Lockout: #{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F
Heat Pump Derate Factor at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F): #{hp_derate_factor_at_user_dsn.round(2)}
Heat Pump Capacity at Rated Condition (47F) Required to Meet Load at Design Sizing Temperature: #{OpenStudio.convert(req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn, 'W', 'ton').get.round(1)} tons
Sizing % increase required to Meet Load at Design Sizing Temperature vs. Cooling Sizing Requirement: #{cool_cap_oversize_pct_actual}%
- User-Defined Maximum Heat Pump Oversizing Limit: #{performance_oversizing_factor*100}%
+ User-Defined Maximum Heat Pump Oversizing Limit: #{performance_oversizing_factor * 100}%
Applied Heat Pump Sizing at Rated Conditions (47F): #{OpenStudio.convert(dx_rated_htg_cap_applied, 'W', 'ton').get.round(1)} tons
- % of Design Heating Load Met at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F) with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn).round(2)*100}%
- % of Design Heating Load Met at 0F with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_0f).round(1)*100}%
- % of Design Heating Load Met at 17F with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_17f).round(1)*100}%
- % of Design Heating Load Met at 47F with Applied Sizing: #{((dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_47f)*100).round(1)}%
+ % of Design Heating Load Met at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F) with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn).round(2) * 100}%
+ % of Design Heating Load Met at 0F with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_0f).round(1) * 100}%
+ % of Design Heating Load Met at 17F with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_17f).round(1) * 100}%
+ % of Design Heating Load Met at 47F with Applied Sizing: #{((dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_47f) * 100).round(1)}%
")
# If required heat pump size for heating is greater than design cooling load, but less than user-defined oversizing limit, size to required heating load
elsif req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn <= max_heat_cap_w_upsize
@@ -825,24 +829,24 @@ def run(model, runner, user_arguments)
dx_rated_htg_cap_applied = req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn
# set cooling capacity to appropriate ratio based on heating capacity needs
cool_cap = req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn
- cool_cap_oversize_pct_actual = (((dsn_clg_load-cool_cap) / dsn_clg_load).abs() * 100).round(2)
+ cool_cap_oversize_pct_actual = (((dsn_clg_load - cool_cap) / dsn_clg_load).abs * 100).round(2)
dx_rated_clg_cap_applied = cool_cap
# print register
runner.registerInfo("For air loop #{thermal_zone.name}:
Design Heating Load: #{OpenStudio.convert(dsn_htg_load, 'W', 'ton').get.round(1)} tons
Design Cooling Load: #{OpenStudio.convert(dsn_clg_load, 'W', 'ton').get.round(1)} tons
- Heating to Cooling Load Ratio: #{(dsn_htg_load/dsn_clg_load).round(2)}
+ Heating to Cooling Load Ratio: #{(dsn_htg_load / dsn_clg_load).round(2)}
Weather File Design Day Temperature: #{OpenStudio.convert(wntr_design_day_temp_c, 'C', 'F').get.round(0)}F
Design Sizing Temperature Accounting for -15F Compressor Lockout: #{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F
Heat Pump Derate Factor at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F): #{hp_derate_factor_at_user_dsn.round(2)}
Heat Pump Capacity at Rated Condition (47F) Required to Meet Load at Design Sizing Temperature: #{OpenStudio.convert(req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn, 'W', 'ton').get.round(1)} tons
Sizing % increase required to Meet Load at Design Sizing Temperature vs. Cooling Sizing Requirement: #{cool_cap_oversize_pct_actual}%
- User-Defined Maximum Heat Pump Oversizing Limit: #{performance_oversizing_factor*100}%
+ User-Defined Maximum Heat Pump Oversizing Limit: #{performance_oversizing_factor * 100}%
Applied Heat Pump Sizing at Rated Conditions (47F): #{OpenStudio.convert(dx_rated_htg_cap_applied, 'W', 'ton').get.round(1)} tons
- % of Design Heating Load Met at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F) with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn).round(2)*100}%
- % of Design Heating Load Met at 0F with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_0f).round(1)*100}%
- % of Design Heating Load Met at 17F with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_17f).round(1)*100}%
- % of Design Heating Load Met at 47F with Applied Sizing: #{((dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_47f)*100).round(1)}%
+ % of Design Heating Load Met at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F) with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn).round(2) * 100}%
+ % of Design Heating Load Met at 0F with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_0f).round(1) * 100}%
+ % of Design Heating Load Met at 17F with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_17f).round(1) * 100}%
+ % of Design Heating Load Met at 47F with Applied Sizing: #{((dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_47f) * 100).round(1)}%
")
else
# set rated heating capacity to maximum allowable based on cooling capacity maximum limit
@@ -858,18 +862,18 @@ def run(model, runner, user_arguments)
runner.registerInfo("For air loop #{thermal_zone.name}:
Design Heating Load: #{OpenStudio.convert(dsn_htg_load, 'W', 'ton').get.round(1)} tons
Design Cooling Load: #{OpenStudio.convert(dsn_clg_load, 'W', 'ton').get.round(1)} tons
- Heating to Cooling Load Ratio: #{(dsn_htg_load/dsn_clg_load).round(2)}
+ Heating to Cooling Load Ratio: #{(dsn_htg_load / dsn_clg_load).round(2)}
Weather File Design Day Temperature: #{OpenStudio.convert(wntr_design_day_temp_c, 'C', 'F').get.round(0)}F
Design Sizing Temperature Accounting for -15F Compressor Lockout: #{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F
Heat Pump Derate Factor at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F): #{hp_derate_factor_at_user_dsn.round(2)}
Heat Pump Capacity at Rated Condition (47F) Required to Meet Load at Design Sizing Temperature: #{OpenStudio.convert(req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn, 'W', 'ton').get.round(1)} tons
Sizing % increase required to Meet Load at Design Sizing Temperature vs. Cooling Sizing Requirement: #{cool_cap_oversize_pct_actual}%
- User-Defined Maximum Heat Pump Oversizing Limit: #{performance_oversizing_factor*100}%
+ User-Defined Maximum Heat Pump Oversizing Limit: #{performance_oversizing_factor * 100}%
Applied Heat Pump Sizing at Rated Conditions (47F): #{OpenStudio.convert(dx_rated_htg_cap_applied, 'W', 'ton').get.round(1)} tons
- % of Design Heating Load Met at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F) with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn).round(2)*100}%
- % of Design Heating Load Met at 0F with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_0f).round(1)*100}%
- % of Design Heating Load Met at 17F with Applied Sizing: #{(dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_17f).round(1)*100}%
- % of Design Heating Load Met at 47F with Applied Sizing: #{((dx_rated_htg_cap_applied/req_rated_hp_cap_at_47f_to_meet_load_at_47f)*100).round(1)}%
+ % of Design Heating Load Met at Design Sizing Temperature (#{OpenStudio.convert(hp_sizing_temp_c, 'C', 'F').get.round(0)}F) with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_user_dsn_to_meet_load_at_user_dsn).round(2) * 100}%
+ % of Design Heating Load Met at 0F with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_0f).round(1) * 100}%
+ % of Design Heating Load Met at 17F with Applied Sizing: #{(dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_17f).round(1) * 100}%
+ % of Design Heating Load Met at 47F with Applied Sizing: #{((dx_rated_htg_cap_applied / req_rated_hp_cap_at_47f_to_meet_load_at_47f) * 100).round(1)}%
")
end
@@ -1050,7 +1054,7 @@ def run(model, runner, user_arguments)
new_dx_cooling_coil_speed1.setTotalCoolingCapacityFunctionofTemperatureCurve(cool_cap_ft1)
new_dx_cooling_coil_speed1.setTotalCoolingCapacityFunctionofFlowFractionCurve(cool_cap_fff_all_stages)
new_dx_cooling_coil_speed1.setEnergyInputRatioFunctionofTemperatureCurve(cool_eir_ft1)
- new_dx_cooling_coil_speed1.setEnergyInputRatioFunctionofFlowFractionCurve (cool_eir_fff_all_stages)
+ new_dx_cooling_coil_speed1.setEnergyInputRatioFunctionofFlowFractionCurve(cool_eir_fff_all_stages)
new_dx_cooling_coil_speed1.setPartLoadFractionCorrelationCurve(cool_plf_fplr_all_stages)
new_dx_cooling_coil_speed1.setNominalTimeforCondensateRemovaltoBegin(1000)
new_dx_cooling_coil_speed1.setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(1.5)
@@ -1070,7 +1074,7 @@ def run(model, runner, user_arguments)
new_dx_cooling_coil_speed2.setTotalCoolingCapacityFunctionofTemperatureCurve(cool_cap_ft2)
new_dx_cooling_coil_speed2.setTotalCoolingCapacityFunctionofFlowFractionCurve(cool_cap_fff_all_stages)
new_dx_cooling_coil_speed2.setEnergyInputRatioFunctionofTemperatureCurve(cool_eir_ft2)
- new_dx_cooling_coil_speed2.setEnergyInputRatioFunctionofFlowFractionCurve (cool_eir_fff_all_stages)
+ new_dx_cooling_coil_speed2.setEnergyInputRatioFunctionofFlowFractionCurve(cool_eir_fff_all_stages)
new_dx_cooling_coil_speed2.setPartLoadFractionCorrelationCurve(cool_plf_fplr_all_stages)
new_dx_cooling_coil_speed2.setNominalTimeforCondensateRemovaltoBegin(1000)
new_dx_cooling_coil_speed2.setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(1.5)
@@ -1090,7 +1094,7 @@ def run(model, runner, user_arguments)
new_dx_cooling_coil_speed3.setTotalCoolingCapacityFunctionofTemperatureCurve(cool_cap_ft3)
new_dx_cooling_coil_speed3.setTotalCoolingCapacityFunctionofFlowFractionCurve(cool_cap_fff_all_stages)
new_dx_cooling_coil_speed3.setEnergyInputRatioFunctionofTemperatureCurve(cool_eir_ft3)
- new_dx_cooling_coil_speed3.setEnergyInputRatioFunctionofFlowFractionCurve (cool_eir_fff_all_stages)
+ new_dx_cooling_coil_speed3.setEnergyInputRatioFunctionofFlowFractionCurve(cool_eir_fff_all_stages)
new_dx_cooling_coil_speed3.setPartLoadFractionCorrelationCurve(cool_plf_fplr_all_stages)
new_dx_cooling_coil_speed3.setNominalTimeforCondensateRemovaltoBegin(1000)
new_dx_cooling_coil_speed3.setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(1.5)
@@ -1109,7 +1113,7 @@ def run(model, runner, user_arguments)
new_dx_cooling_coil_speed4.setTotalCoolingCapacityFunctionofTemperatureCurve(cool_cap_ft4)
new_dx_cooling_coil_speed4.setTotalCoolingCapacityFunctionofFlowFractionCurve(cool_cap_fff_all_stages)
new_dx_cooling_coil_speed4.setEnergyInputRatioFunctionofTemperatureCurve(cool_eir_ft4)
- new_dx_cooling_coil_speed4.setEnergyInputRatioFunctionofFlowFractionCurve (cool_eir_fff_all_stages)
+ new_dx_cooling_coil_speed4.setEnergyInputRatioFunctionofFlowFractionCurve(cool_eir_fff_all_stages)
new_dx_cooling_coil_speed4.setPartLoadFractionCorrelationCurve(cool_plf_fplr_all_stages)
new_dx_cooling_coil_speed4.setNominalTimeforCondensateRemovaltoBegin(1000)
new_dx_cooling_coil_speed4.setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(1.5)
@@ -1300,7 +1304,7 @@ def run(model, runner, user_arguments)
new_dx_heating_coil_speed1.setHeatingCapacityFunctionofTemperatureCurve(heat_cap_ft1)
new_dx_heating_coil_speed1.setHeatingCapacityFunctionofFlowFractionCurve(heat_cap_fff_all_stages)
new_dx_heating_coil_speed1.setEnergyInputRatioFunctionofTemperatureCurve(heat_eir_ft1)
- new_dx_heating_coil_speed1.setEnergyInputRatioFunctionofFlowFractionCurve (heat_eir_fff_all_stages)
+ new_dx_heating_coil_speed1.setEnergyInputRatioFunctionofFlowFractionCurve(heat_eir_fff_all_stages)
new_dx_heating_coil_speed1.setPartLoadFractionCorrelationCurve(heat_plf_fplr_all_stages)
new_dx_heating_coil.addStage(new_dx_heating_coil_speed1)
# create stage 2
@@ -1312,7 +1316,7 @@ def run(model, runner, user_arguments)
new_dx_heating_coil_speed2.setHeatingCapacityFunctionofTemperatureCurve(heat_cap_ft2)
new_dx_heating_coil_speed2.setHeatingCapacityFunctionofFlowFractionCurve(heat_cap_fff_all_stages)
new_dx_heating_coil_speed2.setEnergyInputRatioFunctionofTemperatureCurve(heat_eir_ft2)
- new_dx_heating_coil_speed2.setEnergyInputRatioFunctionofFlowFractionCurve (heat_eir_fff_all_stages)
+ new_dx_heating_coil_speed2.setEnergyInputRatioFunctionofFlowFractionCurve(heat_eir_fff_all_stages)
new_dx_heating_coil_speed2.setPartLoadFractionCorrelationCurve(heat_plf_fplr_all_stages)
new_dx_heating_coil.addStage(new_dx_heating_coil_speed2)
# create stage 3
@@ -1324,7 +1328,7 @@ def run(model, runner, user_arguments)
new_dx_heating_coil_speed3.setHeatingCapacityFunctionofTemperatureCurve(heat_cap_ft3)
new_dx_heating_coil_speed3.setHeatingCapacityFunctionofFlowFractionCurve(heat_cap_fff_all_stages)
new_dx_heating_coil_speed3.setEnergyInputRatioFunctionofTemperatureCurve(heat_eir_ft3)
- new_dx_heating_coil_speed3.setEnergyInputRatioFunctionofFlowFractionCurve (heat_eir_fff_all_stages)
+ new_dx_heating_coil_speed3.setEnergyInputRatioFunctionofFlowFractionCurve(heat_eir_fff_all_stages)
new_dx_heating_coil_speed3.setPartLoadFractionCorrelationCurve(heat_plf_fplr_all_stages)
new_dx_heating_coil.addStage(new_dx_heating_coil_speed3)
# create stage 4
@@ -1336,7 +1340,7 @@ def run(model, runner, user_arguments)
new_dx_heating_coil_speed4.setHeatingCapacityFunctionofTemperatureCurve(heat_cap_ft4)
new_dx_heating_coil_speed4.setHeatingCapacityFunctionofFlowFractionCurve(heat_cap_fff_all_stages)
new_dx_heating_coil_speed4.setEnergyInputRatioFunctionofTemperatureCurve(heat_eir_ft4)
- new_dx_heating_coil_speed4.setEnergyInputRatioFunctionofFlowFractionCurve (heat_eir_fff_all_stages)
+ new_dx_heating_coil_speed4.setEnergyInputRatioFunctionofFlowFractionCurve(heat_eir_fff_all_stages)
new_dx_heating_coil_speed4.setPartLoadFractionCorrelationCurve(heat_plf_fplr_all_stages)
new_dx_heating_coil.addStage(new_dx_heating_coil_speed4)
####################################### End Heating Performance Curves
@@ -1350,7 +1354,7 @@ def run(model, runner, user_arguments)
supp_htg_coil.setEfficiency(1)
# set other features
- unitary_sys.setDXHeatingCoilSizingRatio(1+performance_oversizing_factor)
+ unitary_sys.setDXHeatingCoilSizingRatio(1 + performance_oversizing_factor)
if model.version < OpenStudio::VersionString.new('3.7.0')
# set cooling design flow rate
@@ -1363,7 +1367,7 @@ def run(model, runner, user_arguments)
unitary_sys.setSupplyAirFlowRateMethodWhenNoCoolingorHeatingisRequired('SupplyAirFlowRate')
unitary_sys.setSupplyAirFlowRateWhenNoCoolingorHeatingisRequired(airflow_stage1)
else
- # set cooling design flow rate
+ # set cooling design flow rate
unitary_sys.autosizeSupplyAirFlowRateDuringCoolingOperation
unitary_sys.setSupplyAirFlowRateDuringCoolingOperation(airflow_stage4)
# set heating design flow rate
diff --git a/resources/measures/upgrade_hvac_doas_hp_minisplits/tests/hvac_doas_hp_minisplits_test.rb b/resources/measures/upgrade_hvac_doas_hp_minisplits/tests/hvac_doas_hp_minisplits_test.rb
index 4a88cd4f6..3e8bcd7f0 100644
--- a/resources/measures/upgrade_hvac_doas_hp_minisplits/tests/hvac_doas_hp_minisplits_test.rb
+++ b/resources/measures/upgrade_hvac_doas_hp_minisplits/tests/hvac_doas_hp_minisplits_test.rb
@@ -25,103 +25,154 @@ def test_number_of_arguments_and_argument_names
# get arguments and test that they are what we are expecting
arguments = measure.arguments(model)
- assert_equal(1, arguments.size)
- assert_equal('space_name', arguments[0].name)
+ assert_equal(3, arguments.size)
end
- def test_bad_argument_values
- # create an instance of the measure
- measure = HvacDoasHpMinisplits.new
+ # return file paths to test models in test directory
+ def models_for_tests
+ paths = Dir.glob(File.join(File.dirname(__FILE__), '../../../tests/models/*.osm'))
+ paths = paths.map { |path| File.expand_path(path) }
+ return paths
+ end
- # create runner with empty OSW
- osw = OpenStudio::WorkflowJSON.new
- runner = OpenStudio::Measure::OSRunner.new(osw)
+ # return file paths to epw files in test directory
+ def epws_for_tests
+ paths = Dir.glob(File.join(File.dirname(__FILE__), '../../../tests/weather/*.epw'))
+ paths = paths.map { |path| File.expand_path(path) }
+ return paths
+ end
- # make an empty model
- model = OpenStudio::Model::Model.new
+ def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
+ translator = OpenStudio::OSVersion::VersionTranslator.new
+ model = translator.loadModel(OpenStudio::Path.new(osm_path))
+ assert(!model.empty?)
+ model = model.get
+ return model
+ end
- # get arguments
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
-
- # create hash of argument values
- args_hash = {}
- args_hash['space_name'] = ''
-
- # populate argument with specified hash value if specified
- arguments.each do |arg|
- temp_arg_var = arg.clone
- if args_hash.key?(arg.name)
- assert(temp_arg_var.setValue(args_hash[arg.name]))
- end
- argument_map[arg.name] = temp_arg_var
+ def run_dir(test_name)
+ # always generate test output in specially named 'output' directory so result files are not made part of the measure
+ path = "#{File.dirname(__FILE__)}/output/#{test_name}"
+ unless File.directory?(path)
+ FileUtils.mkdir_p(path)
end
+ return path
+ end
- # run the measure
- measure.run(model, runner, argument_map)
- result = runner.result
+ def model_output_path(test_name)
+ return "#{run_dir(test_name)}/#{test_name}.osm"
+ end
- # show the output
- show_output(result)
+ def sql_path(test_name)
+ return "#{run_dir(test_name)}/run/eplusout.sql"
+ end
- # assert that it ran correctly
- assert_equal('Fail', result.value.valueName)
+ def report_path(test_name)
+ return "#{run_dir(test_name)}/reports/eplustbl.html"
end
- def test_good_argument_values
- # create an instance of the measure
- measure = HvacDoasHpMinisplits.new
+ # applies the measure and then runs the model
+ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path, run_model: false)
+ assert(File.exist?(osm_path))
+ assert(File.exist?(epw_path))
- # create runner with empty OSW
- osw = OpenStudio::WorkflowJSON.new
- runner = OpenStudio::Measure::OSRunner.new(osw)
+ # create run directory if it does not exist
+ FileUtils.mkdir_p(run_dir(test_name))
+ assert(File.exist?(run_dir(test_name)))
- # load the test model
- translator = OpenStudio::OSVersion::VersionTranslator.new
- path = "#{File.dirname(__FILE__)}/example_model.osm"
- model = translator.loadModel(path)
- assert(!model.empty?)
- model = model.get
+ # change into run directory for tests
+ start_dir = Dir.pwd
+ Dir.chdir run_dir(test_name)
- # store the number of spaces in the seed model
- num_spaces_seed = model.getSpaces.size
+ # remove prior runs if they exist
+ FileUtils.rm_f(model_output_path(test_name))
+ FileUtils.rm_f(report_path(test_name))
- # get arguments
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
-
- # create hash of argument values.
- # If the argument has a default that you want to use, you don't need it in the hash
- args_hash = {}
- args_hash['space_name'] = 'New Space'
- # using defaults values from measure.rb for other arguments
-
- # populate argument with specified hash value if specified
- arguments.each do |arg|
- temp_arg_var = arg.clone
- if args_hash.key?(arg.name)
- assert(temp_arg_var.setValue(args_hash[arg.name]))
- end
- argument_map[arg.name] = temp_arg_var
- end
+ # copy the osm and epw to the test directory
+ # new_osm_path = File.expand_path("#{Dir.pwd}/#{File.basename(osm_path)}")
+ new_osm_path = "#{Dir.pwd}/#{File.basename(osm_path)}"
+ FileUtils.cp(osm_path, new_osm_path)
+ new_epw_path = "#{Dir.pwd}/#{File.basename(epw_path)}"
+ FileUtils.cp(epw_path, new_epw_path)
+ # create an instance of a runner
+ runner = OpenStudio::Measure::OSRunner.new(OpenStudio::WorkflowJSON.new)
+
+ # load the test model
+ model = load_model(new_osm_path)
+
+ # set model weather file
+ epw_file = OpenStudio::EpwFile.new(OpenStudio::Path.new(new_epw_path))
+ OpenStudio::Model::WeatherFile.setWeatherFile(model, epw_file)
+ assert(model.weatherFile.is_initialized)
# run the measure
+ puts "\nAPPLYING MEASURE..."
measure.run(model, runner, argument_map)
result = runner.result
+ result_success = result.value.valueName == 'Success'
# show the output
show_output(result)
- # assert that it ran correctly
- assert_equal('Success', result.value.valueName)
- assert(result.info.size == 1)
- assert(result.warnings.empty?)
+ # save model
+ model.save(model_output_path(test_name), true)
- # check that there is now 1 space
- assert_equal(1, model.getSpaces.size - num_spaces_seed)
+ if run_model && result_success
+ puts "\nRUNNING MODEL..."
- # save the model to test output directory
- output_file_path = "#{File.dirname(__FILE__)}//output/test_output.osm"
- model.save(output_file_path, true)
+ std = Standard.build('ComStock DEER 2020')
+ std.model_run_simulation_and_log_errors(model, run_dir(test_name))
+
+ # check that the model ran successfully
+ assert(File.exist?(sql_path(test_name)))
+ end
+
+ # change back directory
+ Dir.chdir(start_dir)
+
+ return result
+ end
+
+ # create an array of hashes with model name, weather, and expected result
+ def models_to_test
+ test_sets = []
+ test_sets << { model: 'LargeOffice_VAV_chiller_boiler', weather: 'VA_MANASSAS_724036_12', result: 'NA' }
+ test_sets << { model: 'Quick_Service_Restaurant_CA', weather: 'CA_LOS-ANGELES-DOWNTOWN-USC_722874S_16', result: 'Success' }
+ return test_sets
+ end
+
+ def test_models
+ test_name = 'test_models'
+ puts "\n######\nTEST:#{test_name}\n######\n"
+
+ models_to_test.each do |set|
+ instance_test_name = set[:model]
+ puts "instance test name: #{instance_test_name}"
+ osm_path = models_for_tests.select { |x| set[:model] == File.basename(x, '.osm') }
+ epw_path = epws_for_tests.select { |x| set[:weather] == File.basename(x, '.epw') }
+ assert(!osm_path.empty?)
+ assert(!epw_path.empty?)
+ osm_path = osm_path[0]
+ epw_path = epw_path[0]
+
+ # create an instance of the measure
+ measure = HvacDoasHpMinisplits.new
+
+ # load the model; only used here for populating arguments
+ model = load_model(osm_path)
+
+ # set arguments here; will vary by measure
+ arguments = measure.arguments(model)
+ argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
+
+ # apply the measure to the model and optionally run the model
+ result = apply_measure_and_run(instance_test_name, measure, argument_map, osm_path, epw_path, run_model: false)
+
+ # check the measure result; result values will equal Success, Fail, or Not Applicable
+ # also check the amount of warnings, info, and error messages
+ # use if or case statements to change expected assertion depending on model characteristics
+ assert_equal(set[:result].to_s, result.value.valueName.to_s)
+ end
end
end
diff --git a/resources/measures/upgrade_hvac_economizer/tests/hvac_economizer_test.rb b/resources/measures/upgrade_hvac_economizer/tests/hvac_economizer_test.rb
index c31b2f51c..01d161fb1 100644
--- a/resources/measures/upgrade_hvac_economizer/tests/hvac_economizer_test.rb
+++ b/resources/measures/upgrade_hvac_economizer/tests/hvac_economizer_test.rb
@@ -78,6 +78,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_electric_boiler/tests/elec_boiler_test.rb b/resources/measures/upgrade_hvac_electric_boiler/tests/elec_boiler_test.rb
index f8e79ac28..83034e2f9 100644
--- a/resources/measures/upgrade_hvac_electric_boiler/tests/elec_boiler_test.rb
+++ b/resources/measures/upgrade_hvac_electric_boiler/tests/elec_boiler_test.rb
@@ -57,6 +57,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_enable_ideal_air_loads/tests/measure_test.rb b/resources/measures/upgrade_hvac_enable_ideal_air_loads/tests/measure_test.rb
index 6b3aaf618..8bdc5cc2a 100644
--- a/resources/measures/upgrade_hvac_enable_ideal_air_loads/tests/measure_test.rb
+++ b/resources/measures/upgrade_hvac_enable_ideal_air_loads/tests/measure_test.rb
@@ -60,6 +60,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_exhaust_air_energy_or_heat_recovery/tests/hvac_exhaust_air_energy_or_heat_recovery_test.rb b/resources/measures/upgrade_hvac_exhaust_air_energy_or_heat_recovery/tests/hvac_exhaust_air_energy_or_heat_recovery_test.rb
index d979ee39e..ae9aada87 100644
--- a/resources/measures/upgrade_hvac_exhaust_air_energy_or_heat_recovery/tests/hvac_exhaust_air_energy_or_heat_recovery_test.rb
+++ b/resources/measures/upgrade_hvac_exhaust_air_energy_or_heat_recovery/tests/hvac_exhaust_air_energy_or_heat_recovery_test.rb
@@ -82,6 +82,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -170,7 +171,7 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
# create an array of hashes with model name, weather, and expected result
def models_to_test
test_sets = []
- test_sets << { model: 'PSZ-AC_with_gas_coil_heat_3B', weather: 'CA_LOS-ANGELES-DOWNTOWN-USC_722874S_16', result: 'Success' }
+ test_sets << { model: '310_PSZ-AC with electric coil', weather: 'Nashville Metropoli TN', result: 'Success' }
return test_sets
end
@@ -200,6 +201,7 @@ def test_models
# apply the measure to the model and optionally run the model
result = apply_measure_and_run(instance_test_name, measure, argument_map, osm_path, epw_path, run_model: false)
+ puts result
# check the measure result; result values will equal Success, Fail, or Not Applicable
# also check the amount of warnings, info, and error messages
diff --git a/resources/measures/upgrade_hvac_fan_static_pressure_reset/measure.rb b/resources/measures/upgrade_hvac_fan_static_pressure_reset/measure.rb
index 9027a3069..9352b61a3 100644
--- a/resources/measures/upgrade_hvac_fan_static_pressure_reset/measure.rb
+++ b/resources/measures/upgrade_hvac_fan_static_pressure_reset/measure.rb
@@ -24,20 +24,13 @@ def modeler_description
def vav_terminals?(air_loop_hvac)
air_loop_hvac.thermalZones.each do |thermal_zone| # iterate thru thermal zones and modify zone-level terminal units
thermal_zone.equipment.each do |equip|
- if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized
+ if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVNoReheat.is_initialized ||
+ equip.to_AirTerminalDualDuctVAV.is_initialized ||
+ equip.to_AirTerminalDualDuctVAVOutdoorAir.is_initialized
return true
- elsif equip.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized
- return true
- elsif equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
- return true
- elsif equip.to_AirTerminalSingleDuctVAVNoReheat.is_initialized
- return true
- elsif equip.to_AirTerminalDualDuctVAV.is_initialized
- return true
- elsif equip.to_AirTerminalDualDuctVAVOutdoorAir.is_initialized
- return true
- else
- next
end
end
end
@@ -106,14 +99,14 @@ def run(model, runner, user_arguments)
next
end
# skip non-VAV systems
- next if !['VAV', 'PVAV'].any? { |word| air_loop_hvac.name.get.include?(word) } && !vav_terminals?(air_loop_hvac)
+ next if ['VAV', 'PVAV'].none? { |word| air_loop_hvac.name.get.include?(word) } && !vav_terminals?(air_loop_hvac)
overall_sel_air_loops << air_loop_hvac
end
# register na if no applicable air loops
- if overall_sel_air_loops.length == 0
+ if overall_sel_air_loops.empty?
runner.registerAsNotApplicable('No applicable air loops found in model')
return true
end
diff --git a/resources/measures/upgrade_hvac_fan_static_pressure_reset/measure.xml b/resources/measures/upgrade_hvac_fan_static_pressure_reset/measure.xml
index c62c25497..e32a9cbe7 100644
--- a/resources/measures/upgrade_hvac_fan_static_pressure_reset/measure.xml
+++ b/resources/measures/upgrade_hvac_fan_static_pressure_reset/measure.xml
@@ -3,8 +3,8 @@
3.1
fan_static_pressure_reset
62500b92-9756-4bba-8b8e-af2a97bf512f
- 2f9f466c-3c68-48f6-9568-0db3760000b1
- 2025-10-21T16:22:27Z
+ 05a359ef-a642-4981-8774-e498de4cad1b
+ 2026-01-13T17:22:58Z
B3180F38
FanStaticPressureReset
Fan Static Pressure Reset
@@ -53,7 +53,7 @@
LICENSE.md
md
license
- CD7F5672
+ 08D3D350
README.md
@@ -82,7 +82,7 @@
measure.rb
rb
script
- 9193ADBE
+ 1D494D0F
example_model.osm
@@ -94,7 +94,7 @@
fan_static_pressure_reset_test.rb
rb
test
- B24C2561
+ 2009D783
diff --git a/resources/measures/upgrade_hvac_fan_static_pressure_reset/tests/fan_static_pressure_reset_test.rb b/resources/measures/upgrade_hvac_fan_static_pressure_reset/tests/fan_static_pressure_reset_test.rb
index 5fa7b85eb..7c6673140 100644
--- a/resources/measures/upgrade_hvac_fan_static_pressure_reset/tests/fan_static_pressure_reset_test.rb
+++ b/resources/measures/upgrade_hvac_fan_static_pressure_reset/tests/fan_static_pressure_reset_test.rb
@@ -21,6 +21,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -61,7 +62,7 @@ def set_weather_and_apply_measure_and_run(test_name, measure, argument_map, osm_
ddy_path = "#{epw_path.gsub('.epw', '')}.ddy"
# create run directory if it does not exist
- FileUtils.mkdir_p(run_dir(test_name)) unless File.exist?(run_dir(test_name))
+ FileUtils.mkdir_p(run_dir(test_name))
assert(File.exist?(run_dir(test_name)))
# change into run directory for tests
@@ -69,8 +70,8 @@ def set_weather_and_apply_measure_and_run(test_name, measure, argument_map, osm_
Dir.chdir run_dir(test_name)
# remove prior runs if they exist
- FileUtils.rm(model_output_path(test_name)) if File.exist?(model_output_path(test_name))
- FileUtils.rm(report_path(test_name)) if File.exist?(report_path(test_name))
+ FileUtils.rm_f(model_output_path(test_name))
+ FileUtils.rm_f(report_path(test_name))
# copy the osm and epw to the test directory
new_osm_path = "#{run_dir(test_name)}/#{File.basename(osm_path)}"
@@ -119,7 +120,7 @@ def set_weather_and_apply_measure_and_run(test_name, measure, argument_map, osm_
end
# assert
- assert_equal(false, model.getDesignDays.size.zero?)
+ assert_equal(false, model.getDesignDays.empty?)
end
if apply
diff --git a/resources/measures/upgrade_hvac_hydronic_gshp/measure.rb b/resources/measures/upgrade_hvac_hydronic_gshp/measure.rb
index 3d4b67353..2bf3e5b21 100644
--- a/resources/measures/upgrade_hvac_hydronic_gshp/measure.rb
+++ b/resources/measures/upgrade_hvac_hydronic_gshp/measure.rb
@@ -160,17 +160,12 @@ def arguments(_model)
def vav_terminals?(air_loop_hvac)
air_loop_hvac.thermalZones.each do |thermal_zone| # iterate thru thermal zones and modify zone-level terminal units
thermal_zone.equipment.each do |equip|
- if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized
- return true
- elsif equip.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized
- return true
- elsif equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
- return true
- elsif equip.to_AirTerminalSingleDuctVAVNoReheat.is_initialized
- return true
- elsif equip.to_AirTerminalDualDuctVAV.is_initialized
- return true
- elsif equip.to_AirTerminalDualDuctVAVOutdoorAir.is_initialized
+ if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVReheat.is_initialized ||
+ equip.to_AirTerminalSingleDuctVAVNoReheat.is_initialized ||
+ equip.to_AirTerminalDualDuctVAV.is_initialized ||
+ equip.to_AirTerminalDualDuctVAVOutdoorAir.is_initialized
return true
end
end
diff --git a/resources/measures/upgrade_hvac_hydronic_gshp/tests/hydronic_gthp_test.rb b/resources/measures/upgrade_hvac_hydronic_gshp/tests/hydronic_gthp_test.rb
index 8681f748a..b7087a94e 100644
--- a/resources/measures/upgrade_hvac_hydronic_gshp/tests/hydronic_gthp_test.rb
+++ b/resources/measures/upgrade_hvac_hydronic_gshp/tests/hydronic_gthp_test.rb
@@ -54,7 +54,7 @@ def setup
Open3.capture3(command)
rescue StandardError
msg = 'GHEDesigner python package not found in this test environment, pip install GHEDesigner and retry'
- raise LoadError.new msg
+ raise LoadError, msg
end
end
@@ -71,6 +71,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_multispeed_minimum_flow/measure.rb b/resources/measures/upgrade_hvac_multispeed_minimum_flow/measure.rb
index 4004cf307..377b79fcf 100644
--- a/resources/measures/upgrade_hvac_multispeed_minimum_flow/measure.rb
+++ b/resources/measures/upgrade_hvac_multispeed_minimum_flow/measure.rb
@@ -63,14 +63,12 @@ def run(workspace, runner, user_arguments)
else
runner.registerAsNotApplicable("Multispeed performance object -- #{ms_perf_obj.name} -- does not specify speed 1 heating and cooling flow ratios, and therefore does not provide necessary information to set the no load flow ratio. No model changes will be made to this object.")
end
- end
- # check if any airflow ratio fields are blank; if so, replace with 0s.
- li_multispeed_perf.each do |ms_perf_obj|
+ # check if any airflow ratio fields are blank; if so, replace with 1s
# get object indices
num_fields = ms_perf_obj.numFields
fields_list = (0...(0 + (num_fields - 1)))
- # loop through indicies to replace any 0s
+ # loop through indicies to replace any blanks with 1s
fields_list.sort.each do |field|
# replace blanks after position 5 with 1s
next unless (field >= 5) && !ms_perf_obj.getDouble(field, false).is_initialized
diff --git a/resources/measures/upgrade_hvac_packaged_gshp/measure.rb b/resources/measures/upgrade_hvac_packaged_gshp/measure.rb
index 4ee92c196..2ae18e5d6 100644
--- a/resources/measures/upgrade_hvac_packaged_gshp/measure.rb
+++ b/resources/measures/upgrade_hvac_packaged_gshp/measure.rb
@@ -53,21 +53,6 @@ def name
'add_packaged_gshp'
end
- # human readable description
- def description
- 'Measure replaces existing packaged single-zone RTU system types with ground source heat pump RTUs.'
- end
-
- # human readable description of modeling approach
- def modeler_description
- 'Modeler has option to set backup heat source, prevelence of heat pump oversizing, heat pump oversizing limit, and addition of energy recovery. This measure will work on unitary PSZ systems as well as single-zone, constant air volume air loop PSZ systems.'
- end
-
- # Define the name and description that will appear in the OpenStudio application
- def name
- 'add_packaged_gshp'
- end
-
def description
'This measure replaces packaged single zone systems with a packaged water-to-air ground source heat pump system.'
end
@@ -299,9 +284,8 @@ def run(model, runner, user_arguments)
# dont delete diffusers from PSZs, these will be reused
next if equip.to_AirTerminalSingleDuctConstantVolumeNoReheat.is_initialized
- if equip.to_ZoneHVACBaseboardConvectiveElectric.is_initialized
- zones_to_skip << thermal_zone.name.get
- elsif equip.to_ZoneHVACUnitHeater.is_initialized
+ if equip.to_ZoneHVACBaseboardConvectiveElectric.is_initialized ||
+ equip.to_ZoneHVACUnitHeater.is_initialized
zones_to_skip << thermal_zone.name.get
else
equip_to_delete << equip
@@ -709,6 +693,7 @@ def run(model, runner, user_arguments)
next unless ['Unitary'].any? do |word|
obj_type.include?(word)
end
+
# TODO: There are more unitary systems types we are not including here
# get unitary system
@@ -750,7 +735,7 @@ def run(model, runner, user_arguments)
if supply_fan_avail_sched.to_ScheduleConstant.is_initialized
supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleConstant.get
elsif supply_fan_avail_sched.to_ScheduleRuleset.is_initialized
- supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleConstant.get
+ supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleRuleset.get
else
runner.registerError("Supply fan availability schedule type for #{supply_fan.name} not supported.")
return false
@@ -795,7 +780,7 @@ def run(model, runner, user_arguments)
if supply_fan_avail_sched.to_ScheduleConstant.is_initialized
supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleConstant.get
elsif supply_fan_avail_sched.to_ScheduleRuleset.is_initialized
- supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleConstant.get
+ supply_fan_avail_sched = supply_fan_avail_sched.to_ScheduleRuleset.get
else
runner.registerError("Supply fan availability schedule type for #{supply_fan.name} not supported.")
return false
@@ -852,9 +837,24 @@ def run(model, runner, user_arguments)
# loop through thermal zones and add
model.getThermalZones.each do |thermal_zone|
- # skip if zone has baseboards and should not get a GHP
- next if zones_to_skip.include? thermal_zone.name.get
- next if unconditioned_zones.include? thermal_zone.name.get
+ # skip if zone was unconditioned in baseline
+ if unconditioned_zones.include? thermal_zone.name.get
+ runner.registerInfo("Thermal zone #{thermal_zone} was unconditioned in the baseline, and will not receive a packaged GHP.")
+ next
+ end
+
+ # if zone has baseboards and should not get a GHP, add electric baseboard if needed
+ if zones_to_skip.include? thermal_zone.name.get
+ if thermal_zone.equipment.empty?
+ baseboard = OpenStudio::Model::ZoneHVACBaseboardConvectiveElectric.new(model)
+ baseboard.setName("#{thermal_zone.name} Electric Baseboard")
+ baseboard.setEfficiency(1.0)
+ baseboard.autosizeNominalCapacity
+ baseboard.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule)
+ baseboard.addToThermalZone(thermal_zone)
+ end
+ next
+ end
# set always on schedule; this will be used in other object definitions
always_on = model.alwaysOnDiscreteSchedule
@@ -953,7 +953,7 @@ def run(model, runner, user_arguments)
fan.setFanPowerMinimumFlowRateInputMethod('Fraction')
fan.setFanPowerMinimumFlowFraction(min_fan_flow_ratio) # need to add check for ventilation
# set fan curve coefficients
- std.fan_variable_volume_set_control_type(fan, 'Single Zone VAV Fan ')
+ std.fan_variable_volume_set_control_type(fan, 'Single Zone VAV Fan')
zone_data["#{thermal_zone.name} min_fan_flow_ratio"] = min_fan_flow_ratio
@@ -1013,22 +1013,6 @@ def run(model, runner, user_arguments)
preheat_coil_setpoint_manager.addToNode(preheat_sm_location)
end
- # for zones that got skipped, check if there are already baseboards. if not, add them.
- model.getThermalZones.each do |thermal_zone|
- if unconditioned_zones.include? thermal_zone.name.get
- runner.registerInfo("Thermal zone #{thermal_zone} was unconditioned in the baseline, and will not receive a packaged GHP.")
- elsif zones_to_skip.include? thermal_zone.name.get
- if thermal_zone.equipment.empty?
- baseboard = OpenStudio::Model::ZoneHVACBaseboardConvectiveElectric.new(model)
- baseboard.setName("#{thermal_zone.name} Electric Baseboard")
- baseboard.setEfficiency(1.0)
- baseboard.autosizeNominalCapacity
- baseboard.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule)
- baseboard.addToThermalZone(thermal_zone)
- end
- end
- end
-
# set initial and final conditions for reporting
# add dcv to air loop if dcv arg is true
diff --git a/resources/measures/upgrade_hvac_packaged_gshp/resources/performance_curves.rb b/resources/measures/upgrade_hvac_packaged_gshp/resources/performance_curves.rb
index 524dc4e24..d738dbdc4 100644
--- a/resources/measures/upgrade_hvac_packaged_gshp/resources/performance_curves.rb
+++ b/resources/measures/upgrade_hvac_packaged_gshp/resources/performance_curves.rb
@@ -39,18 +39,11 @@
module MakePerformanceCurves
# method to convert csv value to float
def convert_to_float(value)
- # check if value is empty
- if value.to_s.empty?
- # value is empty; return nil
- nil
- elsif value.to_f != value
- # elsif not value.to_f.to_s.include? value.to_s
- # value is not a number; return nil
- nil
- else
- # value is a number; convert to float
- value.to_f
- end
+ # check if value is empty or not a number; return nil
+ return nil if value.to_s.empty? || !value.to_s.match?(/^-?\d+\.?\d*$/)
+
+ # value is a number; convert to float
+ value.to_f
end
def read_performance_curve_data(data_path, runner)
diff --git a/resources/measures/upgrade_hvac_packaged_gshp/tests/packaged_gthp_test.rb b/resources/measures/upgrade_hvac_packaged_gshp/tests/packaged_gthp_test.rb
index 687a088e5..cb973e69e 100644
--- a/resources/measures/upgrade_hvac_packaged_gshp/tests/packaged_gthp_test.rb
+++ b/resources/measures/upgrade_hvac_packaged_gshp/tests/packaged_gthp_test.rb
@@ -54,7 +54,7 @@ def setup
Open3.capture3(command)
rescue StandardError
msg = 'GHEDesigner python package not found in this test environment, pip install GHEDesigner and retry'
- raise LoadError.new msg
+ raise LoadError, msg
end
end
@@ -71,6 +71,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_pump/measure.rb b/resources/measures/upgrade_hvac_pump/measure.rb
index 498c44193..76491cf0b 100644
--- a/resources/measures/upgrade_hvac_pump/measure.rb
+++ b/resources/measures/upgrade_hvac_pump/measure.rb
@@ -72,21 +72,21 @@ def arguments(model)
# add outdoor air temperature reset for chilled water supply temperature
chw_hw_oat_reset = OpenStudio::Measure::OSArgument.makeBoolArgument('chw_hw_oat_reset', true)
chw_hw_oat_reset.setDisplayName('Add outdoor air temperature reset' \
- ' for chilled/hot water supply temperature?')
+ ' for chilled/hot water supply temperature?')
chw_hw_oat_reset.setDefaultValue(false)
args << chw_hw_oat_reset
# add outdoor air temperature reset for condenser water temperature
cw_oat_reset = OpenStudio::Measure::OSArgument.makeBoolArgument('cw_oat_reset', true)
cw_oat_reset.setDisplayName('Add outdoor air temperature reset' \
- ' for condenser water temperature?')
+ ' for condenser water temperature?')
cw_oat_reset.setDefaultValue(false)
args << cw_oat_reset
# print out details?
debug_verbose = OpenStudio::Measure::OSArgument.makeBoolArgument('debug_verbose', true)
debug_verbose.setDisplayName('Print out detailed debugging logs' \
- ' if this parameter is true')
+ ' if this parameter is true')
debug_verbose.setDefaultValue(false)
args << debug_verbose
@@ -320,7 +320,7 @@ def self.control_specifications(model)
spms = plant_loop.supplyOutletNode.setpointManagers
case loop_type
- when 'Cooling'
+ when 'Cooling', 'Heating'
# get control specifications
spms.each do |spm|
total_count_spm_chw_hw += 1
@@ -336,14 +336,6 @@ def self.control_specifications(model)
fraction_cw_oat_reset_enabled_sum += 1
end
end
- when 'Heating'
- # get control specifications
- spms.each do |spm|
- total_count_spm_chw_hw += 1
- if spm.to_SetpointManagerOutdoorAirReset.is_initialized
- fraction_chw_hw_oat_reset_enabled_sum += 1
- end
- end
end
end
@@ -367,7 +359,6 @@ def self.control_specifications(model)
# hard-coding this because of https://github.com/NREL/openstudio-standards/issues/1915
# @param plant_loop [OpenStudio::Model::PlantLoop] plant loop
# @return [Boolean] returns true if successful, false if not
- # rubocop:disable Naming/PredicateMethod
def plant_loop_apply_prm_baseline_condenser_water_temperatures(runner, plant_loop)
sizing_plant = plant_loop.sizingPlant
loop_type = sizing_plant.loopType
@@ -537,7 +528,6 @@ def plant_loop_apply_prm_baseline_condenser_water_temperatures(runner, plant_loo
cw_t_stpt_manager.setOffsetTemperatureDifference(approach_k)
true
end
- # rubocop:enable Naming/PredicateMethod
# Determine the performance rating method specified
# design condenser water temperature, approach, and range
@@ -587,7 +577,6 @@ def plant_loop_prm_baseline_condenser_water_temperatures(runner, plant_loop, des
end
# define what happens when the measure is run
- # rubocop:disable Naming/PredicateMethod
def run(model, runner, user_arguments)
super # Do **NOT** remove this line
@@ -928,7 +917,6 @@ def run(model, runner, user_arguments)
return true
end
- # rubocop:enable Naming/PredicateMethod
end
# register the measure to be used by the application
diff --git a/resources/measures/upgrade_hvac_pump/tests/upgrade_hvac_pump_test.rb b/resources/measures/upgrade_hvac_pump/tests/upgrade_hvac_pump_test.rb
index 6e1c66bbe..8fada2833 100644
--- a/resources/measures/upgrade_hvac_pump/tests/upgrade_hvac_pump_test.rb
+++ b/resources/measures/upgrade_hvac_pump/tests/upgrade_hvac_pump_test.rb
@@ -116,6 +116,7 @@ def epws_for_tests
# supporting method: load model from osm path
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_hvac_replace_boiler_by_heatpump/measure.xml b/resources/measures/upgrade_hvac_replace_boiler_by_heatpump/measure.xml
index bce57fda2..a1e8c8c0b 100644
--- a/resources/measures/upgrade_hvac_replace_boiler_by_heatpump/measure.xml
+++ b/resources/measures/upgrade_hvac_replace_boiler_by_heatpump/measure.xml
@@ -2,9 +2,9 @@
3.1
replace_boiler_with_heat_pump
- e5661219-4641-4140-8c83-a9d602e45ca7
- 7c7fe317-e331-43d6-bb99-a84212d66f94
- 2025-07-23T23:00:38Z
+ d30905e5-d454-4454-8e92-330e4fb710d5
+ d4499601-9ce0-4ac3-abb2-65633dbdf30a
+ 2026-01-13T17:23:00Z
F0147A36
ReplaceBoilerWithHeatPump
replace_boiler_by_heatpump
@@ -14,7 +14,6 @@
keep_setpoint
Keep existing hot water loop setpoint?
-
Boolean
true
false
@@ -61,7 +60,6 @@
sizing_method
Select heat pump water heater sizing method
-
Choice
true
false
@@ -98,7 +96,6 @@
hp_des_cap
Rated ASHP heating capacity per unit [kW]
-
Double
true
false
@@ -107,7 +104,6 @@
bu_type
Select backup heater
-
Choice
true
false
@@ -126,7 +122,6 @@
hpwh_cutoff_T
Set the heat pump cutoff temperature [F]
-
Double
true
false
@@ -135,7 +130,6 @@
hpwh_Design_OAT
Set the heat pump design outdoor air temperature to base the performance data [F]
-
Double
true
false
diff --git a/resources/measures/upgrade_hvac_replace_boiler_by_heatpump/tests/add_hpwh_test.rb b/resources/measures/upgrade_hvac_replace_boiler_by_heatpump/tests/add_hpwh_test.rb
index d62c68c08..881875145 100644
--- a/resources/measures/upgrade_hvac_replace_boiler_by_heatpump/tests/add_hpwh_test.rb
+++ b/resources/measures/upgrade_hvac_replace_boiler_by_heatpump/tests/add_hpwh_test.rb
@@ -42,10 +42,10 @@
require 'fileutils'
require_relative '../../../../test/helpers/minitest_helper'
-class AddHpwhTest < Minitest::Test
+class ReplaceBoilerWithHeatPumpTest < Minitest::Test
def test_good_argument_values
# create an instance of the measure
- measure = AddHpwh.new
+ measure = ReplaceBoilerWithHeatPump.new
# create runner with empty OSW
osw = OpenStudio::WorkflowJSON.new
@@ -93,7 +93,7 @@ def test_good_argument_values
def test_empty_model
# create an instance of the measure
- measure = AddHpwh.new
+ measure = ReplaceBoilerWithHeatPump.new
# create runner with empty OSW
osw = OpenStudio::WorkflowJSON.new
@@ -132,7 +132,7 @@ def test_empty_model
def test_custom_args_pumpedcondenser_specific_zone
# create an instance of the measure
- measure = AddHpwh.new
+ measure = ReplaceBoilerWithHeatPump.new
# create runner with empty OSW
osw = OpenStudio::WorkflowJSON.new
@@ -182,7 +182,7 @@ def test_custom_args_pumpedcondenser_specific_zone
def test_custom_args_wrappedcondenser_specific_zone
# create an instance of the measure
- measure = AddHpwh.new
+ measure = ReplaceBoilerWithHeatPump.new
# create runner with empty OSW
osw = OpenStudio::WorkflowJSON.new
diff --git a/resources/measures/upgrade_hvac_rtu_adv/measure.rb b/resources/measures/upgrade_hvac_rtu_adv/measure.rb
index 4f267fc72..e19d8e5ab 100644
--- a/resources/measures/upgrade_hvac_rtu_adv/measure.rb
+++ b/resources/measures/upgrade_hvac_rtu_adv/measure.rb
@@ -614,7 +614,6 @@ def adjust_rated_cop_from_ref_cfm_per_ton(runner, airflow_sized_m_3_per_s, rated
#### End predefined functions
# define what happens when the measure is run
- # rubocop:disable Naming/PredicateMethod
def run(model, runner, user_arguments)
super
@@ -1923,7 +1922,6 @@ def run(model, runner, user_arguments)
true
end
- # rubocop:enable Naming/PredicateMethod
end
# register the measure to be used by the application
diff --git a/resources/measures/upgrade_hvac_rtu_adv/tests/measure_test.rb b/resources/measures/upgrade_hvac_rtu_adv/tests/measure_test.rb
index 32bb8ce1f..10e7cc20b 100644
--- a/resources/measures/upgrade_hvac_rtu_adv/tests/measure_test.rb
+++ b/resources/measures/upgrade_hvac_rtu_adv/tests/measure_test.rb
@@ -255,6 +255,7 @@ def test_get_rated_cop_cooling_adv
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -264,7 +265,7 @@ def load_model(osm_path)
def run_dir(test_name)
# always generate test output in specially named 'output' directory so
# result files are not made part of the measure
- "#{File.dirname(__FILE__)}/output/#{test_name}"
+ File.expand_path(File.join(File.dirname(__FILE__), 'output', test_name))
end
def model_input_path(osm_name)
diff --git a/resources/measures/upgrade_hvac_vrf_hr_doas/measure.rb b/resources/measures/upgrade_hvac_vrf_hr_doas/measure.rb
index 68896295d..88de1035b 100644
--- a/resources/measures/upgrade_hvac_vrf_hr_doas/measure.rb
+++ b/resources/measures/upgrade_hvac_vrf_hr_doas/measure.rb
@@ -311,9 +311,6 @@ def model_add_curve(model, curve_name, standards_data_curve, std)
table.setName(data['name'])
table.setOutputUnitType(data['output_unit_type'])
table
- else
- # OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', "#{curve_name}' has an invalid form: #{data['form']}', cannot create this curve.")
- nil
end
end
@@ -1403,11 +1400,9 @@ def run(model, runner, user_arguments)
airloop_na_tz = []
# air loops with non applicable thermal zones
air_loop_hvac.thermalZones.sort.each do |tz|
- # skip food service air loops
- if ['kitchen', 'KITCHEN', 'Kitchen', 'Dining', 'dining'].any? { |word| tz.name.get.include?(word) }
- airloop_na_tz << tz
- # skip non-conditioned thermal zones
- elsif !OpenstudioStandards::ThermalZone.thermal_zone_heated?(tz) && !OpenstudioStandards::ThermalZone.thermal_zone_cooled?(tz)
+ # skip food service air loops and non-conditioned thermal zones
+ if ['kitchen', 'KITCHEN', 'Kitchen', 'Dining', 'dining'].any? { |word| tz.name.get.include?(word) } ||
+ (!OpenstudioStandards::ThermalZone.thermal_zone_heated?(tz) && !OpenstudioStandards::ThermalZone.thermal_zone_cooled?(tz))
airloop_na_tz << tz
else
airloop_applicable_tz << tz
@@ -1544,17 +1539,13 @@ def run(model, runner, user_arguments)
# loop through reheat types - if gas is found, assume new system to non applicable thermal zone uses gas. Otherwise, assume electric
model.getAirTerminalSingleDuctVAVReheats.each do |terminal|
reheat_coil = terminal.reheatCoil
- if reheat_coil.to_CoilHeatingWater.is_initialized
- htg_type = 'NaturalGas'
- elsif reheat_coil.to_CoilHeatingGas.is_initialized
+ if reheat_coil.to_CoilHeatingWater.is_initialized || reheat_coil.to_CoilHeatingGas.is_initialized
htg_type = 'NaturalGas'
end
end
model.getAirTerminalSingleDuctParallelPIUReheats.each do |terminal|
reheat_coil = terminal.reheatCoil
- if reheat_coil.to_CoilHeatingWater.is_initialized
- htg_type = 'NaturalGas'
- elsif reheat_coil.to_CoilHeatingGas.is_initialized
+ if reheat_coil.to_CoilHeatingWater.is_initialized || reheat_coil.to_CoilHeatingGas.is_initialized
htg_type = 'NaturalGas'
end
end
@@ -1563,9 +1554,7 @@ def run(model, runner, user_arguments)
# Remove air aloops
applicable_air_loops.each do |air_loop|
# Don't remove airloops representing non-mechanically cooled systems
- if !air_loop.additionalProperties.hasFeature('non_mechanically_cooled')
- air_loop.remove
- else
+ if air_loop.additionalProperties.hasFeature('non_mechanically_cooled')
# Remove heating coil on
air_loop.supplyComponents.each do |supply_comp|
# Remove standalone heating coils
@@ -1582,6 +1571,8 @@ def run(model, runner, user_arguments)
end
end
end
+ else
+ air_loop.remove
end
end
# Zone equipment for applicable thermal zones or non-applicable mz thermal zones needing new equipment
@@ -1753,19 +1744,12 @@ def run(model, runner, user_arguments)
air_loop_hvac.sizingSystem.setCentralCoolingDesignSupplyAirTemperature(doas_dat_clg_c)
air_loop_hvac.sizingSystem.setCentralHeatingDesignSupplyAirTemperature(doas_dat_htg_c)
- # remove any existing ERV
- air_loop_hvac.supplyComponents.each do |component|
- next unless component.to_HeatExchangerAirToAirSensibleAndLatent.is_initialized
-
- component.remove
- end
-
- # remove electric heat pump coil so only electric resistance heating coil remains
- # this is needed since constructor method does not have electric resistance heating only
+ # remove any existing ERV and electric heat pump coil so only electric resistance heating coil remains
air_loop_hvac.supplyComponents.each do |component|
- next unless component.to_CoilHeatingDXSingleSpeed.is_initialized
-
- component.remove
+ if component.to_HeatExchangerAirToAirSensibleAndLatent.is_initialized ||
+ component.to_CoilHeatingDXSingleSpeed.is_initialized
+ component.remove
+ end
end
# add ERV/HRV
@@ -1954,21 +1938,19 @@ def run(model, runner, user_arguments)
end
######################################################
- # puts("### clean dummy VRF outdoor unit object")
+ # puts("### clean dummy VRF outdoor unit object and override other curves")
######################################################
+ # modifying cooling EIR modifier curve (function of part-load ratio)")
+ # curve derived from comparing PLR performance between Daikin data, Mitsubishi data, and Daikin spec sheet
model.getAirConditionerVariableRefrigerantFlows.each do |obj|
+ # delete dummy VRF object
if obj.name.to_s == vrf_outdoor_unit_name
# puts("&&& deleting dummy VRF object: #{obj.name}")
obj.remove
+ next
end
- end
- ######################################################
- # puts("### overriding other curves")
- ######################################################
- # modifying cooling EIR modifier curve (function of part-load ratio)")
- # curve derived from comparing PLR performance between Daikin data, Mitsubishi data, and Daikin spec sheet
- model.getAirConditionerVariableRefrigerantFlows.each do |obj|
+ # override curves for actual VRF objects
# puts("&&& overriding other curves: outdoor unit name = #{obj.name}")
next unless obj.coolingEnergyInputRatioModifierFunctionofLowPartLoadRatioCurve.is_initialized
diff --git a/resources/measures/upgrade_hvac_vrf_hr_doas/tests/measure_test.rb b/resources/measures/upgrade_hvac_vrf_hr_doas/tests/measure_test.rb
index df45e3f80..c626b4d13 100644
--- a/resources/measures/upgrade_hvac_vrf_hr_doas/tests/measure_test.rb
+++ b/resources/measures/upgrade_hvac_vrf_hr_doas/tests/measure_test.rb
@@ -62,6 +62,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
diff --git a/resources/measures/upgrade_light_lighting_controls/measure.rb b/resources/measures/upgrade_light_lighting_controls/measure.rb
index 1b5d67b82..a37d4ba92 100644
--- a/resources/measures/upgrade_light_lighting_controls/measure.rb
+++ b/resources/measures/upgrade_light_lighting_controls/measure.rb
@@ -39,7 +39,7 @@
require 'openstudio-standards'
# require all .rb files in resources folder
-Dir[File.dirname(__FILE__) + '/resources/*.rb'].each { |file| require file }
+Dir["#{File.dirname(__FILE__)}/resources/*.rb"].sort.each { |file| require file }
# start the measure
class LightingControls < OpenStudio::Measure::ModelMeasure
@@ -143,8 +143,7 @@ def run(model, runner, user_arguments)
next unless surface.outsideBoundaryCondition == 'Outdoors'
surface.subSurfaces.each do |sub_surface|
- next unless %w[FixedWindow OperableWindow Skylight
- GlassDoor].include?(sub_surface.subSurfaceType)
+ next unless ['FixedWindow', 'OperableWindow', 'Skylight', 'GlassDoor'].include?(sub_surface.subSurfaceType)
# get window area, if area is 0, no exterior fenestration
ext_fen_area_m2 += sub_surface.netArea
@@ -250,21 +249,21 @@ def run(model, runner, user_arguments)
windows = {}
skylights = {}
space.surfaces.sort.each do |surface|
- next unless surface.outsideBoundaryCondition == 'Outdoors' && %w[Wall
- RoofCeiling].include?(surface.surfaceType)
+ next unless surface.outsideBoundaryCondition == 'Outdoors' && ['Wall',
+ 'RoofCeiling'].include?(surface.surfaceType)
# Skip non-vertical walls and non-horizontal roofs
straight_upward = OpenStudio::Vector3d.new(0, 0, 1)
surface_normal = surface.outwardNormal
if surface.surfaceType == 'Wall'
# @todo stop skipping non-vertical walls
- if !(surface_normal.z.abs < 0.001) && !surface.subSurfaces.empty?
+ if surface_normal.z.abs >= 0.001 && !surface.subSurfaces.empty?
runner.registerWarning("Cannot currently handle non-vertical walls; skipping windows on #{surface.name} in #{space.name} for daylight sensor positioning.")
next
end
elsif surface.surfaceType == 'RoofCeiling'
# @todo stop skipping non-horizontal roofs
- if !(surface_normal.to_s == straight_upward.to_s) && !surface.subSurfaces.empty?
+ if surface_normal.to_s != straight_upward.to_s && !surface.subSurfaces.empty?
runner.registerWarning("Cannot currently handle non-horizontal roofs; skipping skylights on #{surface.name} in #{space.name} for daylight sensor positioning.")
runner.registerInfo("---Surface #{surface.name} has outward normal of #{surface_normal.to_s.gsub(
/\[|\]/, '|'
@@ -311,8 +310,7 @@ def run(model, runner, user_arguments)
# Loop through all subsurfaces and
surface.subSurfaces.sort.each do |sub_surface|
- next unless sub_surface.outsideBoundaryCondition == 'Outdoors' && %w[FixedWindow OperableWindow
- Skylight].include?(sub_surface.subSurfaceType)
+ next unless sub_surface.outsideBoundaryCondition == 'Outdoors' && ['FixedWindow', 'OperableWindow', 'Skylight'].include?(sub_surface.subSurfaceType)
# Find the area
net_area_m2 = sub_surface.netArea
@@ -328,7 +326,7 @@ def run(model, runner, user_arguments)
# Log the window properties to use when creating daylight sensors
properties = { facade: facade, area_m2: net_area_m2, handle: sub_surface.handle,
- head_height_m: head_height_m, name: sub_surface.name.get.to_s }
+ head_height_m: head_height_m, name: sub_surface.name.get.to_s }
if facade == '0-Up'
skylights[sub_surface] = properties
else
@@ -557,20 +555,15 @@ def run(model, runner, user_arguments)
# In these spaces, ASHRAE 90.1 already requires occuapancy sensors, therefore we will skip these zones when applying the LPD reduction so as to not overestimate savings.
spaces_to_skip = []
if ['ComStock 90.1-2004', 'ComStock 90.1-2007'].include?(template)
- spaces_to_skip = %w[Meeting StaffLounge Conference]
+ spaces_to_skip = ['Meeting', 'StaffLounge', 'Conference']
elsif template == 'ComStock 90.1-2010'
- spaces_to_skip = %w[Auditorium Classroom ComputerRoom Restroom Meeting PublicRestroom StaffLounge
- Storage Back_Space Conference DressingRoom Janitor LockerRoom CompRoomClassRm
- OfficeSmall StockRoom]
+ spaces_to_skip = ['Auditorium', 'Classroom', 'ComputerRoom', 'Restroom', 'Meeting', 'PublicRestroom', 'StaffLounge', 'Storage', 'Back_Space', 'Conference', 'DressingRoom', 'Janitor', 'LockerRoom', 'CompRoomClassRm', 'OfficeSmall', 'StockRoom']
elsif template == 'ComStock 90.1-2013'
- spaces_to_skip = %w[Auditorium Classroom ComputerRoom Restroom Meeting PublicRestroom StaffLounge
- Storage Back_Space Conference DressingRoom Janitor LockerRoom CompRoomClassRm
- OfficeSmall StockRoom GuestLounge Banquet Lounge]
+ spaces_to_skip = ['Auditorium', 'Classroom', 'ComputerRoom', 'Restroom', 'Meeting', 'PublicRestroom', 'StaffLounge', 'Storage', 'Back_Space', 'Conference', 'DressingRoom', 'Janitor', 'LockerRoom', 'CompRoomClassRm', 'OfficeSmall', 'StockRoom', 'GuestLounge', 'Banquet', 'Lounge']
elsif template == 'ComStock DEER 2011'
- spaces_to_skip = %w[Classroom ComputerRoom Meeting CompRoomClassRm OfficeSmall]
+ spaces_to_skip = ['Classroom', 'ComputerRoom', 'Meeting', 'CompRoomClassRm', 'OfficeSmall']
elsif ['ComStock DEER 2014', 'ComStock DEER 2015', 'ComStock DEER 2017'].include?(template)
- spaces_to_skip = %w[Classroom ComputerRoom Meeting CompRoomClassRm OfficeSmall Restroom GuestLounge
- PublicRestroom StaffLounge Storage LockerRoom Lounge]
+ spaces_to_skip = ['Classroom', 'ComputerRoom', 'Meeting', 'CompRoomClassRm', 'OfficeSmall', 'Restroom', 'GuestLounge', 'PublicRestroom', 'StaffLounge', 'Storage', 'LockerRoom', 'Lounge']
end
# set location for csv lookup file
@@ -629,7 +622,7 @@ def run(model, runner, user_arguments)
end
if num_spaces_to_get_occupancy_sensors + num_spaces_to_get_daylighting_sensors == 0
- runner.registerAsNotApplicable("Neither daylighting sensors nor occupancy sensors were applicable to any spaces in the model. Measure is not applicable.")
+ runner.registerAsNotApplicable('Neither daylighting sensors nor occupancy sensors were applicable to any spaces in the model. Measure is not applicable.')
return true
end
diff --git a/resources/measures/upgrade_ppl_electric_kitchen_equipment/tests/kitchen_equip_measure_test.rb b/resources/measures/upgrade_ppl_electric_kitchen_equipment/tests/kitchen_equip_measure_test.rb
index bfe629472..0ba0fb600 100644
--- a/resources/measures/upgrade_ppl_electric_kitchen_equipment/tests/kitchen_equip_measure_test.rb
+++ b/resources/measures/upgrade_ppl_electric_kitchen_equipment/tests/kitchen_equip_measure_test.rb
@@ -44,7 +44,19 @@
require_relative '../measure'
require_relative '../../../../test/helpers/minitest_helper'
-class EnvSecondaryWindowsTest < Minitest::Test
+class PplElectricKitchenEquipment < Minitest::Test
+ def test_number_of_arguments_and_argument_names
+ # create an instance of the measure
+ measure = ElectrifyKitchenEquipment.new
+
+ # make an empty model
+ model = OpenStudio::Model::Model.new
+
+ # get arguments and test that they are what we are expecting
+ arguments = measure.arguments(model)
+ assert_equal(0, arguments.size)
+ end
+
# return file paths to test models in test directory
def models_for_tests
paths = Dir.glob(File.join(File.dirname(__FILE__), '../../../tests/models/*.osm'))
@@ -60,6 +72,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -69,16 +82,11 @@ def load_model(osm_path)
def run_dir(test_name)
# always generate test output in specially named 'output' directory so result files are not made part of the measure
- return "#{File.dirname(__FILE__)}/output/#{test_name}"
- end
-
- def model_input_path(osm_name)
- # return models_for_tests.select { |x| set[:model] == osm_name }
- return File.join(File.dirname(__FILE__), '../../../tests/models', osm_name)
- end
-
- def epw_input_path(epw_name)
- return File.join(File.dirname(__FILE__), '../../../tests/weather', epw_name)
+ path = "#{File.dirname(__FILE__)}/output/#{test_name}"
+ unless File.directory?(path)
+ FileUtils.mkdir_p(path)
+ end
+ return path
end
def model_output_path(test_name)
@@ -94,7 +102,7 @@ def report_path(test_name)
end
# applies the measure and then runs the model
- def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path, run_model: false, model: nil)
+ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path, run_model: false)
assert(File.exist?(osm_path))
assert(File.exist?(epw_path))
@@ -110,19 +118,17 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
FileUtils.rm_f(model_output_path(test_name))
FileUtils.rm_f(report_path(test_name))
-
# copy the osm and epw to the test directory
- new_osm_path = "#{run_dir(test_name)}/#{File.basename(osm_path)}"
+ # new_osm_path = File.expand_path("#{Dir.pwd}/#{File.basename(osm_path)}")
+ new_osm_path = "#{Dir.pwd}/#{File.basename(osm_path)}"
FileUtils.cp(osm_path, new_osm_path)
- new_epw_path = "#{run_dir(test_name)}/#{File.basename(epw_path)}"
+ new_epw_path = "#{Dir.pwd}/#{File.basename(epw_path)}"
FileUtils.cp(epw_path, new_epw_path)
# create an instance of a runner
runner = OpenStudio::Measure::OSRunner.new(OpenStudio::WorkflowJSON.new)
# load the test model
- if model.nil?
- model = load_model(new_osm_path)
- end
+ model = load_model(new_osm_path)
# set model weather file
epw_file = OpenStudio::EpwFile.new(OpenStudio::Path.new(new_epw_path))
@@ -135,19 +141,19 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
result = runner.result
result_success = result.value.valueName == 'Success'
- # Show the output
+ # show the output
show_output(result)
- # Save model
+ # save model
model.save(model_output_path(test_name), true)
if run_model && result_success
puts "\nRUNNING MODEL..."
- std = Standard.build('90.1-2013')
+ std = Standard.build('ComStock DEER 2020')
std.model_run_simulation_and_log_errors(model, run_dir(test_name))
- # Check that the model ran successfully
+ # check that the model ran successfully
assert(File.exist?(sql_path(test_name)))
end
@@ -157,328 +163,46 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
return result
end
- def test_number_of_arguments_and_argument_names
- # This test ensures that the current test is matched to the measure inputs
- test_name = 'test_number_of_arguments_and_argument_names'
- puts "\n######\nTEST:#{test_name}\n######\n"
-
- # Create an instance of the measure
- measure = EnvSecondaryWindows.new
-
- # Make an empty model
- model = OpenStudio::Model::Model.new
-
- # Get arguments and test that they are what we are expecting
- arguments = measure.arguments(model)
- assert_equal(0, arguments.size)
- end
-
- def test_r_value_cz_3a_single_pane
- osm_name = 'Small_Office_CEC8.osm'
- epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
-
- # Test expectations for Single - No LowE - Clear - Wood in 3A
- # is to increase to U-0.37
- target_u_value_ip = 0.37
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvSecondaryWindows.new
-
- # Load the model; only used here for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Check that the starting R-value is less than the target
- old_u_val_ip = 0
- old_ext_surf_material = nil
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- old_u_val_si = glazing_layer.uFactor
- old_u_val_ip = OpenStudio.convert(old_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- break
- end
- assert(old_u_val_ip > target_u_value_ip)
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- model = load_model(model_output_path(__method__))
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- new_u_val_si = glazing_layer.uFactor
- new_u_val_ip = OpenStudio.convert(new_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- # Check that original U-value was above (worse than) the target threshold
- assert(old_u_val_ip > new_u_val_ip)
-
- # Check that the new U-value matches the target
- tolerance = 0.01
- assert_in_delta(target_u_value_ip, new_u_val_ip, tolerance)
-
- break
- end
- end
-
- def test_r_value_cz_3a_double_pane
- osm_name = 'Quick_Service_Restaurant_Pre1980_3A.osm'
- epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
-
- # Test expectations for Double - LowE - Clear - Aluminum in 3A
- # is to increase to U-0.50
- target_u_value_ip = 0.50
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvSecondaryWindows.new
-
- # Load the model; only used here for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Check that the starting R-value is less than the target
- old_u_val_ip = 0
- old_ext_surf_material = nil
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- old_u_val_si = glazing_layer.uFactor
- old_u_val_ip = OpenStudio.convert(old_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- break
- end
- assert(old_u_val_ip > target_u_value_ip)
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- model = load_model(model_output_path(__method__))
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- new_u_val_si = glazing_layer.uFactor
- new_u_val_ip = OpenStudio.convert(new_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- # Check that original U-value was above (worse than) the target threshold
- assert(old_u_val_ip > new_u_val_ip)
-
- # Check that the new U-value matches the target
- tolerance = 0.01
- assert_in_delta(target_u_value_ip, new_u_val_ip, tolerance)
-
- break
- end
- end
-
- def test_r_value_cz_5a
- osm_name = 'SecondarySchool_Pre1980_5A.osm'
- epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
-
- # Test expectations for Double - No LowE - Clear - Aluminum
- # is to increase to 0.61 in all climate zones
- target_u_value_ip = 0.61
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvSecondaryWindows.new
-
- # Load the model; only used here for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Check that the starting R-value is less than the target
- old_u_val_ip = 0
- old_ext_surf_material = nil
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- old_u_val_si = glazing_layer.uFactor
- old_u_val_ip = OpenStudio.convert(old_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- break
- end
- assert(old_u_val_ip > target_u_value_ip)
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- model = load_model(model_output_path(__method__))
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- new_u_val_si = glazing_layer.uFactor
- new_u_val_ip = OpenStudio.convert(new_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- # Check that original U-value was above (worse than) the target threshold
- assert(old_u_val_ip > new_u_val_ip)
-
- # Check that the new U-value matches the target
- tolerance = 0.01
- assert_in_delta(target_u_value_ip, new_u_val_ip, tolerance)
-
- break
- end
+ # create an array of hashes with model name, weather, and expected result
+ def models_to_test
+ test_sets = []
+ test_sets << { model: '310_PSZ-AC with gas coil', weather: 'VA_MANASSAS_724036_12', result: 'Success' }
+ test_sets << { model: '310_PSZ-AC with district hot water', weather: 'GA_ROBINS_AFB_722175_12', result: 'NA' }
+ return test_sets
end
- def test_r_value_cz_8a_double_pane_thermally_broken
- osm_name = 'Stripmall_Pre1980_8A.osm'
- epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
-
- # Test expectations for Double - No LowE - Clear - Aluminum
- # is to increase to U-0.44 in CZ 8
- target_u_value_ip = 0.44
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvSecondaryWindows.new
-
- # Load the model; only used here for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Check that the starting R-value is less than the target
- old_u_val_ip = 0
- old_ext_surf_material = nil
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- old_u_val_si = glazing_layer.uFactor
- old_u_val_ip = OpenStudio.convert(old_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- break
- end
- assert(old_u_val_ip > target_u_value_ip)
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- model = load_model(model_output_path(__method__))
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- new_u_val_si = glazing_layer.uFactor
- new_u_val_ip = OpenStudio.convert(new_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- # Check that original U-value was above (worse than) the target threshold
- assert(old_u_val_ip > new_u_val_ip)
-
- # Check that the new U-value matches the target
- tolerance = 0.01
- assert_in_delta(target_u_value_ip, new_u_val_ip, tolerance)
-
- break
- end
- end
-
- def test_r_value_cz_cec16_double_pane_thermally_broken
- osm_name = 'Retail_DEERPre1975_CEC16.osm'
- epw_name = 'USA_CA_Fullerton.Muni.AP.722976_TMY3.epw'
-
- # Test expectations for Single - No LowE - Clear - Aluminum
- # is to increase to U-0.61 in all climate zones
- target_u_value_ip = 0.61
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvSecondaryWindows.new
-
- # Load the model; only used here for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Check that the starting R-value is less than the target
- old_u_val_ip = 0
- old_ext_surf_material = nil
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- old_u_val_si = glazing_layer.uFactor
- old_u_val_ip = OpenStudio.convert(old_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- break
- end
- assert(old_u_val_ip > target_u_value_ip)
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- model = load_model(model_output_path(__method__))
- model.getSubSurfaces.each do |sub_surface|
- next unless (sub_surface.outsideBoundaryCondition == 'Outdoors') && sub_surface.subSurfaceType.include?('Window')
-
- surf_const = sub_surface.construction.get.to_LayeredConstruction.get
- glazing_layer = surf_const.layers[0].to_SimpleGlazing.get
- new_u_val_si = glazing_layer.uFactor
- new_u_val_ip = OpenStudio.convert(new_u_val_si, 'W/m^2*K', 'Btu/ft^2*h*R').get
-
- # Check that original U-value was above (worse than) the target threshold
- assert(old_u_val_ip > new_u_val_ip)
-
- # Check that the new U-value matches the target
- # Set the tolerance higher for this test because the
- # Single - No LowE - Clear - Aluminum
- tolerance = 0.01
- assert_in_delta(target_u_value_ip, new_u_val_ip, tolerance)
+ def test_models
+ test_name = 'test_models'
+ puts "\n######\nTEST:#{test_name}\n######\n"
- break
+ models_to_test.each do |set|
+ instance_test_name = set[:model]
+ puts "instance test name: #{instance_test_name}"
+ osm_path = models_for_tests.select { |x| set[:model] == File.basename(x, '.osm') }
+ epw_path = epws_for_tests.select { |x| set[:weather] == File.basename(x, '.epw') }
+ assert(!osm_path.empty?)
+ assert(!epw_path.empty?)
+ osm_path = osm_path[0]
+ epw_path = epw_path[0]
+
+ # create an instance of the measure
+ measure = ElectrifyKitchenEquipment.new
+
+ # load the model; only used here for populating arguments
+ model = load_model(osm_path)
+
+ # set arguments here; will vary by measure
+ arguments = measure.arguments(model)
+ # argument_map = OpenStudio::Measure::OSArgumentMap.new
+ argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
+
+ # apply the measure to the model and optionally run the model
+ result = apply_measure_and_run(instance_test_name, measure, argument_map, osm_path, epw_path, run_model: false)
+
+ # check the measure result; result values will equal Success, Fail, or Not Applicable
+ # also check the amount of warnings, info, and error messages
+ # use if or case statements to change expected assertion depending on model characteristics
+ assert_equal(set[:result].to_s, result.value.valueName.to_s)
end
end
-
- def test_na_simple_glazing_name_not_recognized
- osm_name = 'Warehouse_5A.osm'
- epw_name = 'MI_DETROIT_725375_12.epw'
-
- osm_path = model_input_path(osm_name)
- epw_path = epw_input_path(epw_name)
-
- # Create an instance of the measure
- measure = EnvSecondaryWindows.new
-
- # Load the model for populating arguments
- model = load_model(osm_path)
- arguments = measure.arguments(model)
- argument_map = OpenStudio::Measure::OSArgumentMap.new
-
- # Apply the measure to the model and optionally run the model
- result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
-
- # Should be NA because this is a warehouse with metal building walls
- assert_equal('NA', result.value.valueName)
- end
end
diff --git a/resources/measures/upgrade_unoccupied_oa_controls/.rubocop.yml b/resources/measures/upgrade_unoccupied_oa_controls/.rubocop.yml
index 179890633..1fb89a3ba 100644
--- a/resources/measures/upgrade_unoccupied_oa_controls/.rubocop.yml
+++ b/resources/measures/upgrade_unoccupied_oa_controls/.rubocop.yml
@@ -1,6 +1,5 @@
# This is the configuration used to check the rubocop source code.
-inherit_from: .rubocop_todo.yml
require:
- rubocop/cop/internal_affairs
- rubocop-performance
@@ -142,9 +141,6 @@ RSpec:
RSpec/PredicateMatcher:
EnforcedStyle: explicit
-RSpec/FilePath:
- Enabled: false
-
RSpec/SpecFilePathFormat:
CustomTransform:
GitHubActionsFormatter: github_actions_formatter
diff --git a/resources/measures/upgrade_unoccupied_oa_controls/measure.rb b/resources/measures/upgrade_unoccupied_oa_controls/measure.rb
index 11d894d5f..76f833551 100644
--- a/resources/measures/upgrade_unoccupied_oa_controls/measure.rb
+++ b/resources/measures/upgrade_unoccupied_oa_controls/measure.rb
@@ -106,7 +106,7 @@ def self.air_loop_res?(air_loop_hvac)
is_res_system = true
air_loop_hvac.supplyComponents.each do |component|
obj_type = component.iddObjectType.valueName.to_s
- next unless obj_type == 'OS_AirLoopHVAC_OutdoorAirSystem'
+ next unless obj_type == 'OS_AirLoopHVAC_OutdoorAirSystem'
is_res_system = false
end
return is_res_system
diff --git a/resources/measures/upgrade_unoccupied_oa_controls/tests/unoccupied_oa_controls_measure_test.rb b/resources/measures/upgrade_unoccupied_oa_controls/tests/unoccupied_oa_controls_measure_test.rb
index 764df5548..6645d6066 100644
--- a/resources/measures/upgrade_unoccupied_oa_controls/tests/unoccupied_oa_controls_measure_test.rb
+++ b/resources/measures/upgrade_unoccupied_oa_controls/tests/unoccupied_oa_controls_measure_test.rb
@@ -61,6 +61,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)
@@ -70,13 +71,13 @@ def load_model(osm_path)
def run_dir(test_name)
# always generate test output in specially named 'output' directory so result files are not made part of the measure
- puts "run dir expanded=" + "#{File.expand_path(File.join(File.dirname(__FILE__),'output', test_name.to_s))}"
+ puts "run dir expanded=" + "#{File.expand_path(File.join(File.dirname(__FILE__),'output', test_name.to_s))}"
return File.join(File.dirname(__FILE__),"output","#{test_name}")
end
def model_input_path(osm_name)
# return models_for_tests.select { |x| set[:model] == osm_name }
- puts (File.expand_path(File.dirname(__FILE__))) #expands path relative to current wd, passing abs path back
+ puts (File.expand_path(File.dirname(__FILE__))) #expands path relative to current wd, passing abs path back
return File.expand_path(File.join(File.dirname(__FILE__), '../../../tests/models', osm_name))
end
@@ -118,11 +119,11 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
end
# copy the osm and epw to the test directory
- #osm_path = File.expand_path(osm_path)
- puts(osm_path)
+ # osm_path = File.expand_path(osm_path)
+ puts(osm_path)
new_osm_path = "#{run_dir(test_name)}/#{File.basename(osm_path)}"
- new_osm_path = File.expand_path(new_osm_path)
- puts(new_osm_path)
+ new_osm_path = File.expand_path(new_osm_path)
+ puts(new_osm_path)
FileUtils.cp(osm_path, new_osm_path)
new_epw_path = File.expand_path("#{run_dir(test_name)}/#{File.basename(epw_path)}")
FileUtils.cp(epw_path, new_epw_path)
@@ -131,7 +132,7 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
# load the test model
if model.nil?
- puts 'loading test model 1'
+ puts 'loading test model 1'
model = load_model(new_osm_path)
end
@@ -162,14 +163,14 @@ def apply_measure_and_run(test_name, measure, argument_map, osm_path, epw_path,
result = runner.result
result_success = result.value.valueName == 'Success'
- # change back directory
+ # change back directory
Dir.chdir(start_dir)
# Show the output
show_output(result)
# Save model
- puts "saving model to" + File.expand_path(model_output_path(test_name))
+ puts "saving model to" + File.expand_path(model_output_path(test_name))
model.save(File.expand_path(model_output_path(test_name)), true)
if run_model && result_success
@@ -236,18 +237,18 @@ def test_constant_oa_sched
result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
model = load_model(File.expand_path(model_output_path(__method__)))
- no_constant_oa = true
- model.getAirLoopHVACs.sort.each do |air_loop_hvac|
+ no_constant_oa = true
+ model.getAirLoopHVACs.sort.each do |air_loop_hvac|
air_loop_oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir
- if air_loop_oa_system.minimumOutdoorAirSchedule.get.to_ScheduleConstant.is_initialized
+ if air_loop_oa_system.minimumOutdoorAirSchedule.get.to_ScheduleConstant.is_initialized
no_constant_oa = false
end
end
assert(no_constant_oa)
-#put in assertions here
-#then duplicate it for other models if needed
+ #put in assertions here
+ #then duplicate it for other models if needed
end
def test_constant_air_loop_sched
@@ -271,34 +272,35 @@ def test_constant_air_loop_sched
result = apply_measure_and_run(__method__, measure, argument_map, osm_path, epw_path, run_model: false)
model = load_model(File.expand_path(model_output_path(__method__)))
- no_constant_loop_sched = true
- model.getAirLoopHVACs.sort.each do |air_loop_hvac|
- avail_sched = air_loop_hvac.availabilitySchedule #got an error checking this for initialization
- if avail_sched.to_ScheduleConstant.is_initialized
- no_constant_loop_sched = false
- end
- #among unitary systems, check supply fan operating mode for constant schedules
- if UnoccupiedOAControlsTest.air_loop_hvac_unitary_system?(air_loop_hvac)
- air_loop_hvac.supplyComponents.each do |component|
- obj_type = component.iddObjectType.valueName.to_s
- case obj_type
- when 'OS_AirLoopHVAC_UnitarySystem'
- component = component.to_AirLoopHVACUnitarySystem.get
- when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir'
- component = component.to_AirLoopHVACUnitaryHeatPump_AirToAir.get
- when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir_MultiSpeed'
- component = component.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get
- when 'OS_AirLoopHVAC_UnitaryHeatCool_VAVChangeoverBypass'
- component = component.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.get
- component.getSupplyAirFanOperatingModeSchedule
- if setMinimumOutdoorAirSchedule.to_ScheduleConstant.is_initialized
- no_constant_loop_sched = false
- end
- end
+ no_constant_loop_sched = true
+ model.getAirLoopHVACs.sort.each do |air_loop_hvac|
+ # got an error checking this for initialization
+ avail_sched = air_loop_hvac.availabilitySchedule
+ no_constant_loop_sched = false if avail_sched.to_ScheduleConstant.is_initialized
+
+ # among unitary systems, check supply fan operating mode for constant schedules
+ next unless UnoccupiedOAControlsTest.air_loop_hvac_unitary_system?(air_loop_hvac)
+
+ air_loop_hvac.supplyComponents.each do |component|
+ obj_type = component.iddObjectType.valueName.to_s
+ case obj_type
+ when 'OS_AirLoopHVAC_UnitarySystem'
+ component = component.to_AirLoopHVACUnitarySystem.get
+ when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir'
+ component = component.to_AirLoopHVACUnitaryHeatPump_AirToAir.get
+ when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir_MultiSpeed'
+ component = component.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get
+ when 'OS_AirLoopHVAC_UnitaryHeatCool_VAVChangeoverBypass'
+ component = component.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.get
end
- end
+
+ component.getSupplyAirFanOperatingModeSchedule
+ if setMinimumOutdoorAirSchedule.to_ScheduleConstant.is_initialized
+ no_constant_loop_sched = false
+ end
+ end
end
- assert(no_constant_loop_sched)
+ assert(no_constant_loop_sched)
end
diff --git a/resources/tests/test_template.rb b/resources/tests/test_template.rb
index 65261777e..046edf48a 100644
--- a/resources/tests/test_template.rb
+++ b/resources/tests/test_template.rb
@@ -25,6 +25,7 @@ def epws_for_tests
end
def load_model(osm_path)
+ osm_path = File.expand_path(osm_path)
translator = OpenStudio::OSVersion::VersionTranslator.new
model = translator.loadModel(OpenStudio::Path.new(osm_path))
assert(!model.empty?)