Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 72 additions & 58 deletions HPXMLtoOpenStudio/resources/hotwater_appliances.rb
Original file line number Diff line number Diff line change
Expand Up @@ -382,27 +382,23 @@ def self.apply(runner, model, weather, spaces, hpxml_bldg, hpxml_header, schedul
frac_low_flow_fixtures = 0.0
end

# Calculate mixed water fractions
t_mix = 105.0 # F, Temperature of mixed water at fixtures
avg_setpoint_temp = 0.0 # WH Setpoint: Weighted average by fraction DHW load served
hpxml_bldg.water_heating_systems.each do |water_heating_system|
wh_setpoint = water_heating_system.temperature
wh_setpoint = Defaults.get_water_heater_temperature(eri_version) if wh_setpoint.nil? # using detailed schedules
avg_setpoint_temp += wh_setpoint * water_heating_system.fraction_dhw_load_served

# Set mains water temperature
swmt = model.getSiteWaterMainsTemperature
swmt.setCalculationMethod('Correlation')
swmt.setAnnualAverageOutdoorAirTemperature(UnitConversions.convert(weather.data.AnnualAvgDrybulb, 'F', 'C'))
swmt.setMaximumDifferenceInMonthlyAverageOutdoorAirTemperatures(UnitConversions.convert(weather.data.MonthlyAvgDrybulbs.max - weather.data.MonthlyAvgDrybulbs.min, 'deltaF', 'deltaC'))
if (not hot_water_distribution.dwhr_efficiency.nil?)
# FIXME: Requires new OS/E+ feature
# Calculate DWHR temperature multiplier/offset
# dwhr_factor, dhwr_in_t = get_dwhr_values(nbeds, n_occ, unit_type, hot_water_distribution, frac_low_flow_fixtures)
# temp_mult = 1.0 - dwhr_factor
# temp_offset = dwhr_factor * dhwr_in_t
# temp_offset_c = 5.0/9.0 * (32.0 * temp_mult + temp_offset - 32.0) # Convert adjustment to EnergyPlus input of deg-C
# swmt.setTemperatureMultiplier(temp_mult)
# swmt.setTemperatureOffset(temp_offset_c)
end
daily_wh_inlet_temperatures = calc_water_heater_daily_inlet_temperatures(weather, nbeds, n_occ, unit_type, hot_water_distribution, frac_low_flow_fixtures)
daily_wh_inlet_temperatures_c = daily_wh_inlet_temperatures.map { |t| UnitConversions.convert(t, 'F', 'C') }
daily_mw_fractions = calc_mixed_water_daily_fractions(daily_wh_inlet_temperatures, avg_setpoint_temp, t_mix)

# Schedules
# Replace mains water temperature schedule with water heater inlet temperature schedule.
# These are identical unless there is a DWHR.
start_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(1), 1, hpxml_header.sim_calendar_year)
timestep_day = OpenStudio::Time.new(1, 0)
time_series_tmains = OpenStudio::TimeSeries.new(start_date, timestep_day, OpenStudio::createVector(daily_wh_inlet_temperatures_c), 'C')
schedule_tmains = OpenStudio::Model::ScheduleInterval.fromTimeSeries(time_series_tmains, model).get
schedule_tmains.setName('mains temperature schedule')
model.getSiteWaterMainsTemperature.setTemperatureSchedule(schedule_tmains)

