Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unmet water heater loads EMS program #1502

Draft
wants to merge 30 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6335d24
Add unmet loads EMS program
jmaguire1 Oct 4, 2023
e70d0e9
busted by rubocop :)
jmaguire1 Oct 4, 2023
23381f8
First cut at setting up annual unmet water heating loads.
joseph-robertson Oct 5, 2023
ddc558c
Merge branch 'master' into unmet_wh_loads
joseph-robertson Oct 5, 2023
dffe6f6
Wrong direction for sag calculation
jmaguire1 Oct 6, 2023
92c0e97
Some updates to break out showers from "fixtures"
jmaguire1 Oct 12, 2023
4aa4a0c
One more change towards adding showers back in
jmaguire1 Oct 12, 2023
ec8cef2
Add shower column to constants
jmaguire1 Oct 12, 2023
9d3b025
Fix shower schedules for smooth occupancy
jmaguire1 Oct 12, 2023
7658e4f
Still in progress, but: working on restructuring the calculation base…
jmaguire1 Oct 30, 2023
6a8b015
Working again. Still needs some cleanup and review
jmaguire1 Oct 31, 2023
44c4c61
Merge remote-tracking branch 'remotes/origin/master' into unmet_wh_loads
jmaguire1 Oct 31, 2023
c59eaec
Add to changelog, fix merge conflicts, rubocop
jmaguire1 Oct 31, 2023
6d68919
Fix some typos
jmaguire1 Oct 31, 2023
a58dcbf
rename constant for consistency with schedule:file
jmaguire1 Nov 1, 2023
04b94ad
Major refactor of the unmet WH load calculation
jmaguire1 Nov 16, 2023
227138e
Fix unmet energy calc (incorrectly passing shower peak flow rate)
jmaguire1 Dec 4, 2023
e250890
Merge branch 'master' of https://github.com/NREL/OpenStudio-HPXML int…
jmaguire1 Jan 10, 2024
caae214
remove print statement, rubocop
jmaguire1 Jan 10, 2024
2bae66b
Update some output variable names, add showers to unavailable_periods…
jmaguire1 Jan 17, 2024
3044382
Merge branch 'master' of https://github.com/NREL/OpenStudio-HPXML int…
jmaguire1 Jan 24, 2024
0379ace
Bit of code cleanup and adding FIXMEs to address.
joseph-robertson Jan 24, 2024
70a9868
Oops, showers_schedule_obj does get used.
joseph-robertson Jan 24, 2024
015383e
Merge branch 'master' of https://github.com/NREL/OpenStudio-HPXML int…
jmaguire1 Feb 5, 2024
a58ada4
Merge branch 'master' of https://github.com/NREL/OpenStudio-HPXML int…
jmaguire1 Apr 10, 2024
6421080
oops, missed one update in the merge
jmaguire1 Apr 10, 2024
c4239f0
Merge branch 'master' of https://github.com/NREL/OpenStudio-HPXML int…
jmaguire1 May 14, 2024
258c28f
Remove total shower time, only output unmet shower time (in hours) an…
jmaguire1 May 14, 2024
921a0f0
oops, missed removing shower time in one spot
jmaguire1 May 14, 2024
dc3a752
Some fixes for unmet showers with multiple WHs.
jmaguire1 May 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion BuildResidentialScheduleFile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Absolute/relative path of the HPXML file.

**Schedules: Column Names**

A comma-separated list of the column names to generate. If not provided, defaults to all columns. Possible column names are: occupants, lighting_interior, lighting_garage, cooking_range, dishwasher, clothes_washer, clothes_dryer, ceiling_fan, plug_loads_other, plug_loads_tv, hot_water_dishwasher, hot_water_clothes_washer, hot_water_fixtures.
A comma-separated list of the column names to generate. If not provided, defaults to all columns. Possible column names are: occupants, lighting_interior, lighting_garage, cooking_range, dishwasher, clothes_washer, clothes_dryer, ceiling_fan, plug_loads_other, plug_loads_tv, hot_water_dishwasher, hot_water_clothes_washer, hot_water_fixtures, hot_water_showers.

