diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml
index 7d1bce4ee..c14f65ae7 100644
--- a/.github/workflows/config.yml
+++ b/.github/workflows/config.yml
@@ -11,7 +11,7 @@ jobs:
build:
runs-on: ubuntu-latest
container:
- image: docker://nrel/openstudio:3.1.0
+ image: docker://nrel/openstudio:develop
steps:
- uses: actions/checkout@v2
diff --git a/Changelog.md b/Changelog.md
index 624ae06c9..43ad6fc79 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,17 +1,21 @@
## OpenStudio-HPXML v1.2.0 (Pending)
__New Features__
+- Updates to OpenStudio 3.2.0/EnergyPlus 9.5.0.
- **Breaking change**: Heating/cooling component loads no longer calculated by default for faster performance; use `--add-component-loads` argument if desired.
- **Breaking change**: Replaces `Site/extension/ShelterCoefficient` with `Site/ShieldingofHome`.
- **Breaking change**: `AirDistributionType` is now required for all air distribution systems.
+- Allows additional fuel types for generators.
- Allows `DuctLeakageMeasurement` & `ConditionedFloorAreaServed` to not be specified for ductless fan coil systems.
- Allows `Slab/ExposedPerimeter` to be zero.
- Removes `ClothesDryer/ControlType` from being a required input, it is not used.
- Switches room air conditioner model to use Cutler performance curves.
+- Adds an `--ep-input-format` argument to run_simulation.rb to choose epJSON as the EnergyPlus input file format instead of IDF.
- Relaxes tolerance for duct leakage to outside warning when ducts solely in conditioned space.
- Moves additional error-checking from the ruby measure to the schematron validator.
__Bugfixes__
+- Improves ground reflectance when there is shading of windows/skylights.
- Fixes room air conditioner performance curve.
- Fixes ruby error if elements (e.g., `SystemIdentifier`) exist without the proper 'id'/'idref' attribute.
- Fixes error if boiler/GSHP pump power is zero
diff --git a/HPXMLtoOpenStudio/measure.rb b/HPXMLtoOpenStudio/measure.rb
index 09de5d105..36a13749c 100644
--- a/HPXMLtoOpenStudio/measure.rb
+++ b/HPXMLtoOpenStudio/measure.rb
@@ -337,11 +337,13 @@ def self.add_simulation_params(model)
def self.update_conditioned_basement(runner, model, spaces)
return if @cond_bsmnt_surfaces.empty?
+
# Update @cond_bsmnt_surfaces to include subsurfaces
new_cond_bsmnt_surfaces = @cond_bsmnt_surfaces.dup
@cond_bsmnt_surfaces.each do |cond_bsmnt_surface|
next if cond_bsmnt_surface.is_a? OpenStudio::Model::InternalMassDefinition
next if cond_bsmnt_surface.subSurfaces.empty?
+
cond_bsmnt_surface.subSurfaces.each do |ss|
new_cond_bsmnt_surfaces << ss
end
@@ -359,6 +361,7 @@ def self.update_solar_absorptances(runner, model)
@cond_bsmnt_surfaces.each do |cond_bsmnt_surface|
# skip windows because windows don't have such property to change.
next if cond_bsmnt_surface.is_a?(OpenStudio::Model::SubSurface) && (cond_bsmnt_surface.subSurfaceType.downcase == 'fixedwindow')
+
adj_surface = nil
if not cond_bsmnt_surface.is_a? OpenStudio::Model::InternalMassDefinition
if not cond_bsmnt_surface.is_a? OpenStudio::Model::SubSurface
@@ -397,6 +400,7 @@ def self.update_solar_absorptances(runner, model)
innermost_material.setSolarAbsorptance(0.0)
innermost_material.setVisibleAbsorptance(0.0)
next if adj_surface.nil?
+
# Create new construction in case of shared construciton.
layered_const_adj = OpenStudio::Model::Construction.new(model)
layered_const_adj.setName(cond_bsmnt_surface.construction.get.name.get + ' Reversed Bsmnt')
@@ -1344,9 +1348,10 @@ def self.add_windows(runner, model, spaces, weather)
shading_group = nil
shading_schedules = {}
+ shading_ems = { sensors: {}, program: nil }
surfaces = []
- @hpxml.windows.each do |window|
+ @hpxml.windows.each_with_index do |window, i|
window_height = 4.0 # ft, default
overhang_depth = nil
@@ -1393,7 +1398,8 @@ def self.add_windows(runner, model, spaces, weather)
# Apply interior/exterior shading (as needed)
shading_vertices = Geometry.create_wall_vertices(window_length, window_height, z_origin, window.azimuth)
- shading_group = Constructions.apply_window_skylight_shading(model, window, shading_vertices, surface, sub_surface, shading_group, shading_schedules, Constants.ObjectNameWindowShade, @cooling_season)
+ shading_group = Constructions.apply_window_skylight_shading(model, window, i, shading_vertices, surface, sub_surface, shading_group,
+ shading_schedules, shading_ems, Constants.ObjectNameWindowShade, @cooling_season)
else
# Window is on an interior surface, which E+ does not allow. Model
# as a door instead so that we can get the appropriate conduction
@@ -1435,8 +1441,9 @@ def self.add_skylights(runner, model, spaces, weather)
shading_group = nil
shading_schedules = {}
+ shading_ems = { sensors: {}, program: nil }
- @hpxml.skylights.each do |skylight|
+ @hpxml.skylights.each_with_index do |skylight, i|
tilt = skylight.roof.pitch / 12.0
width = Math::sqrt(skylight.area)
length = skylight.area / width
@@ -1467,7 +1474,8 @@ def self.add_skylights(runner, model, spaces, weather)
# Apply interior/exterior shading (as needed)
shading_vertices = Geometry.create_roof_vertices(length, width, z_origin, skylight.azimuth, tilt)
- shading_group = Constructions.apply_window_skylight_shading(model, skylight, shading_vertices, surface, sub_surface, shading_group, shading_schedules, Constants.ObjectNameSkylightShade, @cooling_season)
+ shading_group = Constructions.apply_window_skylight_shading(model, skylight, i, shading_vertices, surface, sub_surface, shading_group,
+ shading_schedules, shading_ems, Constants.ObjectNameSkylightShade, @cooling_season)
end
apply_adiabatic_construction(runner, model, surfaces, 'roof')
diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml
index 0416c936f..f43582c73 100644
--- a/HPXMLtoOpenStudio/measure.xml
+++ b/HPXMLtoOpenStudio/measure.xml
@@ -3,8 +3,8 @@
3.0
hpxm_lto_openstudio
b1543b30-9465-45ff-ba04-1d1f85e763bc
- 22a33eee-ad90-481f-bdf4-322c249ac6e2
- 20210421T190434Z
+ 61e29072-851e-40a4-b1e2-02610ab3e6f6
+ 20210427T150645Z
D8922A73
HPXMLtoOpenStudio
HPXML to OpenStudio Translator
@@ -301,30 +301,12 @@
resource
5E866DCA
-
- version.rb
- rb
- resource
- 4F621F55
-
location.rb
rb
resource
CB27A3C5
-
- generator.rb
- rb
- resource
- 8094AEAC
-
-
- waterheater.rb
- rb
- resource
- C696219C
-
HPXMLvalidator.xml
xml
@@ -349,12 +331,6 @@
resource
F76E3AB7
-
- pv.rb
- rb
- resource
- 120417ED
-
test_hvac_sizing.rb
rb
@@ -404,76 +380,106 @@
19007457
- test_hvac.rb
+ test_hotwater_appliance.rb
rb
test
- 4D5E86F7
+ E962DB16
- test_hotwater_appliance.rb
+ simcontrols.rb
+ rb
+ resource
+ C18610A9
+
+
+ test_validation.rb
rb
test
- E962DB16
+ E4E8816E
+
+
+ test_defaults.rb
+ rb
+ test
+ 3065BE57
minitest_helper.rb
rb
resource
- 02EC2972
+ FEC94D37
+
+
+ version.rb
+ rb
+ resource
+ FB55E123
+
+
+ generator.rb
+ rb
+ resource
+ FC0A4F2E
+
+
+ waterheater.rb
+ rb
+ resource
+ A9868999
+
+
+ pv.rb
+ rb
+ resource
+ AF98955A
+
+
+ test_hvac.rb
+ rb
+ test
+ 93BE4E7C
EPvalidator.xml
xml
resource
- E88FE4FD
+ 37E36757
test_airflow.rb
rb
test
- 9F818FBE
+ 9219EDE1
test_enclosure.rb
rb
test
- 199ECD03
+ 2A7EF627
constructions.rb
rb
resource
- BD3776F6
-
-
- hvac_sizing.rb
- rb
- resource
- AB9BBAC1
+ 2BDDE11F
energyplus.rb
rb
resource
- 00BB7C11
+ CA9A2E46
- simcontrols.rb
+ hvac_sizing.rb
rb
resource
- C18610A9
-
-
- test_validation.rb
- rb
- test
- E4E8816E
+ ED41663A
- test_defaults.rb
+ util.rb
rb
test
- 3065BE57
+ B185ACE7
weather.rb
@@ -494,34 +500,40 @@
5507213C
- meta_measure.rb
+ hotwater_appliances.rb
rb
resource
- 5523EAA1
+ EF9BC498
- xmlhelper.rb
+ airflow.rb
rb
resource
- EEAB4FC6
+ 73D23AF9
- hotwater_appliances.rb
+ misc_loads.rb
rb
resource
- EF9BC498
+ F085602D
- airflow.rb
+ geometry.rb
rb
resource
- 73D23AF9
+ 19DA45A2
- misc_loads.rb
+ meta_measure.rb
rb
resource
- F085602D
+ F0600D92
+
+
+ xmlhelper.rb
+ rb
+ resource
+ 60F0330A
@@ -532,31 +544,25 @@
measure.rb
rb
script
- ACE8AC03
+ 6D448BA6
- geometry.rb
+ hpxml.rb
rb
resource
- 19DA45A2
+ A829044D
hpxml_defaults.rb
rb
resource
- 19252CCB
-
-
- hpxml.rb
- rb
- resource
- 9504C475
+ 5A575FCF
hvac.rb
rb
resource
- 21E3089F
+ 5737100F
diff --git a/HPXMLtoOpenStudio/resources/EPvalidator.xml b/HPXMLtoOpenStudio/resources/EPvalidator.xml
index 3422e5cf4..5ac297968 100644
--- a/HPXMLtoOpenStudio/resources/EPvalidator.xml
+++ b/HPXMLtoOpenStudio/resources/EPvalidator.xml
@@ -1286,7 +1286,7 @@
Expected 1 element(s) for xpath: SystemIdentifier
Expected 0 or 1 element(s) for xpath: IsSharedSystem
Expected 1 element(s) for xpath: FuelType
- Expected FuelType to be 'natural gas' or 'propane'
+ Expected FuelType to be 'natural gas' or 'fuel oil' or 'fuel oil 1' or 'fuel oil 2' or 'fuel oil 4' or 'fuel oil 5/6' or 'diesel' or 'propane' or 'kerosene' or 'coal' or 'coke' or 'bituminous coal' or 'anthracite coal' or 'wood' or 'wood pellets'
Expected 1 element(s) for xpath: AnnualConsumptionkBtu
Expected AnnualConsumptionkBtu to be greater than 0
Expected 1 element(s) for xpath: AnnualOutputkWh
diff --git a/HPXMLtoOpenStudio/resources/constructions.rb b/HPXMLtoOpenStudio/resources/constructions.rb
index b0c68b01f..ded646cc8 100644
--- a/HPXMLtoOpenStudio/resources/constructions.rb
+++ b/HPXMLtoOpenStudio/resources/constructions.rb
@@ -1169,8 +1169,8 @@ def self.apply_window_skylight(runner, model, type, subsurface, constr_name, ufa
constr.create_and_assign_constructions(runner, [subsurface], model)
end
- def self.apply_window_skylight_shading(model, window_or_skylight, shading_vertices, parent_surface, sub_surface, shading_group,
- shading_schedules, name, cooling_season)
+ def self.apply_window_skylight_shading(model, window_or_skylight, index, shading_vertices, parent_surface, sub_surface, shading_group,
+ shading_schedules, shading_ems, name, cooling_season)
sf_summer = window_or_skylight.interior_shading_factor_summer * window_or_skylight.exterior_shading_factor_summer
sf_winter = window_or_skylight.interior_shading_factor_winter * window_or_skylight.exterior_shading_factor_winter
if (sf_summer < 1.0) || (sf_winter < 1.0)
@@ -1193,10 +1193,34 @@ def self.apply_window_skylight_shading(model, window_or_skylight, shading_vertic
end
shading_surface.setTransmittanceSchedule(shading_schedules[trans_values].schedule)
- # Adjustment to default view factor is used to reduce ground diffuse solar
- avg_trans_value = trans_values.sum(0.0) / 12.0 # FUTURE: Create EnergyPlus actuator to adjust this
- default_vf_to_ground = ((1.0 - Math::cos(parent_surface.tilt)) / 2.0).round(2)
- sub_surface.setViewFactortoGround(default_vf_to_ground * avg_trans_value)
+ # EMS to actuate view factor to ground
+ sub_surface_type = sub_surface.subSurfaceType.downcase.to_s
+ actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(sub_surface, *EPlus::EMSActuatorSurfaceViewFactorToGround)
+ actuator.setName("#{sub_surface_type}#{index}_actuator")
+
+ if shading_ems[:sensors][trans_values].nil?
+ shading_schedule_name = shading_schedules[trans_values].schedule.name.to_s
+ shading_coeff_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
+ shading_coeff_sensor.setName("#{sub_surface_type}_shading_coefficient")
+ shading_coeff_sensor.setKeyName(shading_schedule_name)
+ shading_ems[:sensors][trans_values] = shading_coeff_sensor
+ end
+
+ default_vf_to_ground = ((1.0 - Math::cos(sub_surface.tilt)) / 2.0).round(2)
+ shading_coeff = shading_ems[:sensors][trans_values].name
+ if shading_ems[:program].nil?
+ program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
+ program.setName("#{sub_surface_type}_view_factor_to_ground_program")
+ program.addLine("Set #{actuator.name} = #{default_vf_to_ground}*#{shading_coeff}")
+ shading_ems[:program] = program
+
+ program_cm = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
+ program_cm.setName("#{program.name} calling manager")
+ program_cm.setCallingPoint('BeginZoneTimestepAfterInitHeatBalance') # https://github.com/NREL/EnergyPlus/pull/8477#discussion_r567320478
+ program_cm.addProgram(program)
+ else
+ shading_ems[:program].addLine("Set #{actuator.name} = #{default_vf_to_ground}*#{shading_coeff}")
+ end
if shading_group.nil?
shading_group = OpenStudio::Model::ShadingSurfaceGroup.new(model)
diff --git a/HPXMLtoOpenStudio/resources/energyplus.rb b/HPXMLtoOpenStudio/resources/energyplus.rb
index 3dd6b3abf..3e445acb3 100644
--- a/HPXMLtoOpenStudio/resources/energyplus.rb
+++ b/HPXMLtoOpenStudio/resources/energyplus.rb
@@ -7,6 +7,7 @@ class EPlus
EMSActuatorPumpMassFlowRate = 'Pump', 'Pump Mass Flow Rate'
EMSActuatorPumpPressureRise = 'Pump', 'Pump Pressure Rise'
EMSActuatorScheduleConstantValue = 'Schedule:Constant', 'Schedule Value'
+ EMSActuatorSurfaceViewFactorToGround = 'Surface', 'View Factor To Ground'
EMSActuatorZoneInfiltrationFlowRate = 'Zone Infiltration', 'Air Exchange Flow Rate'
EMSActuatorZoneMixingFlowRate = 'ZoneMixing', 'Air Exchange Flow Rate'
EMSIntVarFanMFR = 'Fan Maximum Mass Flow Rate'
diff --git a/HPXMLtoOpenStudio/resources/generator.rb b/HPXMLtoOpenStudio/resources/generator.rb
index 7485ca64d..366f3fd10 100644
--- a/HPXMLtoOpenStudio/resources/generator.rb
+++ b/HPXMLtoOpenStudio/resources/generator.rb
@@ -10,6 +10,7 @@ def self.apply(model, nbeds, generator)
else
# Apportion to single dwelling unit by # bedrooms
fail if generator.number_of_bedrooms_served.to_f <= nbeds.to_f # EPvalidator.xml should prevent this
+
annual_consumption_kbtu = generator.annual_consumption_kbtu * nbeds.to_f / generator.number_of_bedrooms_served.to_f
annual_output_kwh = generator.annual_output_kwh * nbeds.to_f / generator.number_of_bedrooms_served.to_f
end
@@ -40,7 +41,7 @@ def self.apply(model, nbeds, generator)
gmt.setElectricalEfficiencyFunctionofTemperatureCurve(curve_cubic_constant)
gmt.setElectricalEfficiencyFunctionofPartLoadRatioCurve(curve_cubic_constant)
- elcd = gmt.electricLoadCenterDistribution.get
+ elcd = OpenStudio::Model::ElectricLoadCenterDistribution.new(model)
elcd.setName("#{obj_name} elec load center dist")
elcd.setGeneratorOperationSchemeType('Baseload')
elcd.addGenerator(gmt)
diff --git a/HPXMLtoOpenStudio/resources/hpxml.rb b/HPXMLtoOpenStudio/resources/hpxml.rb
index 672a92872..a48bc573a 100644
--- a/HPXMLtoOpenStudio/resources/hpxml.rb
+++ b/HPXMLtoOpenStudio/resources/hpxml.rb
@@ -798,6 +798,7 @@ def check_for_errors
end
end
next unless (not end_day.nil?) && (months.include? end_month)
+
if not valid_days.include? end_day
errors << "#{sim_ctl} End Day of Month (#{end_day}) must be one of: #{valid_days.join(', ')}."
end
@@ -807,12 +808,14 @@ def check_for_errors
{ 'Run Period' => [@sim_begin_month, @sim_begin_day, @sim_end_month, @sim_end_day] }.each do |sim_ctl, months_and_days|
begin_month, begin_day, end_month, end_day = months_and_days
next unless (not begin_month.nil?) && (not end_month.nil?)
+
if begin_month > end_month
errors << "#{sim_ctl} Begin Month (#{begin_month}) cannot come after #{sim_ctl} End Month (#{end_month})."
end
next unless (not begin_day.nil?) && (not end_day.nil?)
next unless begin_month == end_month
+
if begin_day > end_day
errors << "#{sim_ctl} Begin Day of Month (#{begin_day}) cannot come after #{sim_ctl} End Day of Month (#{end_day}) for the same month (#{begin_month})."
end
diff --git a/HPXMLtoOpenStudio/resources/hpxml_defaults.rb b/HPXMLtoOpenStudio/resources/hpxml_defaults.rb
index 209bf7b88..6eac893a3 100644
--- a/HPXMLtoOpenStudio/resources/hpxml_defaults.rb
+++ b/HPXMLtoOpenStudio/resources/hpxml_defaults.rb
@@ -6,7 +6,7 @@ class HPXMLDefaults
# being written to the HPXML file. This is useful to associate additional values
# with the HPXML objects that will ultimately get passed around.
- def self.apply(hpxml, eri_version, weather, epw_file: nil, do_hvac_sizing: true)
+ def self.apply(hpxml, eri_version, weather, epw_file: nil)
cfa = hpxml.building_construction.conditioned_floor_area
nbeds = hpxml.building_construction.number_of_bedrooms
ncfl = hpxml.building_construction.number_of_conditioned_floors
@@ -27,7 +27,7 @@ def self.apply(hpxml, eri_version, weather, epw_file: nil, do_hvac_sizing: true)
apply_slabs(hpxml)
apply_windows(hpxml)
apply_skylights(hpxml)
- apply_hvac(hpxml, weather, do_hvac_sizing)
+ apply_hvac(hpxml, weather)
apply_hvac_control(hpxml)
apply_hvac_distribution(hpxml, ncfl, ncfl_ag)
apply_ventilation_fans(hpxml)
@@ -44,8 +44,6 @@ def self.apply(hpxml, eri_version, weather, epw_file: nil, do_hvac_sizing: true)
apply_pv_systems(hpxml)
apply_generators(hpxml)
- return unless do_hvac_sizing
-
# Do HVAC sizing after all other defaults have been applied
apply_hvac_sizing(hpxml, weather, cfa, nbeds)
end
@@ -425,8 +423,8 @@ def self.apply_skylights(hpxml)
end
end
- def self.apply_hvac(hpxml, weather, do_hvac_sizing)
- HVAC.apply_shared_systems(hpxml, do_hvac_sizing)
+ def self.apply_hvac(hpxml, weather)
+ HVAC.apply_shared_systems(hpxml)
# Default AC/HP compressor type
hpxml.cooling_systems.each do |cooling_system|
@@ -754,6 +752,7 @@ def self.apply_hvac_control(hpxml)
end
next unless not hvac_control.cooling_setup_temp.nil?
+
if hvac_control.cooling_setup_start_hour.nil?
hvac_control.cooling_setup_start_hour = 9 # 9 am
hvac_control.cooling_setup_start_hour_isdefaulted = true
@@ -1376,6 +1375,7 @@ def self.apply_pools_and_hot_tubs(hpxml, cfa, nbeds)
end
next unless pool.heater_type != HPXML::TypeNone
+
# Heater
if pool.heater_load_value.nil?
default_heater_load_units, default_heater_load_value = MiscLoads.get_pool_heater_default_values(cfa, nbeds, pool.heater_type)
@@ -1429,6 +1429,7 @@ def self.apply_pools_and_hot_tubs(hpxml, cfa, nbeds)
end
next unless hot_tub.heater_type != HPXML::TypeNone
+
# Heater
if hot_tub.heater_load_value.nil?
default_heater_load_units, default_heater_load_value = MiscLoads.get_hot_tub_heater_default_values(cfa, nbeds, hot_tub.heater_type)
@@ -1655,6 +1656,22 @@ def self.apply_fuel_loads(hpxml, cfa, nbeds)
end
def self.apply_hvac_sizing(hpxml, weather, cfa, nbeds)
+ # Convert negative values (e.g., -1) to nil as appropriate
+ hpxml.hvac_systems.each do |hvac_system|
+ if hvac_system.respond_to?(:heating_capacity) && hvac_system.heating_capacity.to_f < 0
+ hvac_system.heating_capacity = nil
+ end
+ if hvac_system.respond_to?(:cooling_capacity) && hvac_system.cooling_capacity.to_f < 0
+ hvac_system.cooling_capacity = nil
+ end
+ if hvac_system.respond_to?(:heating_capacity_17F) && hvac_system.heating_capacity_17F.to_f < 0
+ hvac_system.heating_capacity_17F = nil
+ end
+ if hvac_system.respond_to?(:backup_heating_capacity) && hvac_system.backup_heating_capacity.to_f < 0
+ hvac_system.backup_heating_capacity = nil
+ end
+ end
+
hvac_systems = HVAC.get_hpxml_hvac_systems(hpxml)
# Calculate building design loads and equipment capacities/airflows
diff --git a/HPXMLtoOpenStudio/resources/hvac.rb b/HPXMLtoOpenStudio/resources/hvac.rb
index d4545f37b..dd497f45f 100644
--- a/HPXMLtoOpenStudio/resources/hvac.rb
+++ b/HPXMLtoOpenStudio/resources/hvac.rb
@@ -60,6 +60,7 @@ def self.apply_central_air_conditioner_furnace(model, runner, cooling_system, he
if (not cooling_system.nil?) && (not heating_system.nil?) && (cooling_system.fan_watts_per_cfm.to_f != heating_system.fan_watts_per_cfm.to_f)
fail "Fan powers for heating system '#{heating_system.id}' and cooling system '#{cooling_system.id}' are attached to a single distribution system and therefore must be the same."
end
+
if (not cooling_system.nil?) && (not cooling_system.fan_watts_per_cfm.nil?)
fan_watts_per_cfm = cooling_system.fan_watts_per_cfm
else
@@ -413,25 +414,12 @@ def self.apply_ground_to_air_heat_pump(model, runner, weather, heat_pump,
end
# Cooling Coil
- clg_coil = OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit.new(model)
+ clg_total_cap_curve = create_curve_quad_linear(model, hp_ap.cool_cap_ft_spec[0], obj_name + ' clg total cap curve')
+ clg_sens_cap_curve = create_curve_quint_linear(model, hp_ap.cool_sh_ft_spec[0], obj_name + ' clg sens cap curve')
+ clg_power_curve = create_curve_quad_linear(model, hp_ap.cool_power_ft_spec[0], obj_name + ' clg power curve')
+ clg_coil = OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit.new(model, clg_total_cap_curve, clg_sens_cap_curve, clg_power_curve)
clg_coil.setName(obj_name + ' clg coil')
clg_coil.setRatedCoolingCoefficientofPerformance(1.0 / hp_ap.cool_rated_eirs[0])
- clg_coil.setTotalCoolingCapacityCoefficient1(hp_ap.cool_cap_ft_spec[0][0])
- clg_coil.setTotalCoolingCapacityCoefficient2(hp_ap.cool_cap_ft_spec[0][1])
- clg_coil.setTotalCoolingCapacityCoefficient3(hp_ap.cool_cap_ft_spec[0][2])
- clg_coil.setTotalCoolingCapacityCoefficient4(hp_ap.cool_cap_ft_spec[0][3])
- clg_coil.setTotalCoolingCapacityCoefficient5(hp_ap.cool_cap_ft_spec[0][4])
- clg_coil.setSensibleCoolingCapacityCoefficient1(hp_ap.cool_sh_ft_spec[0][0])
- clg_coil.setSensibleCoolingCapacityCoefficient2(hp_ap.cool_sh_ft_spec[0][1])
- clg_coil.setSensibleCoolingCapacityCoefficient3(hp_ap.cool_sh_ft_spec[0][2])
- clg_coil.setSensibleCoolingCapacityCoefficient4(hp_ap.cool_sh_ft_spec[0][3])
- clg_coil.setSensibleCoolingCapacityCoefficient5(hp_ap.cool_sh_ft_spec[0][4])
- clg_coil.setSensibleCoolingCapacityCoefficient6(hp_ap.cool_sh_ft_spec[0][5])
- clg_coil.setCoolingPowerConsumptionCoefficient1(hp_ap.cool_power_ft_spec[0][0])
- clg_coil.setCoolingPowerConsumptionCoefficient2(hp_ap.cool_power_ft_spec[0][1])
- clg_coil.setCoolingPowerConsumptionCoefficient3(hp_ap.cool_power_ft_spec[0][2])
- clg_coil.setCoolingPowerConsumptionCoefficient4(hp_ap.cool_power_ft_spec[0][3])
- clg_coil.setCoolingPowerConsumptionCoefficient5(hp_ap.cool_power_ft_spec[0][4])
clg_coil.setNominalTimeforCondensateRemovaltoBegin(1000)
clg_coil.setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(1.5)
clg_coil.setRatedAirFlowRate(UnitConversions.convert(clg_cfm, 'cfm', 'm^3/s'))
@@ -441,19 +429,11 @@ def self.apply_ground_to_air_heat_pump(model, runner, weather, heat_pump,
hvac_map[heat_pump.id] << clg_coil
# Heating Coil
- htg_coil = OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit.new(model)
+ htg_cap_curve = create_curve_quad_linear(model, hp_ap.heat_cap_ft_spec[0], obj_name + ' htg cap curve')
+ htg_power_curve = create_curve_quad_linear(model, hp_ap.heat_power_ft_spec[0], obj_name + ' htg power curve')
+ htg_coil = OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit.new(model, htg_cap_curve, htg_power_curve)
htg_coil.setName(obj_name + ' htg coil')
htg_coil.setRatedHeatingCoefficientofPerformance(1.0 / hp_ap.heat_rated_eirs[0])
- htg_coil.setHeatingCapacityCoefficient1(hp_ap.heat_cap_ft_spec[0][0])
- htg_coil.setHeatingCapacityCoefficient2(hp_ap.heat_cap_ft_spec[0][1])
- htg_coil.setHeatingCapacityCoefficient3(hp_ap.heat_cap_ft_spec[0][2])
- htg_coil.setHeatingCapacityCoefficient4(hp_ap.heat_cap_ft_spec[0][3])
- htg_coil.setHeatingCapacityCoefficient5(hp_ap.heat_cap_ft_spec[0][4])
- htg_coil.setHeatingPowerConsumptionCoefficient1(hp_ap.heat_power_ft_spec[0][0])
- htg_coil.setHeatingPowerConsumptionCoefficient2(hp_ap.heat_power_ft_spec[0][1])
- htg_coil.setHeatingPowerConsumptionCoefficient3(hp_ap.heat_power_ft_spec[0][2])
- htg_coil.setHeatingPowerConsumptionCoefficient4(hp_ap.heat_power_ft_spec[0][3])
- htg_coil.setHeatingPowerConsumptionCoefficient5(hp_ap.heat_power_ft_spec[0][4])
htg_coil.setRatedAirFlowRate(UnitConversions.convert(htg_cfm, 'cfm', 'm^3/s'))
htg_coil.setRatedWaterFlowRate(UnitConversions.convert(hp_ap.GSHP_Loop_flow, 'gal/min', 'm^3/s'))
htg_coil.setRatedHeatingCapacity(UnitConversions.convert(heat_pump.heating_capacity, 'Btu/hr', 'W'))
@@ -629,7 +609,7 @@ def self.apply_water_loop_to_air_heat_pump(model, runner, heat_pump,
hvac_map[heat_pump.id] << htg_supp_coil
# Fan
- fan_power_installed = 0.5 # FIXME
+ fan_power_installed = 0.0 # Use provided net COP
fan = create_supply_fan(model, obj_name, 1, fan_power_installed, htg_cfm)
hvac_map[heat_pump.id] += disaggregate_fan_or_pump(model, fan, htg_coil, clg_coil, htg_supp_coil)
@@ -2753,19 +2733,19 @@ def self.set_heat_rated_cfm_per_ton(heating_system)
end
def self.create_curve_biquadratic_constant(model)
- const_biquadratic = OpenStudio::Model::CurveBiquadratic.new(model)
- const_biquadratic.setName('ConstantBiquadratic')
- const_biquadratic.setCoefficient1Constant(1)
- const_biquadratic.setCoefficient2x(0)
- const_biquadratic.setCoefficient3xPOW2(0)
- const_biquadratic.setCoefficient4y(0)
- const_biquadratic.setCoefficient5yPOW2(0)
- const_biquadratic.setCoefficient6xTIMESY(0)
- const_biquadratic.setMinimumValueofx(-100)
- const_biquadratic.setMaximumValueofx(100)
- const_biquadratic.setMinimumValueofy(-100)
- const_biquadratic.setMaximumValueofy(100)
- return const_biquadratic
+ curve = OpenStudio::Model::CurveBiquadratic.new(model)
+ curve.setName('ConstantBiquadratic')
+ curve.setCoefficient1Constant(1)
+ curve.setCoefficient2x(0)
+ curve.setCoefficient3xPOW2(0)
+ curve.setCoefficient4y(0)
+ curve.setCoefficient5yPOW2(0)
+ curve.setCoefficient6xTIMESY(0)
+ curve.setMinimumValueofx(-100)
+ curve.setMaximumValueofx(100)
+ curve.setMinimumValueofy(-100)
+ curve.setMaximumValueofy(100)
+ return curve
end
def self.create_curve_quadratic_constant(model)
@@ -2782,15 +2762,15 @@ def self.create_curve_quadratic_constant(model)
end
def self.create_curve_cubic_constant(model)
- constant_cubic = OpenStudio::Model::CurveCubic.new(model)
- constant_cubic.setName('ConstantCubic')
- constant_cubic.setCoefficient1Constant(1)
- constant_cubic.setCoefficient2x(0)
- constant_cubic.setCoefficient3xPOW2(0)
- constant_cubic.setCoefficient4xPOW3(0)
- constant_cubic.setMinimumValueofx(-100)
- constant_cubic.setMaximumValueofx(100)
- return constant_cubic
+ curve = OpenStudio::Model::CurveCubic.new(model)
+ curve.setName('ConstantCubic')
+ curve.setCoefficient1Constant(1)
+ curve.setCoefficient2x(0)
+ curve.setCoefficient3xPOW2(0)
+ curve.setCoefficient4xPOW3(0)
+ curve.setMinimumValueofx(-100)
+ curve.setMaximumValueofx(100)
+ return curve
end
def self.convert_curve_biquadratic(coeff, ip_to_si = true)
@@ -2899,6 +2879,29 @@ def self.create_curve_exponent(model, coeff, name, min_x, max_x)
return curve
end
+ def self.create_curve_quad_linear(model, coeff, name)
+ curve = OpenStudio::Model::CurveQuadLinear.new(model)
+ curve.setName(name)
+ curve.setCoefficient1Constant(coeff[0])
+ curve.setCoefficient2w(coeff[1])
+ curve.setCoefficient3x(coeff[2])
+ curve.setCoefficient4y(coeff[3])
+ curve.setCoefficient5z(coeff[4])
+ return curve
+ end
+
+ def self.create_curve_quint_linear(model, coeff, name)
+ curve = OpenStudio::Model::CurveQuintLinear.new(model)
+ curve.setName(name)
+ curve.setCoefficient1Constant(coeff[0])
+ curve.setCoefficient2v(coeff[1])
+ curve.setCoefficient3w(coeff[2])
+ curve.setCoefficient4x(coeff[3])
+ curve.setCoefficient5y(coeff[4])
+ curve.setCoefficient6z(coeff[5])
+ return curve
+ end
+
def self.create_dx_cooling_coil(model, obj_name, cooling_system)
clg_ap = cooling_system.additional_properties
@@ -3971,9 +3974,9 @@ def self.get_default_gshp_pump_power()
return 30.0 # W/ton, per ANSI/RESNET/ICC 301-2019 Section 4.4.5 (closed loop)
end
- def self.apply_shared_systems(hpxml, do_hvac_sizing = true)
- applied_clg = apply_shared_cooling_systems(hpxml, do_hvac_sizing)
- applied_htg = apply_shared_heating_systems(hpxml, do_hvac_sizing)
+ def self.apply_shared_systems(hpxml = true)
+ applied_clg = apply_shared_cooling_systems(hpxml)
+ applied_htg = apply_shared_heating_systems(hpxml)
return unless (applied_clg || applied_htg)
# Remove WLHP if not serving heating nor cooling
@@ -4000,7 +4003,7 @@ def self.apply_shared_systems(hpxml, do_hvac_sizing = true)
end
end
- def self.apply_shared_cooling_systems(hpxml, do_hvac_sizing)
+ def self.apply_shared_cooling_systems(hpxml)
applied = false
hpxml.cooling_systems.each do |cooling_system|
next unless cooling_system.is_shared_system
@@ -4058,12 +4061,8 @@ def self.apply_shared_cooling_systems(hpxml, do_hvac_sizing)
cooling_system.cooling_system_type = HPXML::HVACTypeCentralAirConditioner
cooling_system.cooling_efficiency_seer = seer_eq.round(2)
cooling_system.cooling_efficiency_kw_per_ton = nil
- if do_hvac_sizing
- cooling_system.cooling_capacity = nil # Autosize the equipment
- else
- cooling_system.cooling_capacity = -1 # Autosize the equipment
- end
- cooling_system.is_shared_system = nil
+ cooling_system.cooling_capacity = nil # Autosize the equipment
+ cooling_system.is_shared_system = false
cooling_system.number_of_units_served = nil
cooling_system.shared_loop_watts = nil
cooling_system.shared_loop_motor_efficiency = nil
@@ -4113,7 +4112,7 @@ def self.apply_shared_cooling_systems(hpxml, do_hvac_sizing)
return applied
end
- def self.apply_shared_heating_systems(hpxml, do_hvac_sizing)
+ def self.apply_shared_heating_systems(hpxml)
applied = false
hpxml.heating_systems.each do |heating_system|
next unless heating_system.is_shared_system
@@ -4141,11 +4140,7 @@ def self.apply_shared_heating_systems(hpxml, do_hvac_sizing)
heating_system.fraction_heat_load_served = fraction_heat_load_served * (1.0 - 1.0 / wlhp.heating_efficiency_cop)
end
- if do_hvac_sizing
- heating_system.heating_capacity = nil # Autosize the equipment
- else
- heating_system.heating_capacity = -1 # Autosize the equipment
- end
+ heating_system.heating_capacity = nil # Autosize the equipment
end
return applied
diff --git a/HPXMLtoOpenStudio/resources/hvac_sizing.rb b/HPXMLtoOpenStudio/resources/hvac_sizing.rb
index d9529c303..a0bb91a01 100644
--- a/HPXMLtoOpenStudio/resources/hvac_sizing.rb
+++ b/HPXMLtoOpenStudio/resources/hvac_sizing.rb
@@ -890,6 +890,7 @@ def self.process_load_floors(bldg_design_loads, weather)
sum_ua_wall += (1.0 / wall.insulation_assembly_r_value * wall.net_area)
end
fail 'Could not find connected walls.' if sum_a_wall <= 0
+
u_wall = sum_ua_wall / sum_a_wall
# Calculate partition temperature different cooling (PTDC) per Manual J Figure A12-17
@@ -1434,7 +1435,6 @@ def self.apply_hvac_equipment_adjustments(hvac_sizing_values, weather, hvac, cfa
hvac_sizing_values.Cool_Capacity = (hvac_sizing_values.Cool_Load_Tot / totalCap_CurveValue)
hvac_sizing_values.Cool_Capacity_Sens = hvac_sizing_values.Cool_Capacity * hvac.SHRRated[hvac.SizingSpeed]
- # FIXME: Why not use calc_airflow_rate?
hvac_sizing_values.Cool_Airflow = hvac.RatedCFMperTonCooling[-1] * hvac.CapacityRatioCooling[-1] * UnitConversions.convert(hvac_sizing_values.Cool_Capacity, 'Btu/hr', 'ton')
elsif hvac.CoolType == HPXML::HVACTypeRoomAirConditioner
@@ -1444,7 +1444,6 @@ def self.apply_hvac_equipment_adjustments(hvac_sizing_values, weather, hvac, cfa
hvac_sizing_values.Cool_Capacity = hvac_sizing_values.Cool_Load_Tot / totalCap_CurveValue
hvac_sizing_values.Cool_Capacity_Sens = hvac_sizing_values.Cool_Capacity * hvac.SHRRated[hvac.SizingSpeed]
- # FIXME: Why not use calc_airflow_rate?
hvac_sizing_values.Cool_Airflow = hvac.RatedCFMperTonCooling[hvac.SizingSpeed] * UnitConversions.convert(hvac_sizing_values.Cool_Capacity, 'Btu/hr', 'ton')
elsif hvac.CoolType == HPXML::HVACTypeHeatPumpGroundToAir
@@ -1543,7 +1542,6 @@ def self.apply_hvac_equipment_adjustments(hvac_sizing_values, weather, hvac, cfa
hvac_sizing_values.Heat_Capacity = hvac_sizing_values.Heat_Load
hvac_sizing_values.Heat_Capacity_Supp = hvac_sizing_values.Heat_Load
end
- # FIXME: Why not use calc_airflow_rate?
hvac_sizing_values.Heat_Airflow = hvac.RatedCFMperTonHeating[-1] * hvac.CapacityRatioHeating[-1] * UnitConversions.convert(hvac_sizing_values.Heat_Capacity, 'Btu/hr', 'ton') # Maximum air flow under heating operation
elsif hvac.HeatType == HPXML::HVACTypeHeatPumpGroundToAir
diff --git a/HPXMLtoOpenStudio/resources/meta_measure.rb b/HPXMLtoOpenStudio/resources/meta_measure.rb
index 406727e4b..f5368942c 100644
--- a/HPXMLtoOpenStudio/resources/meta_measure.rb
+++ b/HPXMLtoOpenStudio/resources/meta_measure.rb
@@ -3,7 +3,8 @@
require 'fileutils'
def run_hpxml_workflow(rundir, measures, measures_dir, debug: false, output_vars: [],
- output_meters: [], run_measures_only: false, print_prefix: '')
+ output_meters: [], run_measures_only: false, print_prefix: '',
+ ep_input_format: 'idf')
rm_path(rundir)
FileUtils.mkdir_p(rundir)
@@ -47,10 +48,10 @@ def run_hpxml_workflow(rundir, measures, measures_dir, debug: false, output_vars
om.setReportingFrequency(output_meter[1])
end
- # Translate model to IDF
+ # Translate model to workspace
forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new
forward_translator.setExcludeLCCObjects(true)
- model_idf = forward_translator.translateModel(model)
+ workspace = forward_translator.translateModel(model)
success = report_ft_errors_warnings(forward_translator, rundir)
if not success
@@ -60,16 +61,25 @@ def run_hpxml_workflow(rundir, measures, measures_dir, debug: false, output_vars
end
# Apply reporting measure output requests
- apply_energyplus_output_requests(measures_dir, measures, runner, model, model_idf)
-
- # Write IDF to file
- File.open(File.join(rundir, 'in.idf'), 'w') { |f| f << model_idf.to_s }
+ apply_energyplus_output_requests(measures_dir, measures, runner, model, workspace)
+
+ # Write to file
+ if ep_input_format == 'idf'
+ ep_input_filename = 'in.idf'
+ File.open(File.join(rundir, ep_input_filename), 'w') { |f| f << workspace.to_s }
+ elsif ep_input_format == 'epjson'
+ ep_input_filename = 'in.epJSON'
+ json = OpenStudio::EPJSON::toJSONString(workspace.toIdfFile)
+ File.open(File.join(rundir, ep_input_filename), 'w') { |f| f << json.to_s }
+ else
+ fail "Unexpected ep_input_format: #{ep_input_format}."
+ end
# Run simulation
print "#{print_prefix}Running simulation...\n"
ep_path = File.absolute_path(File.join(OpenStudio.getOpenStudioCLI.to_s, '..', '..', 'EnergyPlus', 'energyplus')) # getEnergyPlusDirectory can be unreliable, using getOpenStudioCLI instead
simulation_start = Time.now
- command = "\"#{ep_path}\" -w \"#{model.getWeatherFile.path.get}\" in.idf"
+ command = "\"#{ep_path}\" -w \"#{model.getWeatherFile.path.get}\" #{ep_input_filename}"
if debug
File.open(File.join(rundir, 'run.log'), 'a') do |f|
f << "Executing command '#{command}' from working directory '#{rundir}'.\n"
@@ -159,7 +169,7 @@ def apply_measures(measures_dir, measures, runner, model, show_measure_calls = t
return true
end
-def apply_energyplus_output_requests(measures_dir, measures, runner, model, model_idf)
+def apply_energyplus_output_requests(measures_dir, measures, runner, model, workspace)
# Call each measure in the specified order
measures.keys.each do |measure_subdir|
# Gather measure arguments and call measure
@@ -173,7 +183,7 @@ def apply_energyplus_output_requests(measures_dir, measures, runner, model, mode
runner.setLastOpenStudioModel(model)
idf_objects = measure.energyPlusOutputRequests(runner, argument_map)
idf_objects.each do |idf_object|
- model_idf.addObject(idf_object)
+ workspace.addObject(idf_object)
end
end
end
@@ -433,6 +443,7 @@ def report_os_warnings(os_log, rundir)
next if s.logMessage.include? 'WorkflowStepResult value called with undefined stepResult'
next if s.logMessage.include?("Object of type 'Schedule:Constant' and named 'Always") && s.logMessage.include?('points to an object named') && s.logMessage.include?('but that object cannot be located')
next if s.logMessage.include? 'Appears there are no design condition fields in the EPW file'
+ next if s.logMessage.include?('Using EnergyPlusVersion version') && s.logMessage.include?("which should have 'Year' field, but it's always zero")
f << "OS Message: #{s.logMessage}\n"
end
diff --git a/HPXMLtoOpenStudio/resources/minitest_helper.rb b/HPXMLtoOpenStudio/resources/minitest_helper.rb
index dfbf313d0..37d44b0e9 100644
--- a/HPXMLtoOpenStudio/resources/minitest_helper.rb
+++ b/HPXMLtoOpenStudio/resources/minitest_helper.rb
@@ -1,16 +1,8 @@
# frozen_string_literal: true
-called_from_cli = true
-begin
- OpenStudio.getOpenStudioCLI
-rescue
- called_from_cli = false
-end
-
require 'minitest/autorun'
-if not called_from_cli # cli can't load minitest-reporters gem
- require 'minitest/reporters'
+require 'minitest/reporters'
+require 'minitest/reporters/spec_reporter' # Needed when run via OS CLI
- Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # spec-like progress
-end
+Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # spec-like progress
diff --git a/HPXMLtoOpenStudio/resources/pv.rb b/HPXMLtoOpenStudio/resources/pv.rb
index a38f0926e..f9c8b55a4 100644
--- a/HPXMLtoOpenStudio/resources/pv.rb
+++ b/HPXMLtoOpenStudio/resources/pv.rb
@@ -9,6 +9,7 @@ def self.apply(model, nbeds, pv_system)
else
# Apportion to single dwelling unit by # bedrooms
fail if pv_system.number_of_bedrooms_served.to_f <= nbeds.to_f # EPvalidator.xml should prevent this
+
max_power = pv_system.max_power_output * nbeds.to_f / pv_system.number_of_bedrooms_served.to_f
end
@@ -40,7 +41,7 @@ def self.apply(model, nbeds, pv_system)
ipvwatts.setName("#{obj_name} inverter")
ipvwatts.setInverterEfficiency(pv_system.inverter_efficiency)
- elcd = gpvwatts.electricLoadCenterDistribution.get
+ elcd = OpenStudio::Model::ElectricLoadCenterDistribution.new(model)
elcd.setName("#{obj_name} elec load center dist")
elcd.addGenerator(gpvwatts)
elcd.setInverter(ipvwatts)
diff --git a/HPXMLtoOpenStudio/resources/version.rb b/HPXMLtoOpenStudio/resources/version.rb
index 6b0bb01ec..debc56a69 100644
--- a/HPXMLtoOpenStudio/resources/version.rb
+++ b/HPXMLtoOpenStudio/resources/version.rb
@@ -2,7 +2,7 @@
class Version
OS_HPXML_Version = '1.1.0' # Version of the OS-HPXML workflow
- OS_Version = '3.1.0' # Required version of OpenStudio (can be 'X.X' or 'X.X.X')
+ OS_Version = '3.2.0' # Required version of OpenStudio (can be 'X.X' or 'X.X.X')
HPXML_Version = '3.0' # HPXML schemaVersion
def self.check_openstudio_version
diff --git a/HPXMLtoOpenStudio/resources/waterheater.rb b/HPXMLtoOpenStudio/resources/waterheater.rb
index fa44ad732..a10c3690b 100644
--- a/HPXMLtoOpenStudio/resources/waterheater.rb
+++ b/HPXMLtoOpenStudio/resources/waterheater.rb
@@ -183,6 +183,7 @@ def self.apply_combi(model, runner, loc_space, loc_schedule, water_heating_syste
if water_heating_system.standby_loss <= 0
fail 'A negative indirect water heater standby loss was calculated, double check water heater inputs.'
end
+
act_vol = calc_storage_tank_actual_vol(water_heating_system.tank_volume, nil)
a_side = calc_tank_areas(act_vol)[1]
ua = calc_indirect_ua_with_standbyloss(act_vol, water_heating_system, a_side, solar_fraction)
diff --git a/HPXMLtoOpenStudio/resources/xmlhelper.rb b/HPXMLtoOpenStudio/resources/xmlhelper.rb
index 7ba0d1320..1e2b5848b 100644
--- a/HPXMLtoOpenStudio/resources/xmlhelper.rb
+++ b/HPXMLtoOpenStudio/resources/xmlhelper.rb
@@ -62,6 +62,7 @@ def self.get_value(parent, element_name, datatype)
if element.nil?
return
end
+
value = element.text
if datatype == :integer
diff --git a/HPXMLtoOpenStudio/tests/test_airflow.rb b/HPXMLtoOpenStudio/tests/test_airflow.rb
index ce44eb3c0..3d74db151 100644
--- a/HPXMLtoOpenStudio/tests/test_airflow.rb
+++ b/HPXMLtoOpenStudio/tests/test_airflow.rb
@@ -6,38 +6,13 @@
require 'fileutils'
require_relative '../measure.rb'
require_relative '../resources/util.rb'
+require_relative 'util.rb'
class HPXMLtoOpenStudioAirflowTest < MiniTest::Test
def sample_files_dir
return File.join(File.dirname(__FILE__), '..', '..', 'workflow', 'sample_files')
end
- def get_ems_values(ems_objects, name)
- values = {}
- ems_objects.each do |ems_object|
- next unless ems_object.name.to_s.include? name.gsub(' ', '_')
-
- ems_object.lines.each do |line|
- next unless line.downcase.start_with? 'set'
-
- lhs, rhs = line.split('=')
- lhs = lhs.gsub('Set', '').gsub('set', '').strip
- rhs = rhs.gsub(',', '').gsub(';', '').strip
- values[lhs] = [] if values[lhs].nil?
- # eg. "Q = Q + 1.5"
- if rhs.include? '+'
- rhs_els = rhs.split('+')
- rhs = rhs_els.map { |s| s.to_f }.sum(0.0)
- else
- rhs = rhs.to_f
- end
- values[lhs] << rhs
- end
- end
- assert_operator(values.size, :>, 0)
- return values
- end
-
def get_eed_for_ventilation(model, ee_name)
eeds = []
model.getElectricEquipmentDefinitions.each do |eed|
diff --git a/HPXMLtoOpenStudio/tests/test_enclosure.rb b/HPXMLtoOpenStudio/tests/test_enclosure.rb
index d5e4ee523..c4d8b223f 100644
--- a/HPXMLtoOpenStudio/tests/test_enclosure.rb
+++ b/HPXMLtoOpenStudio/tests/test_enclosure.rb
@@ -6,6 +6,7 @@
require 'fileutils'
require_relative '../measure.rb'
require_relative '../resources/util.rb'
+require_relative 'util.rb'
class HPXMLtoOpenStudioEnclosureTest < MiniTest::Test
def sample_files_dir
@@ -33,12 +34,9 @@ def test_windows_shading
model, hpxml = _test_measure(args_hash)
summer_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('June'), 1, model.yearDescription.get.assumedYear)
- summer_length = 6 # 6 months for Denver
winter_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('January'), 1, model.yearDescription.get.assumedYear)
- winter_length = 12 - summer_length # months
hpxml.windows.each do |window|
- os_window = model.getSubSurfaces.select { |w| w.name.to_s == window.id }[0]
sf_summer = window.interior_shading_factor_summer
sf_winter = window.interior_shading_factor_winter
sf_summer *= window.exterior_shading_factor_summer unless window.exterior_shading_factor_summer.nil?
@@ -56,10 +54,11 @@ def test_windows_shading
assert_equal(sf_winter, winter_transmittance)
end
- # Check view factor for ground diffuse
- default_view_factor = 0.5 # Default for vertical wall
- view_factor = default_view_factor * (sf_summer * summer_length + sf_winter * winter_length) / 12.0
- assert_in_delta(view_factor, os_window.viewFactortoGround.get, 0.001)
+ # Check subsurface view factor to ground
+ subsurface_view_factor = 0.5
+ window_actuator = model.getEnergyManagementSystemActuators.select { |w| w.actuatedComponent.get.name.to_s == window.id }[0]
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, 'fixedwindow view factor to ground program')
+ assert_equal(subsurface_view_factor, program_values["#{window_actuator.name.to_s}"][0])
end
end
@@ -84,12 +83,9 @@ def test_skylights_shading
model, hpxml = _test_measure(args_hash)
summer_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('June'), 1, model.yearDescription.get.assumedYear)
- summer_length = 6 # 6 months for Denver
winter_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('January'), 1, model.yearDescription.get.assumedYear)
- winter_length = 12 - summer_length # months
hpxml.skylights.each do |skylight|
- os_window = model.getSubSurfaces.select { |w| w.name.to_s == skylight.id }[0]
sf_summer = skylight.interior_shading_factor_summer
sf_winter = skylight.interior_shading_factor_winter
sf_summer *= skylight.exterior_shading_factor_summer unless skylight.exterior_shading_factor_summer.nil?
@@ -107,10 +103,11 @@ def test_skylights_shading
assert_equal(sf_winter, winter_transmittance)
end
- # Check view factor for ground diffuse
- default_view_factor = 0.05 # Based on 6:12 pitch in HPXML
- view_factor = default_view_factor * (sf_summer * summer_length + sf_winter * winter_length) / 12.0
- assert_in_delta(view_factor, os_window.viewFactortoGround.get, 0.001)
+ # Check subsurface view factor to ground
+ subsurface_view_factor = 0.05 # 6:12 pitch
+ skylight_actuator = model.getEnergyManagementSystemActuators.select { |w| w.actuatedComponent.get.name.to_s == skylight.id }[0]
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, 'skylight view factor to ground program')
+ assert_equal(subsurface_view_factor, program_values["#{skylight_actuator.name.to_s}"][0])
end
end
diff --git a/HPXMLtoOpenStudio/tests/test_hvac.rb b/HPXMLtoOpenStudio/tests/test_hvac.rb
index 354edee04..d07c53607 100644
--- a/HPXMLtoOpenStudio/tests/test_hvac.rb
+++ b/HPXMLtoOpenStudio/tests/test_hvac.rb
@@ -6,6 +6,7 @@
require 'fileutils'
require_relative '../measure.rb'
require_relative '../resources/util.rb'
+require_relative 'util.rb'
class HPXMLtoOpenStudioHVACTest < MiniTest::Test
def sample_files_dir
@@ -31,7 +32,7 @@ def test_central_air_conditioner_1_speed
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -56,7 +57,7 @@ def test_central_air_conditioner_2_speed
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -81,7 +82,7 @@ def test_central_air_conditioner_var_speed
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -271,7 +272,7 @@ def test_air_to_air_heat_pump_1_speed
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -314,7 +315,7 @@ def test_air_to_air_heat_pump_2_speed
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -357,7 +358,7 @@ def test_air_to_air_heat_pump_var_speed
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -397,7 +398,7 @@ def test_mini_split_heat_pump
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -422,7 +423,7 @@ def test_mini_split_air_conditioner
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -461,7 +462,7 @@ def test_ground_to_air_heat_pump
# Check EMS
assert_equal(1, model.getAirLoopHVACUnitarySystems.size)
unitary_system = model.getAirLoopHVACUnitarySystems[0]
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
assert(program_values.empty?) # Check no EMS program
end
@@ -673,7 +674,7 @@ def test_install_quality_air_to_air_heat_pump_1_speed_ratio
assert_in_epsilon(fan_watts_cfm, fanonoff.pressureRise / fanonoff.fanEfficiency * UnitConversions.convert(1.0, 'cfm', 'm^3/s'), 0.01)
# Check installation quality EMS
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
# defect ratios in EMS is calculated correctly
assert_in_epsilon(program_values['F_CH'].sum, charge_defect, 0.01)
@@ -731,7 +732,7 @@ def test_install_quality_furnace_central_air_conditioner_1_speed_ratio
assert_in_epsilon(fan_watts_cfm2, fanonoff.pressureRise / fanonoff.fanEfficiency * UnitConversions.convert(1.0, 'cfm', 'm^3/s'), 0.01)
# Check installation quality EMS
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
# defect ratios in EMS is calculated correctly
assert_in_epsilon(program_values['F_CH'].sum, charge_defect, 0.01)
@@ -840,31 +841,6 @@ def _test_measure(args_hash)
return model, hpxml
end
- def _get_ems_values(ems_objects, name)
- values = {}
- ems_objects.each do |ems_object|
- next unless ems_object.name.to_s.include? name.gsub(' ', '_')
-
- ems_object.lines.each do |line|
- next unless line.downcase.start_with? 'set'
-
- lhs, rhs = line.split('=')
- lhs = lhs.gsub('Set', '').gsub('set', '').strip
- rhs = rhs.gsub(',', '').gsub(';', '').strip
- values[lhs] = [] if values[lhs].nil?
- # eg. "Q = Q + 1.5"
- if rhs.include? '+'
- rhs_els = rhs.split('+')
- rhs = rhs_els.map { |s| s.to_f }.sum(0.0)
- else
- rhs = rhs.to_f
- end
- values[lhs] << rhs
- end
- end
- return values
- end
-
def _check_install_quality_multispeed_ratio(hpxml_clg_sys, model, hpxml_htg_sys = nil)
airflow_defect = hpxml_clg_sys.airflow_defect_ratio
charge_defect = hpxml_clg_sys.charge_defect_ratio
@@ -891,7 +867,7 @@ def _check_install_quality_multispeed_ratio(hpxml_clg_sys, model, hpxml_htg_sys
assert_in_epsilon(fan_watts_cfm, fanonoff.pressureRise / fanonoff.fanEfficiency * UnitConversions.convert(1.0, 'cfm', 'm^3/s'), 0.01)
# Check installation quality EMS
- program_values = _get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
+ program_values = get_ems_values(model.getEnergyManagementSystemPrograms, "#{unitary_system.name} install quality")
clg_speed_cfms = clg_ratios.map { |ratio| cooling_cfm * ratio }
assert_in_epsilon(program_values['F_CH'].sum, charge_defect, 0.01)
assert_in_epsilon(program_values['FF_AF_c'].sum, clg_speed_cfms.zip(rated_airflow_cfm_clg).map { |cfm, rated_cfm| cfm / rated_cfm }.sum, 0.01)
diff --git a/HPXMLtoOpenStudio/tests/util.rb b/HPXMLtoOpenStudio/tests/util.rb
new file mode 100644
index 000000000..4714d34d8
--- /dev/null
+++ b/HPXMLtoOpenStudio/tests/util.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+def get_ems_values(ems_objects, name)
+ values = {}
+ ems_objects.each do |ems_object|
+ next unless ems_object.name.to_s.include? name.gsub(' ', '_')
+
+ ems_object.lines.each do |line|
+ next unless line.downcase.start_with? 'set'
+
+ lhs, rhs = line.split('=')
+ lhs = lhs.gsub('Set', '').gsub('set', '').strip
+ rhs = rhs.gsub(',', '').gsub(';', '').strip
+ values[lhs] = [] if values[lhs].nil?
+ # eg. "Q = Q + 1.5"
+ if rhs.include? '+'
+ rhs_els = rhs.split('+')
+ rhs = rhs_els.map { |s| s.to_f }.sum(0.0)
+ else
+ rhs = rhs.to_f
+ end
+ values[lhs] << rhs
+ end
+ end
+ return values
+end
diff --git a/SimulationOutputReport/measure.rb b/SimulationOutputReport/measure.rb
index ce103744c..e2808dd99 100644
--- a/SimulationOutputReport/measure.rb
+++ b/SimulationOutputReport/measure.rb
@@ -2146,6 +2146,7 @@ def get_timeseries_units_from_fuel_type(fuel_type)
@end_uses[[FT::Oil, EUT::Lighting]] = EndUse.new(meters: ["#{Constants.ObjectNameMiscLighting}:InteriorEquipment:#{EPlus::FuelTypeOil}"])
@end_uses[[FT::Oil, EUT::Fireplace]] = EndUse.new(meters: ["#{Constants.ObjectNameMiscFireplace}:InteriorEquipment:#{EPlus::FuelTypeOil}"])
end
+ @end_uses[[FT::Oil, EUT::Generator]] = EndUse.new(meters: ["Cogeneration:#{EPlus::FuelTypeOil}"])
@end_uses[[FT::Propane, EUT::Heating]] = EndUse.new(variables: OutputVars.SpaceHeating(EPlus::FuelTypePropane))
@end_uses[[FT::Propane, EUT::HotWater]] = EndUse.new(variables: OutputVars.WaterHeating(EPlus::FuelTypePropane))
@end_uses[[FT::Propane, EUT::ClothesDryer]] = EndUse.new(meters: ["#{Constants.ObjectNameClothesDryer}:InteriorEquipment:#{EPlus::FuelTypePropane}"])
@@ -2167,6 +2168,7 @@ def get_timeseries_units_from_fuel_type(fuel_type)
@end_uses[[FT::WoodCord, EUT::Lighting]] = EndUse.new(meters: ["#{Constants.ObjectNameMiscLighting}:InteriorEquipment:#{EPlus::FuelTypeWoodCord}"])
@end_uses[[FT::WoodCord, EUT::Fireplace]] = EndUse.new(meters: ["#{Constants.ObjectNameMiscFireplace}:InteriorEquipment:#{EPlus::FuelTypeWoodCord}"])
end
+ @end_uses[[FT::WoodCord, EUT::Generator]] = EndUse.new(meters: ["Cogeneration:#{EPlus::FuelTypeWoodCord}"])
@end_uses[[FT::WoodPellets, EUT::Heating]] = EndUse.new(variables: OutputVars.SpaceHeating(EPlus::FuelTypeWoodPellets))
@end_uses[[FT::WoodPellets, EUT::HotWater]] = EndUse.new(variables: OutputVars.WaterHeating(EPlus::FuelTypeWoodPellets))
@end_uses[[FT::WoodPellets, EUT::ClothesDryer]] = EndUse.new(meters: ["#{Constants.ObjectNameClothesDryer}:InteriorEquipment:#{EPlus::FuelTypeWoodPellets}"])
@@ -2177,6 +2179,7 @@ def get_timeseries_units_from_fuel_type(fuel_type)
@end_uses[[FT::WoodPellets, EUT::Lighting]] = EndUse.new(meters: ["#{Constants.ObjectNameMiscLighting}:InteriorEquipment:#{EPlus::FuelTypeWoodPellets}"])
@end_uses[[FT::WoodPellets, EUT::Fireplace]] = EndUse.new(meters: ["#{Constants.ObjectNameMiscFireplace}:InteriorEquipment:#{EPlus::FuelTypeWoodPellets}"])
end
+ @end_uses[[FT::WoodPellets, EUT::Generator]] = EndUse.new(meters: ["Cogeneration:#{EPlus::FuelTypeWoodPellets}"])
@end_uses[[FT::Coal, EUT::Heating]] = EndUse.new(variables: OutputVars.SpaceHeating(EPlus::FuelTypeCoal))
@end_uses[[FT::Coal, EUT::HotWater]] = EndUse.new(variables: OutputVars.WaterHeating(EPlus::FuelTypeCoal))
@end_uses[[FT::Coal, EUT::ClothesDryer]] = EndUse.new(meters: ["#{Constants.ObjectNameClothesDryer}:InteriorEquipment:#{EPlus::FuelTypeCoal}"])
@@ -2187,6 +2190,7 @@ def get_timeseries_units_from_fuel_type(fuel_type)
@end_uses[[FT::Coal, EUT::Lighting]] = EndUse.new(meters: ["#{Constants.ObjectNameMiscLighting}:InteriorEquipment:#{EPlus::FuelTypeCoal}"])
@end_uses[[FT::Coal, EUT::Fireplace]] = EndUse.new(meters: ["#{Constants.ObjectNameMiscFireplace}:InteriorEquipment:#{EPlus::FuelTypeCoal}"])
end
+ @end_uses[[FT::Coal, EUT::Generator]] = EndUse.new(meters: ["Cogeneration:#{EPlus::FuelTypeCoal}"])
@end_uses.each do |key, end_use|
fuel_type, end_use_type = key
diff --git a/SimulationOutputReport/measure.xml b/SimulationOutputReport/measure.xml
index 993fc347a..a966bcbbb 100644
--- a/SimulationOutputReport/measure.xml
+++ b/SimulationOutputReport/measure.xml
@@ -3,8 +3,8 @@
3.0
simulation_output_report
df9d170c-c21a-4130-866d-0d46b06073fd
- 5dc771e9-535e-4f54-aba9-7b4ac53507ec
- 20210415T235911Z
+ 75305b3d-ecfd-4831-8d14-9c791b2b2580
+ 20210420T225414Z
9BF1E6AC
SimulationOutputReport
HPXML Simulation Output Report
@@ -16,7 +16,6 @@
Output Format
The file format of the annual (and timeseries, if requested) outputs.
Choice
-
false
false
csv
@@ -30,15 +29,12 @@
json
-
-
timeseries_frequency
Timeseries Reporting Frequency
The frequency at which to report timeseries output data. Using 'none' will disable timeseries outputs.
Choice
-
true
false
none
@@ -64,15 +60,12 @@
monthly
-
-
include_timeseries_fuel_consumptions
Generate Timeseries Output: Fuel Consumptions
Generates timeseries energy consumptions for each fuel type.
Boolean
-
true
false
false
@@ -86,15 +79,12 @@
false
-
-
include_timeseries_end_use_consumptions
Generate Timeseries Output: End Use Consumptions
Generates timeseries energy consumptions for each end use.
Boolean
-
true
false
false
@@ -108,15 +98,12 @@
false
-
-
include_timeseries_hot_water_uses
Generate Timeseries Output: Hot Water Uses
Generates timeseries hot water usages for each end use.
Boolean
-
true
false
false
@@ -130,15 +117,12 @@
false
-
-
include_timeseries_total_loads
Generate Timeseries Output: Total Loads
Generates timeseries total heating, cooling, and hot water loads.
Boolean
-
true
false
false
@@ -152,15 +136,12 @@
false
-
-
include_timeseries_component_loads
Generate Timeseries Output: Component Loads
Generates timeseries heating and cooling loads disaggregated by component type.
Boolean
-
true
false
false
@@ -174,15 +155,12 @@
false
-
-
include_timeseries_unmet_loads
Generate Timeseries Output: Unmet Loads
Generates timeseries unmet heating and cooling loads.
Boolean
-
true
false
false
@@ -196,15 +174,12 @@
false
-
-
include_timeseries_zone_temperatures
Generate Timeseries Output: Zone Temperatures
Generates timeseries temperatures for each thermal zone.
Boolean
-
true
false
false
@@ -218,15 +193,12 @@
false
-
-
include_timeseries_airflows
Generate Timeseries Output: Airflows
Generates timeseries airflows.
Boolean
-
true
false
false
@@ -240,15 +212,12 @@
false
-
-
include_timeseries_weather
Generate Timeseries Output: Weather
Generates timeseries weather data.
Boolean
-
true
false
false
@@ -262,8 +231,6 @@
false
-
-
@@ -271,819 +238,665 @@
Fuel Use: Electricity: Total MBtu
Fuel Use: Electricity: Total MBtu
Fuel Use: Electricity: Total MBtu
-
Double
-
false
+
+
+
+
@@ -1124,14 +937,13 @@
measure.rb
rb
script
- CECAEBCC
+ 19F36E24
output_report_test.rb
rb
test
- DF2FAFC5
+ A9C2B966
-uninitialized constant SimulationOutputReport::EPlus
diff --git a/SimulationOutputReport/tests/output_report_test.rb b/SimulationOutputReport/tests/output_report_test.rb
index b05dcbce8..5369f19a6 100644
--- a/SimulationOutputReport/tests/output_report_test.rb
+++ b/SimulationOutputReport/tests/output_report_test.rb
@@ -68,6 +68,7 @@ class SimulationOutputReportTest < MiniTest::Test
'End Use: Fuel Oil: Lighting (MBtu)',
'End Use: Fuel Oil: Fireplace (MBtu)',
'End Use: Fuel Oil: Mech Vent Preheating (MBtu)',
+ 'End Use: Fuel Oil: Generator (MBtu)',
'End Use: Propane: Heating (MBtu)',
'End Use: Propane: Hot Water (MBtu)',
'End Use: Propane: Clothes Dryer (MBtu)',
@@ -85,6 +86,7 @@ class SimulationOutputReportTest < MiniTest::Test
'End Use: Wood Cord: Lighting (MBtu)',
'End Use: Wood Cord: Fireplace (MBtu)',
'End Use: Wood Cord: Mech Vent Preheating (MBtu)',
+ 'End Use: Wood Cord: Generator (MBtu)',
'End Use: Wood Pellets: Heating (MBtu)',
'End Use: Wood Pellets: Hot Water (MBtu)',
'End Use: Wood Pellets: Clothes Dryer (MBtu)',
@@ -93,6 +95,7 @@ class SimulationOutputReportTest < MiniTest::Test
'End Use: Wood Pellets: Lighting (MBtu)',
'End Use: Wood Pellets: Fireplace (MBtu)',
'End Use: Wood Pellets: Mech Vent Preheating (MBtu)',
+ 'End Use: Wood Pellets: Generator (MBtu)',
'End Use: Coal: Heating (MBtu)',
'End Use: Coal: Hot Water (MBtu)',
'End Use: Coal: Clothes Dryer (MBtu)',
@@ -101,6 +104,7 @@ class SimulationOutputReportTest < MiniTest::Test
'End Use: Coal: Lighting (MBtu)',
'End Use: Coal: Fireplace (MBtu)',
'End Use: Coal: Mech Vent Preheating (MBtu)',
+ 'End Use: Coal: Generator (MBtu)',
'Load: Heating (MBtu)',
'Load: Cooling (MBtu)',
'Load: Hot Water: Delivered (MBtu)',
@@ -211,6 +215,7 @@ class SimulationOutputReportTest < MiniTest::Test
'End Use: Fuel Oil: Grill',
'End Use: Fuel Oil: Lighting',
'End Use: Fuel Oil: Fireplace',
+ 'End Use: Fuel Oil: Generator',
'End Use: Propane: Heating',
'End Use: Propane: Hot Water',
'End Use: Propane: Clothes Dryer',
@@ -226,6 +231,7 @@ class SimulationOutputReportTest < MiniTest::Test
'End Use: Wood Cord: Grill',
'End Use: Wood Cord: Lighting',
'End Use: Wood Cord: Fireplace',
+ 'End Use: Wood Cord: Generator',
'End Use: Wood Pellets: Heating',
'End Use: Wood Pellets: Hot Water',
'End Use: Wood Pellets: Clothes Dryer',
@@ -233,6 +239,7 @@ class SimulationOutputReportTest < MiniTest::Test
'End Use: Wood Pellets: Grill',
'End Use: Wood Pellets: Lighting',
'End Use: Wood Pellets: Fireplace',
+ 'End Use: Wood Pellets: Generator',
'End Use: Coal: Heating',
'End Use: Coal: Hot Water',
'End Use: Coal: Clothes Dryer',
@@ -240,6 +247,7 @@ class SimulationOutputReportTest < MiniTest::Test
'End Use: Coal: Grill',
'End Use: Coal: Lighting',
'End Use: Coal: Fireplace',
+ 'End Use: Coal: Generator',
]
TimeseriesColsWaterUses = [
@@ -382,6 +390,7 @@ class SimulationOutputReportTest < MiniTest::Test
'enduseFuelOilClothesDryer',
'enduseFuelOilRangeOven',
'enduseFuelOilMechVentPreheating',
+ 'enduseFuelOilGenerator',
'endusePropaneHeating',
'endusePropaneHotWater',
'endusePropaneClothesDryer',
@@ -393,16 +402,19 @@ class SimulationOutputReportTest < MiniTest::Test
'enduseWoodCordClothesDryer',
'enduseWoodCordRangeOven',
'enduseWoodCordMechVentPreheating',
+ 'enduseWoodCordGenerator',
'enduseWoodPelletsHeating',
'enduseWoodPelletsHotWater',
'enduseWoodPelletsClothesDryer',
'enduseWoodPelletsRangeOven',
'enduseWoodPelletsMechVentPreheating',
+ 'enduseWoodPelletsGenerator',
'enduseCoalHeating',
'enduseCoalHotWater',
'enduseCoalClothesDryer',
'enduseCoalRangeOven',
'enduseCoalMechVentPreheating',
+ 'enduseCoalGenerator',
'loadHeating',
'loadCooling',
'loadHotWaterDelivered',
diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst
index d59433bb1..d3537e6a3 100644
--- a/docs/source/getting_started.rst
+++ b/docs/source/getting_started.rst
@@ -6,7 +6,7 @@ Setup
To get started:
-#. Download `OpenStudio 3.1.0 `_ and install the Command Line Interface/EnergyPlus components, or use the `nrel/openstudio docker image `_.
+#. Download `OpenStudio 3.2.0 `_ and install the Command Line Interface/EnergyPlus components, or use the `nrel/openstudio docker image `_.
#. Download the `latest release `_.
Running
diff --git a/docs/source/workflow_inputs.rst b/docs/source/workflow_inputs.rst
index 609ad8f53..0f2dc0e0a 100644
--- a/docs/source/workflow_inputs.rst
+++ b/docs/source/workflow_inputs.rst
@@ -1812,7 +1812,7 @@ If not entered, the simulation will not include generators.
``NumberofBedroomsServed`` integer > 1 See [#]_ Number of bedrooms served
========================== ======= ======= =========== ======== ======= ============================================
- .. [#] FuelType choices are "natural gas" or "propane".
+ .. [#] FuelType choices are "natural gas", "fuel oil", "fuel oil 1", "fuel oil 2", "fuel oil 4", "fuel oil 5/6", "diesel", "propane", "kerosene", "coal", "coke", "bituminous coal", "anthracite coal", "wood", or "wood pellets".
.. [#] AnnualOutputkWh must also be < AnnualConsumptionkBtu*3.412 (i.e., the generator must consume more energy than it produces).
.. [#] NumberofBedroomsServed only required if IsSharedSystem is true, in which case it must be > NumberofBedrooms.
Annual consumption and annual production will be apportioned to the dwelling unit using its number of bedrooms divided by the total number of bedrooms served by the generator.
diff --git a/tasks.rb b/tasks.rb
index 0cd50b795..7e55a5baf 100644
--- a/tasks.rb
+++ b/tasks.rb
@@ -2994,6 +2994,7 @@ def create_hpxmls
if errors.size > 0
fail "ERRORS: #{errors}"
end
+
# Check for errors
errors = hpxml.check_for_errors()
if errors.size > 0
@@ -5078,6 +5079,7 @@ def set_hpxml_windows(hpxml_file, hpxml)
hpxml.windows[-1].fraction_operable = 1.0
end
next unless hpxml_file == 'base-enclosure-split-surfaces2.xml'
+
hpxml.windows[-1].ufactor += 0.01 * i
hpxml.windows[-1].interior_shading_factor_summer -= 0.02 * i
hpxml.windows[-1].interior_shading_factor_winter -= 0.01 * i
@@ -5187,6 +5189,7 @@ def set_hpxml_skylights(hpxml_file, hpxml)
hpxml.skylights[-1].id += i.to_s
hpxml.skylights[-1].roof_idref += i.to_s if i % 2 == 0
next unless hpxml_file == 'base-enclosure-split-surfaces2.xml'
+
hpxml.skylights[-1].ufactor += 0.01 * i
hpxml.skylights[-1].interior_shading_factor_summer -= 0.02 * i
hpxml.skylights[-1].interior_shading_factor_winter -= 0.01 * i
@@ -7080,7 +7083,7 @@ def set_hpxml_generators(hpxml_file, hpxml)
annual_consumption_kbtu: 8500,
annual_output_kwh: 500)
hpxml.generators.add(id: 'Generator2',
- fuel_type: HPXML::FuelTypePropane,
+ fuel_type: HPXML::FuelTypeOil,
annual_consumption_kbtu: 8500,
annual_output_kwh: 500)
elsif ['base-bldgtype-multifamily-shared-generator.xml'].include? hpxml_file
diff --git a/workflow/run_simulation.rb b/workflow/run_simulation.rb
index 3c5ced1ec..d1ed1998b 100644
--- a/workflow/run_simulation.rb
+++ b/workflow/run_simulation.rb
@@ -11,7 +11,7 @@
basedir = File.expand_path(File.dirname(__FILE__))
def run_workflow(basedir, rundir, hpxml, debug, timeseries_output_freq, timeseries_outputs, skip_validation, add_comp_loads,
- output_format, building_id)
+ output_format, building_id, ep_input_format)
measures_dir = File.join(basedir, '..')
measures = {}
@@ -43,7 +43,7 @@ def run_workflow(basedir, rundir, hpxml, debug, timeseries_output_freq, timeseri
args['include_timeseries_weather'] = timeseries_outputs.include? 'weather'
update_args_hash(measures, measure_subdir, args)
- results = run_hpxml_workflow(rundir, measures, measures_dir, debug: debug)
+ results = run_hpxml_workflow(rundir, measures, measures_dir, debug: debug, ep_input_format: ep_input_format)
return results[:success]
end
@@ -96,6 +96,11 @@ def run_workflow(basedir, rundir, hpxml, debug, timeseries_output_freq, timeseri
options[:add_comp_loads] = true
end
+ options[:ep_input_format] = 'idf'
+ opts.on('--ep-input-format TYPE', 'EnergyPlus input file format (idf, epjson)') do |t|
+ options[:ep_input_format] = t
+ end
+
opts.on('-b', '--building-id ', 'ID of Building to simulate (required when multiple HPXML Building elements)') do |t|
options[:building_id] = t
end
@@ -179,7 +184,8 @@ def run_workflow(basedir, rundir, hpxml, debug, timeseries_output_freq, timeseri
# Run design
puts "HPXML: #{options[:hpxml]}"
success = run_workflow(basedir, rundir, options[:hpxml], options[:debug], timeseries_output_freq, timeseries_outputs,
- options[:skip_validation], options[:add_comp_loads], options[:output_format], options[:building_id])
+ options[:skip_validation], options[:add_comp_loads], options[:output_format], options[:building_id],
+ options[:ep_input_format])
if not success
exit! 1
diff --git a/workflow/sample_files/base-misc-generators.xml b/workflow/sample_files/base-misc-generators.xml
index 6d3c7a4d8..9ba6a2512 100644
--- a/workflow/sample_files/base-misc-generators.xml
+++ b/workflow/sample_files/base-misc-generators.xml
@@ -422,7 +422,7 @@
- propane
+ fuel oil
8500.0
500.0
diff --git a/workflow/sample_files/invalid_files/generator-output-greater-than-consumption.xml b/workflow/sample_files/invalid_files/generator-output-greater-than-consumption.xml
index 09ac61c7d..144d69b87 100644
--- a/workflow/sample_files/invalid_files/generator-output-greater-than-consumption.xml
+++ b/workflow/sample_files/invalid_files/generator-output-greater-than-consumption.xml
@@ -422,7 +422,7 @@
- propane
+ fuel oil
8500.0
500.0
diff --git a/workflow/tests/base_results/results.csv b/workflow/tests/base_results/results.csv
index 114eee76b..b8971ad0c 100644
--- a/workflow/tests/base_results/results.csv
+++ b/workflow/tests/base_results/results.csv
@@ -24,13 +24,13 @@ base-bldgtype-multifamily-adjacent-to-other-housing-unit.xml,24.87,24.87,1.08,0.
base-bldgtype-multifamily-shared-boiler-chiller-baseboard.xml,25.76,25.76,0.82,0.0,0.0,0.0,0.0,0.0,0.0,0.04,2.36,0.61,9.67,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.82,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.75,6.55,9.52,0.58,0.0,0.0,0.0,0.01,6192.0,6194.0,8.64,7.04,0.0,-0.01,2.62,0.0,0.0,0.41,1.49,0.0,-0.01,0.0,-0.34,1.64,0.0,0.51,0.0,0.0,-5.6,0.0,-0.0,-0.85,0.0,0.0,-0.04,3.42,0.0,-0.0,0.0,-0.33,-0.65,-3.12,-0.17,0.0,0.0,8.4,1354.0,998.0,11594.0,3210.0
base-bldgtype-multifamily-shared-boiler-chiller-fan-coil-ducted.xml,26.33,26.33,0.95,0.0,0.0,0.0,0.0,0.0,0.0,0.07,2.82,0.69,9.67,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.95,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.86,7.45,9.52,0.58,0.0,0.0,0.0,0.01,6221.0,6232.0,8.94,8.56,0.0,-0.01,2.62,0.0,0.0,0.41,1.48,0.0,-0.01,0.0,-0.34,1.64,0.0,0.51,0.0,0.11,-5.59,0.0,-0.0,-0.85,0.0,0.0,-0.04,3.41,0.0,-0.0,0.0,-0.33,-0.65,-3.13,-0.17,0.0,0.91,8.42,1354.0,998.0,11594.0,3210.0
base-bldgtype-multifamily-shared-boiler-chiller-fan-coil.xml,26.01,26.01,0.79,0.0,0.0,0.0,0.0,0.0,0.0,0.09,2.57,0.61,9.67,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.75,6.56,9.52,0.58,0.0,0.0,0.0,0.01,6234.0,6205.0,8.64,7.04,0.0,-0.01,2.62,0.0,0.0,0.41,1.49,0.0,-0.01,0.0,-0.34,1.64,0.0,0.51,0.0,0.0,-5.6,0.0,-0.0,-0.85,0.0,0.0,-0.04,3.42,0.0,-0.0,0.0,-0.33,-0.65,-3.12,-0.17,0.0,0.0,8.4,1354.0,998.0,11594.0,3210.0
-base-bldgtype-multifamily-shared-boiler-chiller-water-loop-heat-pump.xml,30.04,30.04,0.64,0.0,0.0,0.0,0.0,0.0,0.04,0.05,6.51,0.69,9.67,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.64,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.76,7.46,9.52,0.58,0.0,0.0,0.0,0.01,6227.0,6439.0,8.71,8.56,0.0,-0.01,2.62,0.0,0.0,0.41,1.48,0.0,-0.01,0.0,-0.34,1.64,0.0,0.51,0.0,0.02,-5.59,0.0,-0.0,-0.86,0.0,0.0,-0.04,3.41,0.0,-0.0,0.0,-0.33,-0.65,-3.13,-0.17,0.0,0.91,8.42,1354.0,998.0,11596.0,3211.0
-base-bldgtype-multifamily-shared-boiler-cooling-tower-water-loop-heat-pump.xml,26.2,26.2,0.64,0.0,0.0,0.0,0.0,0.0,0.04,0.05,2.67,0.69,9.67,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.64,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.76,7.46,9.52,0.58,0.0,0.0,0.0,0.01,6206.0,6226.0,8.71,8.56,0.0,-0.01,2.62,0.0,0.0,0.41,1.48,0.0,-0.01,0.0,-0.34,1.64,0.0,0.51,0.0,0.02,-5.59,0.0,-0.0,-0.86,0.0,0.0,-0.04,3.41,0.0,-0.0,0.0,-0.33,-0.65,-3.13,-0.17,0.0,0.91,8.42,1354.0,998.0,11596.0,3211.0
+base-bldgtype-multifamily-shared-boiler-chiller-water-loop-heat-pump.xml,30.04,30.04,0.64,0.0,0.0,0.0,0.0,0.0,0.04,0.04,6.51,0.69,9.67,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.64,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.76,7.46,9.52,0.58,0.0,0.0,0.0,0.01,6227.0,6439.0,8.72,8.56,0.0,-0.01,2.62,0.0,0.0,0.41,1.48,0.0,-0.01,0.0,-0.34,1.64,0.0,0.51,0.0,0.02,-5.59,0.0,-0.0,-0.86,0.0,0.0,-0.04,3.41,0.0,-0.0,0.0,-0.33,-0.65,-3.13,-0.17,0.0,0.91,8.42,1354.0,998.0,11596.0,3211.0
+base-bldgtype-multifamily-shared-boiler-cooling-tower-water-loop-heat-pump.xml,26.2,26.2,0.64,0.0,0.0,0.0,0.0,0.0,0.04,0.04,2.67,0.69,9.67,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.64,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.76,7.46,9.52,0.58,0.0,0.0,0.0,0.01,6204.0,6226.0,8.72,8.56,0.0,-0.01,2.62,0.0,0.0,0.41,1.48,0.0,-0.01,0.0,-0.34,1.64,0.0,0.51,0.0,0.02,-5.59,0.0,-0.0,-0.86,0.0,0.0,-0.04,3.41,0.0,-0.0,0.0,-0.33,-0.65,-3.13,-0.17,0.0,0.91,8.42,1354.0,998.0,11596.0,3211.0
base-bldgtype-multifamily-shared-boiler-only-baseboard.xml,22.74,22.74,0.72,0.0,0.0,0.0,0.0,0.0,0.0,0.03,0.0,0.0,9.63,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.72,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.66,0.0,9.52,0.53,0.0,0.0,0.0,0.0,6142.0,0.0,8.64,0.0,0.0,-0.0,3.36,0.0,0.0,0.47,1.27,0.0,-0.0,0.0,-0.01,2.08,0.0,0.6,0.0,0.0,-7.23,0.0,0.0,-0.87,0.0,0.0,-0.12,2.01,0.0,0.0,0.0,0.0,-0.59,-7.01,-0.2,0.0,0.0,6.73,1354.0,998.0,11593.0,3210.0
base-bldgtype-multifamily-shared-boiler-only-fan-coil-ducted.xml,22.77,22.77,0.84,0.0,0.0,0.0,0.0,0.0,0.0,0.06,0.0,0.0,9.63,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.84,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.75,0.0,9.52,0.53,0.0,0.0,0.0,0.0,6171.0,0.0,8.94,0.0,0.0,-0.0,3.36,0.0,0.0,0.47,1.27,0.0,-0.0,0.0,-0.01,2.08,0.0,0.6,0.0,0.1,-7.23,0.0,0.0,-0.87,0.0,0.0,-0.12,2.01,0.0,0.0,0.0,0.0,-0.59,-7.01,-0.2,0.0,0.0,6.73,1354.0,998.0,11593.0,3210.0
base-bldgtype-multifamily-shared-boiler-only-fan-coil-eae.xml,22.78,22.78,0.7,0.0,0.0,0.0,0.0,0.0,0.0,0.07,0.0,0.0,9.63,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.7,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.66,0.0,9.52,0.53,0.0,0.0,0.0,0.0,6183.0,0.0,8.64,0.0,0.0,-0.0,3.36,0.0,0.0,0.47,1.27,0.0,-0.0,0.0,-0.01,2.08,0.0,0.6,0.0,0.0,-7.23,0.0,0.0,-0.87,0.0,0.0,-0.12,2.01,0.0,0.0,0.0,0.0,-0.59,-7.01,-0.2,0.0,0.0,6.73,1354.0,998.0,11593.0,3210.0
base-bldgtype-multifamily-shared-boiler-only-fan-coil.xml,22.79,22.79,0.69,0.0,0.0,0.0,0.0,0.0,0.0,0.08,0.0,0.0,9.63,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.69,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.66,0.0,9.52,0.53,0.0,0.0,0.0,0.0,6184.0,0.0,8.64,0.0,0.0,-0.0,3.36,0.0,0.0,0.47,1.27,0.0,-0.0,0.0,-0.01,2.08,0.0,0.6,0.0,0.0,-7.23,0.0,0.0,-0.87,0.0,0.0,-0.12,2.01,0.0,0.0,0.0,0.0,-0.59,-7.01,-0.2,0.0,0.0,6.73,1354.0,998.0,11593.0,3210.0
-base-bldgtype-multifamily-shared-boiler-only-water-loop-heat-pump.xml,22.79,22.79,0.56,0.0,0.0,0.0,0.0,0.0,0.03,0.04,0.0,0.0,9.63,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.56,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.67,0.0,9.52,0.53,0.0,0.0,0.0,0.0,6156.0,2682.0,8.71,0.0,0.0,-0.0,3.36,0.0,0.0,0.47,1.27,0.0,-0.0,0.0,-0.01,2.08,0.0,0.6,0.0,0.02,-7.23,0.0,0.0,-0.87,0.0,0.0,-0.12,2.01,0.0,0.0,0.0,0.0,-0.59,-7.01,-0.2,0.0,0.0,6.73,1354.0,998.0,11595.0,3211.0
+base-bldgtype-multifamily-shared-boiler-only-water-loop-heat-pump.xml,22.78,22.78,0.56,0.0,0.0,0.0,0.0,0.0,0.04,0.04,0.0,0.0,9.63,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.56,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.67,0.0,9.52,0.53,0.0,0.0,0.0,0.0,6154.0,2678.0,8.71,0.0,0.0,-0.0,3.36,0.0,0.0,0.47,1.27,0.0,-0.0,0.0,-0.01,2.08,0.0,0.6,0.0,0.02,-7.23,0.0,0.0,-0.87,0.0,0.0,-0.12,2.01,0.0,0.0,0.0,0.0,-0.59,-7.01,-0.2,0.0,0.0,6.73,1354.0,998.0,11595.0,3211.0
base-bldgtype-multifamily-shared-chiller-only-baseboard.xml,25.7,25.7,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,2.34,0.6,9.68,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,6.46,9.52,0.59,0.0,0.0,0.0,0.01,0.0,6194.0,0.0,7.04,0.0,-0.01,2.46,0.0,0.0,0.37,1.13,0.0,-0.01,0.0,-0.31,1.51,0.0,0.47,0.0,0.0,-5.72,0.0,-0.0,-0.83,0.0,0.0,-0.04,3.35,0.0,-0.0,0.0,-0.3,-0.63,-3.12,-0.17,0.0,0.0,8.29,1354.0,998.0,11594.0,3210.0
base-bldgtype-multifamily-shared-chiller-only-fan-coil-ducted.xml,26.24,26.24,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,2.79,0.68,9.68,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,7.35,9.52,0.59,0.0,0.0,0.0,0.01,6080.0,6232.0,0.34,8.56,0.0,-0.01,2.46,0.0,0.0,0.37,1.12,0.0,-0.01,0.0,-0.31,1.51,0.0,0.47,0.0,-0.0,-5.7,0.0,-0.0,-0.83,0.0,0.0,-0.04,3.34,0.0,-0.0,0.0,-0.29,-0.64,-3.13,-0.18,0.0,0.9,8.31,1354.0,998.0,11593.0,3210.0
base-bldgtype-multifamily-shared-chiller-only-fan-coil.xml,25.91,25.91,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,2.54,0.6,9.68,0.0,0.0,2.03,0.0,0.21,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.32,0.37,1.51,1.53,0.0,2.11,2.79,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,6.46,9.52,0.59,0.0,0.0,0.0,0.01,0.0,6205.0,0.0,7.04,0.0,-0.01,2.46,0.0,0.0,0.37,1.13,0.0,-0.01,0.0,-0.31,1.51,0.0,0.47,0.0,0.0,-5.72,0.0,-0.0,-0.83,0.0,0.0,-0.04,3.35,0.0,-0.0,0.0,-0.3,-0.63,-3.12,-0.17,0.0,0.0,8.29,1354.0,998.0,11594.0,3210.0
diff --git a/workflow/tests/hpxml_translator_test.rb b/workflow/tests/hpxml_translator_test.rb
index 00ef053af..450c6e0a8 100644
--- a/workflow/tests/hpxml_translator_test.rb
+++ b/workflow/tests/hpxml_translator_test.rb
@@ -75,10 +75,10 @@ def test_run_simulation_json_output
# Check for output files
sql_path = File.join(File.dirname(xml), 'run', 'eplusout.sql')
assert(File.exist? sql_path)
- csv_output_path = File.join(File.dirname(xml), 'run', 'results_annual.json')
- assert(File.exist? csv_output_path)
- csv_output_path = File.join(File.dirname(xml), 'run', 'results_timeseries.json')
- assert(File.exist? csv_output_path)
+ json_output_path = File.join(File.dirname(xml), 'run', 'results_annual.json')
+ assert(File.exist? json_output_path)
+ json_output_path = File.join(File.dirname(xml), 'run', 'results_timeseries.json')
+ assert(File.exist? json_output_path)
# Check for debug files
osm_path = File.join(File.dirname(xml), 'run', 'in.osm')
@@ -87,6 +87,44 @@ def test_run_simulation_json_output
assert(File.exist? hpxml_defaults_path)
end
+ def test_run_simulation_epjson_input
+ # Check that we can run a simulation using epJSON (instead of IDF) if requested
+ os_cli = OpenStudio.getOpenStudioCLI
+ rb_path = File.join(File.dirname(__FILE__), '..', 'run_simulation.rb')
+ xml = File.join(File.dirname(__FILE__), '..', 'sample_files', 'base.xml')
+ command = "#{os_cli} #{rb_path} -x #{xml} --ep-input-format epjson"
+ system(command, err: File::NULL)
+
+ # Check for epjson file
+ epjson = File.join(File.dirname(xml), 'run', 'in.epJSON')
+ assert(File.exist? epjson)
+
+ # Check for output files
+ sql_path = File.join(File.dirname(xml), 'run', 'eplusout.sql')
+ assert(File.exist? sql_path)
+ csv_output_path = File.join(File.dirname(xml), 'run', 'results_annual.csv')
+ assert(File.exist? csv_output_path)
+ end
+
+ def test_run_simulation_idf_input
+ # Check that we can run a simulation using IDF (instead of epJSON) if requested
+ os_cli = OpenStudio.getOpenStudioCLI
+ rb_path = File.join(File.dirname(__FILE__), '..', 'run_simulation.rb')
+ xml = File.join(File.dirname(__FILE__), '..', 'sample_files', 'base.xml')
+ command = "#{os_cli} #{rb_path} -x #{xml} --ep-input-format idf"
+ system(command, err: File::NULL)
+
+ # Check for idf file
+ idf = File.join(File.dirname(xml), 'run', 'in.idf')
+ assert(File.exist? idf)
+
+ # Check for output files
+ sql_path = File.join(File.dirname(xml), 'run', 'eplusout.sql')
+ assert(File.exist? sql_path)
+ csv_output_path = File.join(File.dirname(xml), 'run', 'results_annual.csv')
+ assert(File.exist? csv_output_path)
+ end
+
def test_run_simulation_faster_performance
# Run w/ --skip-validation and w/o --add-component-loads arguments
os_cli = OpenStudio.getOpenStudioCLI
@@ -349,6 +387,7 @@ def _run_xml(xml, worker_num = nil, expect_error = false, expect_error_msgs = ni
found_error_msg = false
run_log.each do |run_line|
next unless run_line.start_with? 'Error: '
+
n_errors += 1 if i == 0
next unless run_line.include? error_msg
@@ -580,6 +619,7 @@ def _verify_outputs(rundir, hpxml_path, results, hpxml)
next if err_line.include? 'SimHVAC: Maximum iterations (20) exceeded for all HVAC loops'
next if err_line.include? 'Rated air volume flow rate per watt of rated total water heating capacity is out of range'
next if err_line.include? 'For object = Coil:WaterHeating:AirToWaterHeatPump:Wrapped'
+ next if err_line.include? 'Enthalpy out of range (PsyTsatFnHPb)'
end
# HP defrost curves
if hpxml.heat_pumps.select { |hp| [HPXML::HVACTypeHeatPumpAirToAir, HPXML::HVACTypeHeatPumpMiniSplit].include? hp.heat_pump_type }.size > 0
@@ -603,6 +643,9 @@ def _verify_outputs(rundir, hpxml_path, results, hpxml)
if hpxml_path.include?('ground-to-air-heat-pump-cooling-only.xml') || hpxml_path.include?('ground-to-air-heat-pump-heating-only.xml')
next if err_line.include? 'COIL:HEATING:WATERTOAIRHEATPUMP:EQUATIONFIT' # heating capacity is > 20% different than cooling capacity; safe to ignore
end
+ if hpxml.solar_thermal_systems.size > 0
+ next if err_line.include? 'Supply Side is storing excess heat the majority of the time.'
+ end
if hpxml_path.include?('base-schedules-stochastic.xml') || hpxml_path.include?('base-schedules-stochastic-vacant.xml') || hpxml_path.include?('base-schedules-user-specified.xml')
next if err_line.include?('GetCurrentScheduleValue: Schedule=') && err_line.include?('is a Schedule:File')
end