lib/openstudio-standards/standards/Standards.WaterHeaterMixed.rb in openstudio-standards-0.1.15 vs lib/openstudio-standards/standards/Standards.WaterHeaterMixed.rb in openstudio-standards-0.2.0.rc1

- old
+ new

@@ -1,270 +1,207 @@ -# Reopen the OpenStudio class to add methods to apply standards to this object -class OpenStudio::Model::WaterHeaterMixed +class Standard + # @!group WaterHeaterMixed + # Applies the standard efficiency ratings and typical losses and paraisitic loads to this object. # Efficiency and skin loss coefficient (UA) # Per PNNL http://www.energycodes.gov/sites/default/files/documents/PrototypeModelEnhancements_2014_0.pdf # Appendix A: Service Water Heating # - # @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013' - # @param standards [Hash] the OpenStudio_Standards spreadsheet in hash format # @return [Bool] true if successful, false if not - def apply_efficiency(template) + def water_heater_mixed_apply_efficiency(water_heater_mixed) # Get the capacity of the water heater # TODO add capability to pull autosized water heater capacity # if the Sizing:WaterHeater object is ever implemented in OpenStudio. - capacity_w = heaterMaximumCapacity + capacity_w = water_heater_mixed.heaterMaximumCapacity if capacity_w.empty? - OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{name}, cannot find capacity, standard will not be applied.") + OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name}, cannot find capacity, standard will not be applied.") return false else capacity_w = capacity_w.get end capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get - capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get # Get the volume of the water heater # TODO add capability to pull autosized water heater volume # if the Sizing:WaterHeater object is ever implemented in OpenStudio. - volume_m3 = tankVolume + volume_m3 = water_heater_mixed.tankVolume if volume_m3.empty? - OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{name}, cannot find volume, standard will not be applied.") + OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name}, cannot find volume, standard will not be applied.") return false else volume_m3 = volume_m3.get end volume_gal = OpenStudio.convert(volume_m3, 'm^3', 'gal').get # Get the heater fuel type - fuel_type = heaterFuelType + fuel_type = water_heater_mixed.heaterFuelType unless fuel_type == 'NaturalGas' || fuel_type == 'Electricity' - OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{name}, fuel type of #{fuel_type} is not yet supported, standard will not be applied.") + OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name}, fuel type of #{fuel_type} is not yet supported, standard will not be applied.") end + # Get the water heater properties + search_criteria = {} + search_criteria['template'] = template + search_criteria['fuel_type'] = fuel_type + wh_props = model_find_object(standards_data['water_heaters'], search_criteria, capacity_btu_per_hr) + unless wh_props + OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.BoilerHotWater', "For #{water_heater_mixed.name}, cannot find water heater properties, cannot apply efficiency standard.") + return false + end + # Calculate the water heater efficiency and - # skin loss coefficient (UA) - # Calculate the energy factor (EF) - # From PNNL http://www.energycodes.gov/sites/default/files/documents/PrototypeModelEnhancements_2014_0.pdf - # Appendix A: Service Water Heating + # skin loss coefficient (UA) using different methods, + # depending on the metrics specified by the standard water_heater_eff = nil ua_btu_per_hr_per_f = nil - sl_btu_per_hr = nil - case fuel_type - when 'Electricity' - case template - when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013' - if capacity_w <= 12_000 # I think this should be 12000W, use variable capacity_w instead of capacity_btu_per_hr (as per PNNL doc) - # Fixed water heater efficiency per PNNL - water_heater_eff = 1 - # Calculate the minimum Energy Factor (EF) - base_ef, vol_drt = case template - when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007' - [0.93, 0.00132] - when '90.1-2010' - [0.97, 0.00132] - when '90.1-2013' - [0.97, 0.00035] - end + # Rarely specified by thermal efficiency alone + if wh_props['thermal_efficiency'] && !wh_props['standby_loss_capacity_allowance'] + et = wh_props['thermal_efficiency'] + water_heater_eff = et + # Fixed UA + ua_btu_per_hr_per_f = 11.37 + end - ef = base_ef - (vol_drt * volume_gal) - # Calculate the skin loss coefficient (UA) - ua_btu_per_hr_per_f = (41_094 * (1 / ef - 1)) / (24 * 67.5) - else - # Fixed water heater efficiency per PNNL - water_heater_eff = 1 - # Calculate the skin loss coefficient (UA) - case template - when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010' - # Calculate the max allowable standby loss (SL) - sl_btu_per_hr = 20 + (35 * Math.sqrt(volume_gal)) - # Calculate the skin loss coefficient (UA) - ua_btu_per_hr_per_f = sl_btu_per_hr / 70 - when '90.1-2013' - # Calculate the percent loss per hr - hrly_loss_pct = (0.3 + 27 / volume_gal) / 100 - # Convert to Btu/hr, assuming: - # Water at 120F, density = 8.25 lb/gal - # 1 Btu to raise 1 lb of water 1 F - # Therefore 8.25 Btu / gal of water * deg F - # 70F delta-T between water and zone - hrly_loss_btu_per_hr = hrly_loss_pct * volume_gal * 8.25 * 70 - # Calculate the skin loss coefficient (UA) - ua_btu_per_hr_per_f = hrly_loss_btu_per_hr / 70 - end - end - - when 'NECB 2011' - volume_l_per_s = volume_m3 * 1000 - if capacity_btu_per_hr <= OpenStudio.convert(12, 'kW', 'Btu/hr').get - # Fixed water heater efficiency per PNNL - water_heater_eff = 1 - # Calculate the max allowable standby loss (SL) - sl_w = if volume_l_per_s < 270 - 40 + 0.2 * volume_l_per_s # assume bottom inlet - else - 0.472 * volume_l_per_s - 33.5 - end # assume bottom inlet - sl_btu_per_hr = OpenStudio.convert(sl_w, 'W', 'Btu/hr').get - else - # Fixed water heater efficiency per PNNL - water_heater_eff = 1 - # Calculate the max allowable standby loss (SL) # use this - NECB does not give SL calculation for cap > 12 kW - sl_btu_per_hr = 20 + (35 * Math.sqrt(volume_gal)) - end + # Typically specified this way for small electric water heaters + # and small natural gas water heaters + if wh_props['energy_factor_base'] && wh_props['energy_factor_volume_derate'] + # Calculate the energy factor (EF) + base_ef = wh_props['energy_factor_base'] + vol_drt = wh_props['energy_factor_volume_derate'] + ef = base_ef - (vol_drt * volume_gal) + # Calculate the skin loss coefficient (UA) + # differently depending on the fuel type + if fuel_type == 'Electricity' + # Fixed water heater efficiency per PNNL + water_heater_eff = 1 + ua_btu_per_hr_per_f = (41_094 * (1 / ef - 1)) / (24 * 67.5) + elsif fuel_type == 'NaturalGas' + # Fixed water heater thermal efficiency per PNNL + water_heater_eff = 0.82 + # Calculate the Recovery Efficiency (RE) + # based on a fixed capacity of 75,000 Btu/hr + # and a fixed volume of 40 gallons by solving + # this system of equations: + # ua = (1/.95-1/re)/(67.5*(24/41094-1/(re*cap))) + # 0.82 = (ua*67.5+cap*re)/cap + cap = 75_000.0 + re = (Math.sqrt(6724 * ef**2 * cap**2 + 40_409_100 * ef**2 * cap - 28_080_900 * ef * cap + 29_318_000_625 * ef**2 - 58_636_001_250 * ef + 29_318_000_625) + 82 * ef * cap + 171_225 * ef - 171_225) / (200 * ef * cap) # Calculate the skin loss coefficient (UA) - ua_btu_per_hr_per_f = sl_btu_per_hr / 70 + # based on the actual capacity. + ua_btu_per_hr_per_f = (water_heater_eff - re) * capacity_btu_per_hr / 67.5 end + end - when 'NaturalGas' - case template # TODO: inconsistency; ref buildings don't calculate water heater UA the same way - when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004' - water_heater_eff = 0.78 - ua_btu_per_hr_per_f = 11.37 - when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013', 'NECB 2011' - if capacity_btu_per_hr <= 75_000 - # Fixed water heater thermal efficiency per PNNL - water_heater_eff = 0.82 - # Calculate the minimum Energy Factor (EF) - base_ef, vol_drt = case template - when '90.1-2004', '90.1-2007' - [0.62, 0.0019] - when '90.1-2010', 'NECB 2011' - [0.67, 0.0019] - when '90.1-2013' - [0.67, 0.0005] - end + # Typically specified this way for large electric water heaters + if wh_props['standby_loss_base'] && wh_props['standby_loss_volume_allowance'] + # Fixed water heater efficiency per PNNL + water_heater_eff = 1 + # Calculate the max allowable standby loss (SL) + sl_base = wh_props['standby_loss_base'] + sl_drt = wh_props['standby_loss_volume_allowance'] + sl_btu_per_hr = sl_base + (sl_drt * Math.sqrt(volume_gal)) + # Calculate the skin loss coefficient (UA) + ua_btu_per_hr_per_f = sl_btu_per_hr / 70 + end - ef = base_ef - (vol_drt * volume_gal) - # Calculate the Recovery Efficiency (RE) - # based on a fixed capacity of 75,000 Btu/hr - # and a fixed volume of 40 gallons by solving - # this system of equations: - # ua = (1/.95-1/re)/(67.5*(24/41094-1/(re*cap))) - # 0.82 = (ua*67.5+cap*re)/cap - cap = 75_000.0 - re = (Math.sqrt(6724 * ef**2 * cap**2 + 40_409_100 * ef**2 * cap - 28_080_900 * ef * cap + 29_318_000_625 * ef**2 - 58_636_001_250 * ef + 29_318_000_625) + 82 * ef * cap + 171_225 * ef - 171_225) / (200 * ef * cap) - # Calculate the skin loss coefficient (UA) - # based on the actual capacity. - ua_btu_per_hr_per_f = (water_heater_eff - re) * capacity_btu_per_hr / 67.5 - else - # Thermal efficiency requirement from 90.1 - et = 0.8 - # Calculate the max allowable standby loss (SL) - cap_adj, vol_drt = case template - when '90.1-2004', '90.1-2007', '90.1-2010', 'NECB 2011' - [800, 110] - when '90.1-2013' - [799, 16.6] - end - sl_btu_per_hr = (capacity_btu_per_hr / cap_adj + vol_drt * Math.sqrt(volume_gal)) - # Calculate the skin loss coefficient (UA) - ua_btu_per_hr_per_f = (sl_btu_per_hr * et) / 70 - # Calculate water heater efficiency - water_heater_eff = (ua_btu_per_hr_per_f * 70 + capacity_btu_per_hr * et) / capacity_btu_per_hr - end - end + # Typically specified this way for newer large electric water heaters + if wh_props['hourly_loss_base'] && wh_props['hourly_loss_volume_allowance'] + # Fixed water heater efficiency per PNNL + water_heater_eff = 1 + # Calculate the percent loss per hr + hr_loss_base = wh_props['hourly_loss_base'] + hr_loss_allow = wh_props['hourly_loss_volume_allowance'] + hrly_loss_pct = (hr_loss_base + hr_loss_allow / volume_gal) / 100 + # Convert to Btu/hr, assuming: + # Water at 120F, density = 8.25 lb/gal + # 1 Btu to raise 1 lb of water 1 F + # Therefore 8.25 Btu / gal of water * deg F + # 70F delta-T between water and zone + hrly_loss_btu_per_hr = hrly_loss_pct * volume_gal * 8.25 * 70 + # Calculate the skin loss coefficient (UA) + ua_btu_per_hr_per_f = hrly_loss_btu_per_hr / 70 end + # Typically specified this way for large natural gas water heaters + if wh_props['standby_loss_capacity_allowance'] && wh_props['standby_loss_volume_allowance'] && wh_props['thermal_efficiency'] + sl_cap_adj = wh_props['standby_loss_capacity_allowance'] + sl_vol_drt = wh_props['standby_loss_volume_allowance'] + et = wh_props['thermal_efficiency'] + # Calculate the max allowable standby loss (SL) + sl_btu_per_hr = (capacity_btu_per_hr / sl_cap_adj + sl_vol_drt * Math.sqrt(volume_gal)) + # Calculate the skin loss coefficient (UA) + ua_btu_per_hr_per_f = (sl_btu_per_hr * et) / 70 + # Calculate water heater efficiency + water_heater_eff = (ua_btu_per_hr_per_f * 70 + capacity_btu_per_hr * et) / capacity_btu_per_hr + end + + # Ensure that efficiency and UA were both set\ + if water_heater_eff.nil? + OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name}, cannot calculate efficiency, cannot apply efficiency standard.") + return false + end + + if ua_btu_per_hr_per_f.nil? + OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name}, cannot calculate UA, cannot apply efficiency standard.") + return false + end + # Convert to SI ua_btu_per_hr_per_c = OpenStudio.convert(ua_btu_per_hr_per_f, 'Btu/hr*R', 'W/K').get # Set the water heater properties # Efficiency - setHeaterThermalEfficiency(water_heater_eff) + water_heater_mixed.setHeaterThermalEfficiency(water_heater_eff) # Skin loss - setOffCycleLossCoefficienttoAmbientTemperature(ua_btu_per_hr_per_c) - setOnCycleLossCoefficienttoAmbientTemperature(ua_btu_per_hr_per_c) + water_heater_mixed.setOffCycleLossCoefficienttoAmbientTemperature(ua_btu_per_hr_per_c) + water_heater_mixed.setOnCycleLossCoefficienttoAmbientTemperature(ua_btu_per_hr_per_c) # TODO: Parasitic loss (pilot light) # PNNL document says pilot lights were removed, but IDFs # still have the on/off cycle parasitic fuel consumptions filled in - setOnCycleParasiticFuelType(fuel_type) + water_heater_mixed.setOnCycleParasiticFuelType(fuel_type) # self.setOffCycleParasiticFuelConsumptionRate(??) - setOnCycleParasiticHeatFractiontoTank(0) - setOffCycleParasiticFuelType(fuel_type) + water_heater_mixed.setOnCycleParasiticHeatFractiontoTank(0) + water_heater_mixed.setOffCycleParasiticFuelType(fuel_type) # self.setOffCycleParasiticFuelConsumptionRate(??) - setOffCycleParasiticHeatFractiontoTank(0.8) + water_heater_mixed.setOffCycleParasiticHeatFractiontoTank(0.8) - # set part-load performance curve - if template == 'NECB 2011' && fuel_type == 'NaturalGas' - plf_vs_plr_curve = model.add_curve('SWH-EFFFPLR-NECB2011') - setPartLoadFactorCurve(plf_vs_plr_curve) - end - # Append the name with standards information - setName("#{name} #{water_heater_eff.round(3)} Therm Eff") - OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.WaterHeaterMixed', "For #{template}: #{name}; thermal efficiency = #{water_heater_eff.round(3)}, skin-loss UA = #{ua_btu_per_hr_per_f.round}Btu/hr") + water_heater_mixed.setName("#{water_heater_mixed.name} #{water_heater_eff.round(3)} Therm Eff") + OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.WaterHeaterMixed', "For #{template}: #{water_heater_mixed.name}; thermal efficiency = #{water_heater_eff.round(3)}, skin-loss UA = #{ua_btu_per_hr_per_f.round}Btu/hr") return true end # Applies the correct fuel type for the water heaters # in the baseline model. For most standards and for most building # types, the baseline uses the same fuel type as the proposed. - # However, certain standards like 90.1-2013 require a change - # in some scenarios. # # @param building_type [String] the building type # @return [Bool] returns true if successful, false if not. - def apply_prm_baseline_fuel_type(template, building_type) - # For all standards except 90.1-2013 - # baseline is same as proposed per - # Table G3.1 item 11.b - unless template == '90.1-2013' - return true - end - - # Determine the building-type specific - # fuel requirements from Table G3.1.1-2 - new_fuel = nil - case building_type - when 'SecondarySchool', 'PrimarySchool', # School/university - 'SmallHotel', # Motel - 'LargeHotel', # Hotel - 'QuickServiceRestaurant', # Dining: Cafeteria/fast food - 'FullServiceRestaurant', # Dining: Family - 'MidriseApartment', 'HighriseApartment', # Multifamily - 'Hospital', # Hospital - 'Outpatient' # Health-care clinic - new_fuel = 'NaturalGas' - when 'SmallOffice', 'MediumOffice', 'LargeOffice', # Office - 'RetailStandalone', 'RetailStripmall', # Retail - 'Warehouse' # Warehouse - new_fuel = 'Electricity' - else - new_fuel = 'NaturalGas' - end - - # Change the fuel type if necessary - old_fuel = heaterFuelType - unless new_fuel == old_fuel - setHeaterFuelType(new_fuel) - OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.WaterHeaterMixed', "For #{name}, changed baseline water heater fuel from #{old_fuel} to #{new_fuel}.") - end - - return true + def water_heater_mixed_apply_prm_baseline_fuel_type(water_heater_mixed, building_type) + # baseline is same as proposed per Table G3.1 item 11.b + return true # Do nothing end # Finds capacity in Btu/hr # # @return [Double] capacity in Btu/hr to be used for find object - def find_capacity() - + def water_heater_mixed_find_capacity(water_heater_mixed) # Get the coil capacity capacity_w = nil - if self.heaterMaximumCapacity.is_initialized - capacity_w = self.heaterMaximumCapacity.get - elsif self.autosizedHeaterMaximumCapacity.is_initialized - capacity_w = self.autosizedHeaterMaximumCapacity.get + if water_heater_mixed.heaterMaximumCapacity.is_initialized + capacity_w = water_heater_mixed.heaterMaximumCapacity.get + elsif water_heater_mixed.autosizedHeaterMaximumCapacity.is_initialized + capacity_w = water_heater_mixed.autosizedHeaterMaximumCapacity.get else - OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{self.name} capacity is not available.") + OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name} capacity is not available.") return false end # Convert capacity to Btu/hr - capacity_btu_per_hr = OpenStudio.convert(capacity_w, "W", "Btu/hr").get + capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get return capacity_btu_per_hr - end end