mw_temp_schedule = Model.add_schedule_constant(
model,
Expand Down Expand Up @@ -432,13 +428,18 @@ def self.apply(runner, model, weather, spaces, hpxml_bldg, hpxml_header, schedul
end
end

if Constants::ERIVersions.index(eri_version) < Constants::ERIVersions.index('2014A')
# Calculate annual average mixed water fraction
avg_mw_fraction = calc_mixed_water_fraction(hpxml_bldg, t_mix, weather, nbeds, n_occ, unit_type, hot_water_distribution, frac_low_flow_fixtures, eri_version)
end

hpxml_bldg.water_heating_systems.each do |water_heating_system|
non_solar_fraction = 1.0 - Waterheater.get_water_heater_solar_fraction(water_heating_system, hpxml_bldg)

gpd_frac = water_heating_system.fraction_dhw_load_served # Fixtures fraction
if gpd_frac > 0

fx_gpd = get_fixtures_gpd(eri_version, nbeds, frac_low_flow_fixtures, daily_mw_fractions, fixtures_usage_multiplier, n_occ)
fx_gpd = get_fixtures_gpd(eri_version, nbeds, frac_low_flow_fixtures, avg_mw_fraction, fixtures_usage_multiplier, n_occ)
w_gpd = get_dist_waste_gpd(eri_version, nbeds, has_uncond_bsmnt, has_cond_bsmnt, cfa, ncfl, hot_water_distribution, frac_low_flow_fixtures, fixtures_usage_multiplier, n_occ)

fx_peak_flow = nil
Expand Down Expand Up @@ -1037,15 +1038,24 @@ def self.get_fridge_or_freezer_coefficients_schedule(model, col_name, obj_name,
return schedule
end

# Calculates Drain Water Heat Recovery (DWHR) factors.
# Returns Drain Water Heat Recovery (DWHR) aggregate factor and temperature.
#
# Source: ANSI/RESNET/ICC 301
#
# @param nbeds_eq [Integer] Number of bedrooms (or equivalent bedrooms, as adjusted by the number of occupants) in the dwelling unit
# @param nbeds [Integer] Number of bedrooms in the dwelling unit
# @param n_occ [Double] Number of occupants in the dwelling unit
# @param unit_type [String] Type of dwelling unit (HXPML::ResidentialTypeXXX)
# @param hot_water_distribution [HPXML::HotWaterDistribution] The HPXML hot water distribution system of interest
# @param frac_low_flow_fixtures [Double] The fraction of fixtures considered low-flow
# @return [Array<Double, Double, Double, Double, Double>] Effectiveness (frac), fraction of water impacted by DWHR, piping loss coefficient, location factor, fixture factor
def self.get_dwhr_factors(nbeds_eq, hot_water_distribution, frac_low_flow_fixtures)
# @return [Array<Double, Double>] Aggregate factor (frac), temperature (F)
def self.get_dwhr_values(nbeds, n_occ, unit_type, hot_water_distribution, frac_low_flow_fixtures)
if n_occ == 0
# Operational calculation w/ zero occupants
nbeds_eq = 0
else
nbeds_eq = Defaults.get_equivalent_nbeds(nbeds, n_occ, unit_type)
end

# ANSI/RESNET/ICC 301-2022 Eq. 4.2-42

eff_adj = 1.0 + 0.082 * frac_low_flow_fixtures
Expand Down Expand Up @@ -1073,34 +1083,50 @@ def self.get_dwhr_factors(nbeds_eq, hot_water_distribution, frac_low_flow_fixtur
fix_f = 0.5
end

return eff_adj, i_frac, plc, loc_f, fix_f
dwhr_factor = eff_adj * i_frac * plc * loc_f * fix_f * hot_water_distribution.dwhr_efficiency # Aggregate factor
dhwr_in_t = 97.0 # deg-F

return dwhr_factor, dhwr_in_t
end

# Calculates daily water heater inlet temperatures, which includes an adjustment if
# there is a drain water heat recovery device.
#
# Source: ANSI/RESNET/ICC 301
#
# @return [Array<Double>] Daily water heater inlet temperatures (F)
def self.calc_water_heater_daily_inlet_temperatures()
end

# Calculates the annual average mixed water adjustment fraction. The fraction converts from
# gallons of mixed water to gallons of hot water that needs to be served by the water heater.
#
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @param t_mix [Double] Temperature of mixed water at fixtures (F)
# @param weather [WeatherFile] Weather object containing EPW information
# @param nbeds [Integer] Number of bedrooms in the dwelling unit
# @param n_occ [Double] Number of occupants in the dwelling unit
# @param unit_type [String] Type of dwelling unit (HXPML::ResidentialTypeXXX)
# @param hot_water_distribution [HPXML::HotWaterDistribution] The HPXML hot water distribution system of interest
# @param frac_low_flow_fixtures [Double] The fraction of fixtures considered low-flow
# @return [Array<Double>] Daily water heater inlet temperatures (F)
def self.calc_water_heater_daily_inlet_temperatures(weather, nbeds, n_occ, unit_type, hot_water_distribution, frac_low_flow_fixtures)
if n_occ == 0
# Operational calculation w/ zero occupants
nbeds_eq = 0
else
nbeds_eq = Defaults.get_equivalent_nbeds(nbeds, n_occ, unit_type)
# @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions
# @return [Double] Annual average mixed water adjustment fraction
def self.calc_mixed_water_fraction(hpxml_bldg, t_mix, weather, nbeds, n_occ, unit_type, hot_water_distribution, frac_low_flow_fixtures, eri_version)
# WH Setpoint: Weighted average by fraction DHW load served
t_set = 0.0
hpxml_bldg.water_heating_systems.each do |water_heating_system|
wh_setpoint = water_heating_system.temperature
wh_setpoint = Defaults.get_water_heater_temperature(eri_version) if wh_setpoint.nil? # using detailed schedules
t_set += wh_setpoint * water_heating_system.fraction_dhw_load_served
end

# Calculates daily water heater inlet temperatures, which includes an adjustment if
# there is a drain water heat recovery device.
wh_temps_daily = weather.data.MainsDailyTemps.dup
if (not hot_water_distribution.dwhr_efficiency.nil?)
# Per ANSI/RESNET/ICC 301
dwhr_eff_adj, dwhr_iFrac, dwhr_plc, dwhr_locF, dwhr_fixF = get_dwhr_factors(nbeds_eq, hot_water_distribution, frac_low_flow_fixtures)
dwhr_factor, dhwr_in_t = get_dwhr_values(nbeds, n_occ, unit_type, hot_water_distribution, frac_low_flow_fixtures)

# Adjust inlet temperatures
dwhr_inT = 97.0 # F
for day in 0..wh_temps_daily.size - 1
dwhr_WHinTadj = dwhr_iFrac * (dwhr_inT - wh_temps_daily[day]) * hot_water_distribution.dwhr_efficiency * dwhr_eff_adj * dwhr_plc * dwhr_locF * dwhr_fixF
dwhr_WHinTadj = (dhwr_in_t - wh_temps_daily[day]) * dwhr_factor
wh_temps_daily[day] = (wh_temps_daily[day] + dwhr_WHinTadj).round(3)
end
else
Expand All @@ -1109,24 +1135,13 @@ def self.calc_water_heater_daily_inlet_temperatures(weather, nbeds, n_occ, unit_
end
end

return wh_temps_daily
end

# Calculates the daily mixed water adjustment fractions. These fractions convert from
# gallons of mixed water to gallons of hot water that needs to be served by the water heater.
#
# @param daily_wh_inlet_temperatures [Array<Double>] Daily water heater inlet temperatures (F)
# @param t_set [Double] Water heater setpoint temperature (F)
# @param t_use [Double] Temperature of mixed water at fixtures (F)
# @return [Array<Double>] Daily mixed water adjustment fractions
def self.calc_mixed_water_daily_fractions(daily_wh_inlet_temperatures, t_set, t_use)
# Per ANSI/RESNET/ICC 301
# Calculate daily adjustment fractions
adj_f_mix = []
for day in 0..daily_wh_inlet_temperatures.size - 1
adj_f_mix << (1.0 - ((t_set - t_use) / (t_set - daily_wh_inlet_temperatures[day]))).round(4)
for day in 0..wh_temps_daily.size - 1
adj_f_mix << (1.0 - ((t_set - t_mix) / (t_set - wh_temps_daily[day]))).round(4)
end

return adj_f_mix
return adj_f_mix.sum / Float(adj_f_mix.size)
end

# Calculates annual energy use for a recirculation (or shared recirculation) hot water
Expand Down Expand Up @@ -1195,11 +1210,11 @@ def self.get_fixtures_effectiveness(frac_low_flow_fixtures)
# @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions
# @param nbeds [Integer] Number of bedrooms in the dwelling unit
# @param frac_low_flow_fixtures [Double] The fraction of fixtures considered low-flow
# @param daily_mw_fractions [Array<Double>] Daily mixed water adjustment fractions
# @param avg_mw_fraction [Double] Annual average mixed water fraction
# @param fixtures_usage_multiplier [Double] Occupant usage multiplier
# @param n_occ [Double] Number of occupants in the dwelling unit
# @return [Double] Mixed water use (gal/day)
def self.get_fixtures_gpd(eri_version, nbeds, frac_low_flow_fixtures, daily_mw_fractions, fixtures_usage_multiplier = 1.0, n_occ = nil)
def self.get_fixtures_gpd(eri_version, nbeds, frac_low_flow_fixtures, avg_mw_fraction, fixtures_usage_multiplier = 1.0, n_occ = nil)
if Constants::ERIVersions.index(eri_version) >= Constants::ERIVersions.index('2014A')
if n_occ.nil? # Asset calculation
ref_f_gpd = 14.6 + 10.0 * nbeds # ANSI/RESNET/ICC 301-2022 Eq. 4.2-29 (refFgpd)
Expand All @@ -1210,9 +1225,8 @@ def self.get_fixtures_gpd(eri_version, nbeds, frac_low_flow_fixtures, daily_mw_f
return f_eff * ref_f_gpd * fixtures_usage_multiplier
else
hw_gpd = 30.0 + 10.0 * nbeds # Table 4.2.2(1) Service water heating systems
# Convert to mixed water gpd
avg_mw_fraction = daily_mw_fractions.reduce(:+) / daily_mw_fractions.size.to_f
return hw_gpd / avg_mw_fraction * fixtures_usage_multiplier
mw_gpd = hw_gpd / avg_mw_fraction # Convert to mixed water gpd
return mw_gpd * fixtures_usage_multiplier
end
end

Expand Down
Loading