- **Name:** ``schedules_column_names``
- **Type:** ``String``
Expand Down
1 change: 1 addition & 0 deletions BuildResidentialScheduleFile/resources/schedules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ def create_stochastic_schedules(args:,
shower_activity_sch = aggregate_array(shower_activity_sch, @minutes_per_step)
shower_peak_flow = shower_activity_sch.max
showers = shower_activity_sch.map { |flow| flow / shower_peak_flow }
@schedules[SchedulesFile::ColumnHotWaterShowers] = showers

random_offset = (prng.rand * 2 * offset_range).to_i - offset_range
sink_activity_sch = sink_activity_sch.rotate(-4 * 60 + random_offset) # 4 am shifting
Expand Down
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ __New Features__
- Allows above-grade basements/crawlspaces defined solely with Wall (not FoundationWall) elements.
- Updates to 2022 EIA energy costs.
- Added README.md documentation for all OpenStudio measures.
- Added metrics for unmet hot water loads

__Bugfixes__
- Fixes battery resilience output to properly incorporate battery losses.
Expand Down
10 changes: 7 additions & 3 deletions HPXMLtoOpenStudio/measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1533,18 +1533,22 @@ def add_hot_water_and_appliances(runner, model, weather, spaces)
end

# Hot water fixtures and appliances
HotWaterAndAppliances.apply(model, runner, @hpxml_header, @hpxml_bldg, weather, spaces, hot_water_distribution,
solar_thermal_system, @eri_version, @schedules_file, plantloop_map,
@hpxml_header.unavailable_periods, @hpxml_bldg.building_construction.number_of_units,
showers_peak_flows = HotWaterAndAppliances.apply(model, runner, @hpxml_header, @hpxml_bldg, weather, spaces, hot_water_distribution,
solar_thermal_system, @eri_version, @schedules_file, plantloop_map,
@hpxml_header.unavailable_periods, @hpxml_bldg.building_construction.number_of_units,
@apply_ashrae140_assumptions)


if (not solar_thermal_system.nil?) && (not solar_thermal_system.collector_area.nil?) # Detailed solar water heater
loc_space, loc_schedule = get_space_or_schedule_from_location(solar_thermal_system.water_heating_system.location, model, spaces)
Waterheater.apply_solar_thermal(model, loc_space, loc_schedule, solar_thermal_system, plantloop_map, unit_multiplier)
end

# Add combi-system EMS program with water use equipment information
Waterheater.apply_combi_system_EMS(model, @hpxml_bldg.water_heating_systems, plantloop_map)

# Add unmet wh loads calculation
Waterheater.unmet_wh_loads_program(model, @hpxml_bldg.water_heating_systems, plantloop_map, showers_peak_flows)
end

def add_cooling_system(model, runner, weather, spaces, airloop_map)
Expand Down
8 changes: 8 additions & 0 deletions HPXMLtoOpenStudio/resources/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ def self.ObjectNameRoomAirConditioner
return 'room ac'
end

def self.ObjectNameShowers
return 'hot_water_showers'
end

def self.ObjectNameSolarHotWater
return 'solar hot water'
end
Expand All @@ -364,6 +368,10 @@ def self.ObjectNameUnmetHoursProgram
return 'unmet hours program'
end

def self.ObjectNameUnmetLoadsProgram
return 'unmet loads program'
end

def self.ObjectNameWaterHeater
return 'water heater'
end
Expand Down
1 change: 1 addition & 0 deletions HPXMLtoOpenStudio/resources/data/unavailable_periods.csv
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pool_heater,0,1
permanent_spa_pump,0,1
permanent_spa_heater,0,1
hot_water_fixtures,1,1
hot_water_showers,1,1
hot_water_recirculation_pump,1,1
general_water_use,1,0
hvac,0,1
Expand Down
52 changes: 52 additions & 0 deletions HPXMLtoOpenStudio/resources/hotwater_appliances.rb
Original file line number Diff line number Diff line change
Expand Up @@ -327,27 +327,56 @@ def self.apply(model, runner, hpxml_header, hpxml_bldg, weather, spaces, hot_wat
runner.registerWarning("Both '#{fixtures_col_name}' schedule file and weekend fractions provided; the latter will be ignored.") if !hpxml_bldg.water_heating.water_fixtures_weekend_fractions.nil?
runner.registerWarning("Both '#{fixtures_col_name}' schedule file and monthly multipliers provided; the latter will be ignored.") if !hpxml_bldg.water_heating.water_fixtures_monthly_multipliers.nil?
end

# Create shower schedule, used only for unmet load calculations
# Create separate shower schedule: Only used for calculating unmet loads. Shower hot water usage is part of the fixtures usage.
showers_schedule = nil
showers_col_name = SchedulesFile::Columns[:HotWaterShowers].name
if not schedules_file.nil?
showers_schedule = schedules_file.create_schedule_file(model, col_name: showers_col_name, schedule_type_limits_name: Constants.ScheduleTypeLimitsFraction)
end
if showers_schedule.nil?
showers_unavailable_periods = Schedule.get_unavailable_periods(runner, showers_col_name, unavailable_periods)
showers_weekday_sch = Schedule.ShowersWeekdayFractions # FIXME: should we expose HPXML elements for these? sounds like maybe not.
showers_weekend_sch = Schedule.ShowersWeekendFractions
showers_monthly_sch = Schedule.ShowersMonthlyMultipliers
showers_schedule_obj = MonthWeekdayWeekendSchedule.new(model, Constants.ObjectNameShowers, showers_weekday_sch, showers_weekend_sch, showers_monthly_sch, Constants.ScheduleTypeLimitsFraction, unavailable_periods: showers_unavailable_periods)
else
runner.registerWarning("Both '#{showers_col_name}' schedule file and weekday fractions provided; the latter will be ignored.") if !Schedule.ShowersWeekdayFractions.nil?
runner.registerWarning("Both '#{showers_col_name}' schedule file and weekend fractions provided; the latter will be ignored.") if !Schedule.ShowersWeekendFractions.nil?
runner.registerWarning("Both '#{showers_col_name}' schedule file and monthly multipliers provided; the latter will be ignored.") if !Schedule.ShowersMonthlyMultipliers.nil?
end
end

# create an array of peak flow to return
shower_peak_flows = {} # used for unmet wh load calculations
hpxml_bldg.water_heating_systems.each do |water_heating_system|
non_solar_fraction = 1.0 - Waterheater.get_water_heater_solar_fraction(water_heating_system, solar_thermal_system)

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

# For showers, calculate flow rates but don't add a WaterUse:Equipment object. Shower usage is included in fixtures and only used for tracking unmet loads
fx_gpd = get_fixtures_gpd(eri_version, nbeds, frac_low_flow_fixtures, daily_mw_fractions, fixtures_usage_multiplier)
shower_gpd = get_showers_gpd(eri_version, nbeds, frac_low_flow_fixtures, daily_mw_fractions, fixtures_usage_multiplier)
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)

fx_peak_flow = nil
shower_peak_flow = nil
if not schedules_file.nil?
fx_peak_flow = schedules_file.calc_peak_flow_from_daily_gpm(col_name: SchedulesFile::Columns[:HotWaterFixtures].name, daily_water: fx_gpd)
shower_peak_flow = schedules_file.calc_peak_flow_from_daily_gpm(col_name: SchedulesFile::Columns[:HotWaterShowers].name, daily_water: shower_gpd)
dist_water_peak_flow = schedules_file.calc_peak_flow_from_daily_gpm(col_name: SchedulesFile::Columns[:HotWaterFixtures].name, daily_water: w_gpd)
end
if fx_peak_flow.nil?
fx_peak_flow = fixtures_schedule_obj.calc_design_level_from_daily_gpm(fx_gpd)
shower_peak_flow = showers_schedule_obj.calc_design_level_from_daily_gpm(shower_gpd)
dist_water_peak_flow = fixtures_schedule_obj.calc_design_level_from_daily_gpm(w_gpd)
end

id = water_heating_system.id
shower_peak_flows[id] = shower_peak_flow * gpd_frac * non_solar_fraction

# Fixtures (showers, sinks, baths)
add_water_use_equipment(model, fixtures_obj_name, fx_peak_flow * gpd_frac * non_solar_fraction, fixtures_schedule, water_use_connections[water_heating_system.id], unit_multiplier, mw_temp_schedule)

Expand Down Expand Up @@ -470,6 +499,8 @@ def self.apply(model, runner, hpxml_header, hpxml_bldg, weather, spaces, hot_wat
add_other_equipment(model, Constants.ObjectNameGeneralWaterUseSensible, conditioned_space, water_design_level_sens, 1.0, 0.0, water_schedule, nil)
add_other_equipment(model, Constants.ObjectNameGeneralWaterUseLatent, conditioned_space, water_design_level_lat, 0.0, 1.0, water_schedule, nil)
end

return shower_peak_flows
end

def self.get_range_oven_default_values()
Expand Down Expand Up @@ -1088,6 +1119,27 @@ def self.get_fixtures_gpd(eri_version, nbeds, frac_low_flow_fixtures, daily_mw_f
return f_eff * ref_f_gpd * fixtures_usage_multiplier
end

def self.get_showers_gpd(eri_version, nbeds, frac_low_flow_fixtures, daily_mw_fractions, fixtures_usage_multiplier = 1.0)
if nbeds < 0.0
return 0.0
end

if Constants.ERIVersions.index(eri_version) < Constants.ERIVersions.index('2014A')
# Note that the standard only has a total hot water usage, it does not specify a fraction for showers. Assuming showers are 40% of total HW usage (based on BA Benchmark usage)
showers_gpd = 0.4 * (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 showers_gpd / avg_mw_fraction * fixtures_usage_multiplier
end

# ANSI/RESNET 301-2014 Addendum A-2015
# Amendment on Domestic Hot Water (DHW) Systems
ref_shower_gpd = 14.0 + 4.67 * nbeds # Based on BA Benchmark shower usage
f_eff = get_fixtures_effectiveness(frac_low_flow_fixtures)

return f_eff * ref_shower_gpd * fixtures_usage_multiplier
end

def self.get_water_gains_sens_lat(nbeds, general_water_use_usage_multiplier = 1.0)
# Table 4.2.2(3). Internal Gains for Reference Homes
sens_gains = (-1227.0 - 409.0 * nbeds) * general_water_use_usage_multiplier # Btu/day
Expand Down
6 changes: 6 additions & 0 deletions HPXMLtoOpenStudio/resources/output.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ class UHT
Cooling = 'Cooling'
end

class ULT
# Unmet Loads Types
HotWaterShowerE = 'Hot Water Shower Energy'
HotWaterShowerUnmetTime = 'Hot Water Shower Unmet Time'
end

class RT
# Resilience Types
Battery = 'Battery'
Expand Down