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 Fuel Use: Natural Gas: Total MBtu Fuel Use: Natural Gas: Total MBtu Fuel Use: Natural Gas: Total MBtu - Double - false Fuel Use: Fuel Oil: Total MBtu Fuel Use: Fuel Oil: Total MBtu Fuel Use: Fuel Oil: Total MBtu - Double - false Fuel Use: Propane: Total MBtu Fuel Use: Propane: Total MBtu Fuel Use: Propane: Total MBtu - Double - false Fuel Use: Wood Cord: Total MBtu Fuel Use: Wood Cord: Total MBtu Fuel Use: Wood Cord: Total MBtu - Double - false Fuel Use: Wood Pellets: Total MBtu Fuel Use: Wood Pellets: Total MBtu Fuel Use: Wood Pellets: Total MBtu - Double - false Fuel Use: Coal: Total MBtu Fuel Use: Coal: Total MBtu Fuel Use: Coal: Total MBtu - Double - false End Use: Electricity: Heating MBtu End Use: Electricity: Heating MBtu End Use: Electricity: Heating MBtu - Double - false End Use: Electricity: Heating Fans/Pumps MBtu End Use: Electricity: Heating Fans/Pumps MBtu End Use: Electricity: Heating Fans/Pumps MBtu - Double - false End Use: Electricity: Cooling MBtu End Use: Electricity: Cooling MBtu End Use: Electricity: Cooling MBtu - Double - false End Use: Electricity: Cooling Fans/Pumps MBtu End Use: Electricity: Cooling Fans/Pumps MBtu End Use: Electricity: Cooling Fans/Pumps MBtu - Double - false End Use: Electricity: Hot Water MBtu End Use: Electricity: Hot Water MBtu End Use: Electricity: Hot Water MBtu - Double - false End Use: Electricity: Hot Water Recirc Pump MBtu End Use: Electricity: Hot Water Recirc Pump MBtu End Use: Electricity: Hot Water Recirc Pump MBtu - Double - false End Use: Electricity: Hot Water Solar Thermal Pump MBtu End Use: Electricity: Hot Water Solar Thermal Pump MBtu End Use: Electricity: Hot Water Solar Thermal Pump MBtu - Double - false End Use: Electricity: Lighting Interior MBtu End Use: Electricity: Lighting Interior MBtu End Use: Electricity: Lighting Interior MBtu - Double - false End Use: Electricity: Lighting Garage MBtu End Use: Electricity: Lighting Garage MBtu End Use: Electricity: Lighting Garage MBtu - Double - false End Use: Electricity: Lighting Exterior MBtu End Use: Electricity: Lighting Exterior MBtu End Use: Electricity: Lighting Exterior MBtu - Double - false End Use: Electricity: Mech Vent MBtu End Use: Electricity: Mech Vent MBtu End Use: Electricity: Mech Vent MBtu - Double - false End Use: Electricity: Mech Vent Preheating MBtu End Use: Electricity: Mech Vent Preheating MBtu End Use: Electricity: Mech Vent Preheating MBtu - Double - false End Use: Electricity: Mech Vent Precooling MBtu End Use: Electricity: Mech Vent Precooling MBtu End Use: Electricity: Mech Vent Precooling MBtu - Double - false End Use: Electricity: Whole House Fan MBtu End Use: Electricity: Whole House Fan MBtu End Use: Electricity: Whole House Fan MBtu - Double - false End Use: Electricity: Refrigerator MBtu End Use: Electricity: Refrigerator MBtu End Use: Electricity: Refrigerator MBtu - Double - false End Use: Electricity: Freezer MBtu End Use: Electricity: Freezer MBtu End Use: Electricity: Freezer MBtu - Double - false End Use: Electricity: Dehumidifier MBtu End Use: Electricity: Dehumidifier MBtu End Use: Electricity: Dehumidifier MBtu - Double - false End Use: Electricity: Dishwasher MBtu End Use: Electricity: Dishwasher MBtu End Use: Electricity: Dishwasher MBtu - Double - false End Use: Electricity: Clothes Washer MBtu End Use: Electricity: Clothes Washer MBtu End Use: Electricity: Clothes Washer MBtu - Double - false End Use: Electricity: Clothes Dryer MBtu End Use: Electricity: Clothes Dryer MBtu End Use: Electricity: Clothes Dryer MBtu - Double - false End Use: Electricity: Range/Oven MBtu End Use: Electricity: Range/Oven MBtu End Use: Electricity: Range/Oven MBtu - Double - false End Use: Electricity: Ceiling Fan MBtu End Use: Electricity: Ceiling Fan MBtu End Use: Electricity: Ceiling Fan MBtu - Double - false End Use: Electricity: Television MBtu End Use: Electricity: Television MBtu End Use: Electricity: Television MBtu - Double - false End Use: Electricity: Plug Loads MBtu End Use: Electricity: Plug Loads MBtu End Use: Electricity: Plug Loads MBtu - Double - false End Use: Electricity: Electric Vehicle Charging MBtu End Use: Electricity: Electric Vehicle Charging MBtu End Use: Electricity: Electric Vehicle Charging MBtu - Double - false End Use: Electricity: Well Pump MBtu End Use: Electricity: Well Pump MBtu End Use: Electricity: Well Pump MBtu - Double - false End Use: Electricity: Pool Heater MBtu End Use: Electricity: Pool Heater MBtu End Use: Electricity: Pool Heater MBtu - Double - false End Use: Electricity: Pool Pump MBtu End Use: Electricity: Pool Pump MBtu End Use: Electricity: Pool Pump MBtu - Double - false End Use: Electricity: Hot Tub Heater MBtu End Use: Electricity: Hot Tub Heater MBtu End Use: Electricity: Hot Tub Heater MBtu - Double - false End Use: Electricity: Hot Tub Pump MBtu End Use: Electricity: Hot Tub Pump MBtu End Use: Electricity: Hot Tub Pump MBtu - Double - false End Use: Electricity: PV MBtu End Use: Electricity: PV MBtu End Use: Electricity: PV MBtu - Double - false End Use: Electricity: Generator MBtu End Use: Electricity: Generator MBtu End Use: Electricity: Generator MBtu - Double - false End Use: Natural Gas: Heating MBtu End Use: Natural Gas: Heating MBtu End Use: Natural Gas: Heating MBtu - Double - false End Use: Natural Gas: Hot Water MBtu End Use: Natural Gas: Hot Water MBtu End Use: Natural Gas: Hot Water MBtu - Double - false End Use: Natural Gas: Clothes Dryer MBtu End Use: Natural Gas: Clothes Dryer MBtu End Use: Natural Gas: Clothes Dryer MBtu - Double - false End Use: Natural Gas: Range/Oven MBtu End Use: Natural Gas: Range/Oven MBtu End Use: Natural Gas: Range/Oven MBtu - Double - false End Use: Natural Gas: Mech Vent Preheating MBtu End Use: Natural Gas: Mech Vent Preheating MBtu End Use: Natural Gas: Mech Vent Preheating MBtu - Double - false End Use: Natural Gas: Pool Heater MBtu End Use: Natural Gas: Pool Heater MBtu End Use: Natural Gas: Pool Heater MBtu - Double - false End Use: Natural Gas: Hot Tub Heater MBtu End Use: Natural Gas: Hot Tub Heater MBtu End Use: Natural Gas: Hot Tub Heater MBtu - Double - false End Use: Natural Gas: Grill MBtu End Use: Natural Gas: Grill MBtu End Use: Natural Gas: Grill MBtu - Double - false End Use: Natural Gas: Lighting MBtu End Use: Natural Gas: Lighting MBtu End Use: Natural Gas: Lighting MBtu - Double - false End Use: Natural Gas: Fireplace MBtu End Use: Natural Gas: Fireplace MBtu End Use: Natural Gas: Fireplace MBtu - Double - false End Use: Natural Gas: Generator MBtu End Use: Natural Gas: Generator MBtu End Use: Natural Gas: Generator MBtu - Double - false End Use: Fuel Oil: Heating MBtu End Use: Fuel Oil: Heating MBtu End Use: Fuel Oil: Heating MBtu - Double - false End Use: Fuel Oil: Hot Water MBtu End Use: Fuel Oil: Hot Water MBtu End Use: Fuel Oil: Hot Water MBtu - Double - false End Use: Fuel Oil: Clothes Dryer MBtu End Use: Fuel Oil: Clothes Dryer MBtu End Use: Fuel Oil: Clothes Dryer MBtu - Double - false End Use: Fuel Oil: Range/Oven MBtu End Use: Fuel Oil: Range/Oven MBtu End Use: Fuel Oil: Range/Oven MBtu - Double - false End Use: Fuel Oil: Mech Vent Preheating MBtu End Use: Fuel Oil: Mech Vent Preheating MBtu End Use: Fuel Oil: Mech Vent Preheating MBtu - Double - false End Use: Fuel Oil: Grill MBtu End Use: Fuel Oil: Grill MBtu End Use: Fuel Oil: Grill MBtu - Double - false End Use: Fuel Oil: Lighting MBtu End Use: Fuel Oil: Lighting MBtu End Use: Fuel Oil: Lighting MBtu - Double - false End Use: Fuel Oil: Fireplace MBtu End Use: Fuel Oil: Fireplace MBtu End Use: Fuel Oil: Fireplace MBtu - Double - + false + + + End Use: Fuel Oil: Generator MBtu + End Use: Fuel Oil: Generator MBtu + End Use: Fuel Oil: Generator MBtu + Double false End Use: Propane: Heating MBtu End Use: Propane: Heating MBtu End Use: Propane: Heating MBtu - Double - false End Use: Propane: Hot Water MBtu End Use: Propane: Hot Water MBtu End Use: Propane: Hot Water MBtu - Double - false End Use: Propane: Clothes Dryer MBtu End Use: Propane: Clothes Dryer MBtu End Use: Propane: Clothes Dryer MBtu - Double - false End Use: Propane: Range/Oven MBtu End Use: Propane: Range/Oven MBtu End Use: Propane: Range/Oven MBtu - Double - false End Use: Propane: Mech Vent Preheating MBtu End Use: Propane: Mech Vent Preheating MBtu End Use: Propane: Mech Vent Preheating MBtu - Double - false End Use: Propane: Grill MBtu End Use: Propane: Grill MBtu End Use: Propane: Grill MBtu - Double - false End Use: Propane: Lighting MBtu End Use: Propane: Lighting MBtu End Use: Propane: Lighting MBtu - Double - false End Use: Propane: Fireplace MBtu End Use: Propane: Fireplace MBtu End Use: Propane: Fireplace MBtu - Double - false End Use: Propane: Generator MBtu End Use: Propane: Generator MBtu End Use: Propane: Generator MBtu - Double - false End Use: Wood Cord: Heating MBtu End Use: Wood Cord: Heating MBtu End Use: Wood Cord: Heating MBtu - Double - false End Use: Wood Cord: Hot Water MBtu End Use: Wood Cord: Hot Water MBtu End Use: Wood Cord: Hot Water MBtu - Double - false End Use: Wood Cord: Clothes Dryer MBtu End Use: Wood Cord: Clothes Dryer MBtu End Use: Wood Cord: Clothes Dryer MBtu - Double - false End Use: Wood Cord: Range/Oven MBtu End Use: Wood Cord: Range/Oven MBtu End Use: Wood Cord: Range/Oven MBtu - Double - false End Use: Wood Cord: Mech Vent Preheating MBtu End Use: Wood Cord: Mech Vent Preheating MBtu End Use: Wood Cord: Mech Vent Preheating MBtu - Double - false End Use: Wood Cord: Grill MBtu End Use: Wood Cord: Grill MBtu End Use: Wood Cord: Grill MBtu - Double - false End Use: Wood Cord: Lighting MBtu End Use: Wood Cord: Lighting MBtu End Use: Wood Cord: Lighting MBtu - Double - false End Use: Wood Cord: Fireplace MBtu End Use: Wood Cord: Fireplace MBtu End Use: Wood Cord: Fireplace MBtu - Double - + false + + + End Use: Wood Cord: Generator MBtu + End Use: Wood Cord: Generator MBtu + End Use: Wood Cord: Generator MBtu + Double false End Use: Wood Pellets: Heating MBtu End Use: Wood Pellets: Heating MBtu End Use: Wood Pellets: Heating MBtu - Double - false End Use: Wood Pellets: Hot Water MBtu End Use: Wood Pellets: Hot Water MBtu End Use: Wood Pellets: Hot Water MBtu - Double - false End Use: Wood Pellets: Clothes Dryer MBtu End Use: Wood Pellets: Clothes Dryer MBtu End Use: Wood Pellets: Clothes Dryer MBtu - Double - false End Use: Wood Pellets: Range/Oven MBtu End Use: Wood Pellets: Range/Oven MBtu End Use: Wood Pellets: Range/Oven MBtu - Double - false End Use: Wood Pellets: Mech Vent Preheating MBtu End Use: Wood Pellets: Mech Vent Preheating MBtu End Use: Wood Pellets: Mech Vent Preheating MBtu - Double - false End Use: Wood Pellets: Grill MBtu End Use: Wood Pellets: Grill MBtu End Use: Wood Pellets: Grill MBtu - Double - false End Use: Wood Pellets: Lighting MBtu End Use: Wood Pellets: Lighting MBtu End Use: Wood Pellets: Lighting MBtu - Double - false End Use: Wood Pellets: Fireplace MBtu End Use: Wood Pellets: Fireplace MBtu End Use: Wood Pellets: Fireplace MBtu - Double - + false + + + End Use: Wood Pellets: Generator MBtu + End Use: Wood Pellets: Generator MBtu + End Use: Wood Pellets: Generator MBtu + Double false End Use: Coal: Heating MBtu End Use: Coal: Heating MBtu End Use: Coal: Heating MBtu - Double - false End Use: Coal: Hot Water MBtu End Use: Coal: Hot Water MBtu End Use: Coal: Hot Water MBtu - Double - false End Use: Coal: Clothes Dryer MBtu End Use: Coal: Clothes Dryer MBtu End Use: Coal: Clothes Dryer MBtu - Double - false End Use: Coal: Range/Oven MBtu End Use: Coal: Range/Oven MBtu End Use: Coal: Range/Oven MBtu - Double - false End Use: Coal: Mech Vent Preheating MBtu End Use: Coal: Mech Vent Preheating MBtu End Use: Coal: Mech Vent Preheating MBtu - Double - false End Use: Coal: Grill MBtu End Use: Coal: Grill MBtu End Use: Coal: Grill MBtu - Double - false End Use: Coal: Lighting MBtu End Use: Coal: Lighting MBtu End Use: Coal: Lighting MBtu - Double - false End Use: Coal: Fireplace MBtu End Use: Coal: Fireplace MBtu End Use: Coal: Fireplace MBtu - Double - + false + + + End Use: Coal: Generator MBtu + End Use: Coal: Generator MBtu + End Use: Coal: Generator 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