Skip to content

Commit

Permalink
Support EMM region breakouts in baseline load data
Browse files Browse the repository at this point in the history
Baseline hourly load data were updated to reflect EUSS loads, resolved by EMM region, building type, and end use. The previous expectation of ASHRAE-resolved base load data in ecm_prep has been replaced with handling of the EMM breakouts. ASHRAE-based breakouts of TSV metric information (e.g., peak days/hours) and measure TSV features (e.g., custom CSV saving shape data) remain and are still mapped as before to EMM regions. The latter maintains compatibility with existing flexibility (EE, DF, EE+DF) measure sets; in the future, updated measure savings shapes will likely use EMM region breakouts, requiring further updates to ecm_prep to accommodate. For now, relative hourly savings values for each existing measure with a CSV savings shape are applied to the updated baselines with the EMM breakouts. For efficiency measures without CSV savings shapes, an even annual relative performance improvement is applied across all baseline hourly load values.
  • Loading branch information
jtlangevin committed Jul 28, 2023
1 parent 682782e commit 0ec2ab4
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 17,557 deletions.
123 changes: 94 additions & 29 deletions ecm_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -2243,19 +2243,18 @@ def __init__(
bd_key = bd
if type(bd_key) != str:
bd_key = str(bd_key)
# Account for possible use of StandAlone naming in
# savings shape CSV, vs. Standalone naming in Scout's
# baseline load shapes file
# Account for possible use of "StandAlone" naming in
# savings shape CSV, vs. expected "Standalone"
if bd_key == "RetailStandAlone":
bd_key = "RetailStandalone"
# Account for possible use of MediumOffice naming
# in savings shape CSV, vs. MediumOfficeDetailed in
# Scout's baseline load shapes file
# Account for possible use of "MediumOffice" naming
# in savings shape CSV, vs. expected
# "MediumOfficeDetailed"
elif bd_key == "MediumOffice":
bd_key = "MediumOfficeDetailed"
# Account for possible use of MediumOffice naming
# in savings shape CSV, vs. MediumOfficeDetailed in
# Scout's baseline load shapes file
# Account for possible use of "LargeOffice" naming
# in savings shape CSV, vs. expected
# "LargeOfficeDetailed""
elif bd_key == "LargeOffice":
bd_key = "LargeOfficeDetailed"
# Initialize dict under the current end use and
Expand Down Expand Up @@ -6493,22 +6492,73 @@ def apply_tsv(self, load_fact, ash_cz_wts, eplus_bldg_wts,
for bldg in eplus_bldg_wts.keys():
# Find the appropriate key in the load shape information for the
# current EnergyPlus building type
load_fact_bldg_key = [
x for x in load_fact.keys() if (bldg in load_fact[x][
try:
load_fact_bldg_key = [
x for x in load_fact.keys() if (bldg in load_fact[x][
"represented building types"] or load_fact[x][
"represented building types"] == "all")][0]
except IndexError:
# Check for possible naming inconsistency between certain
# building type names used in baseline load data vs. expected
# reference case building type names
if bldg in [
"RetailStandalone", "MediumOfficeDetailed",
"LargeOfficeDetailed"]:
# Account for possible use of StandAlone naming in
# baseline load shapes file (vs. expected 'Standalone')
if bldg == "RetailStandalone":
bldg_adj = "RetailStandAlone"
# Account for possible use of MediumOffice naming
# in in baseline load shapes file (vs. expected
# 'MediumOfficeDetailed')
elif bldg == "MediumOffice":
bldg_adj = "MediumOfficeDetailed"
# Account for possible use of LargeOffice naming
# in in baseline load shapes file (vs. expected
# 'LargeOfficeDetailed')
elif bldg == "LargeOffice":
bldg_adj = "LargeOfficeDetailed"
else:
bldg_adj = None
raise ValueError(
"No representative baseline load shape data "
"available for building type '" + bldg +
"'. Check './supporting_data/tsv_data/tsv_load.gz "
"to ensure that this building type name is "
"correctly listed under 'represented building "
"types key")
# Redo search for appropriate bldg key in load shape data
load_fact_bldg_key = [
x for x in load_fact.keys() if (bldg_adj in load_fact[x][
"represented building types"] or load_fact[x][
"represented building types"] == "all")][0]

# Ensure that all applicable ASHRAE climate zones are represented
# in the keys for time sensitive metrics data; if a zone is not
# represented, remove it from the weighting and renormalize weights
ash_cz_wts = [
[x[0], x[1]] for x in ash_cz_wts if x[0] in
self.handyvars.tsv_climate_regions]
# Ensure that ASHRAE climate zone weightings sum to 1
ash_cz_renorm = sum([x[1] for x in ash_cz_wts])
if round(ash_cz_renorm, 2) != 1:
ash_cz_wts = [[x[0], (x[1] / ash_cz_renorm)] for
x in ash_cz_wts]
# Calculations for TSV metric outputs and/or any measures with
# time sensitive valuation features are based on data with ASHRAE
# climate zone breakouts, which must be mapped into the EMM
# region breakouts of the Scout microsegments. In these cases,
# find list of applicable ASHRAE regions and weights for mapping
# into the EMM region of the current microsegment, then implement
# the mapping via for loop below. In cases where TSV metrics
# or measure features are not present, set the list to a single
# region of "None" with weight of 1 (effectively avoiding mapping)
if opts.tsv_metrics is not False or len(
tsv_adjustments.keys()) != 0:
# Ensure that all applicable ASHRAE climate zones are
# represented in the keys for time sensitive metrics data; if
# a zone is not represented, remove it from the weighting and
# renormalize weights
ash_cz_wts = [
[x[0], x[1]] for x in ash_cz_wts if x[0] in
self.handyvars.tsv_climate_regions]
# Ensure that ASHRAE climate zone weightings sum to 1
ash_cz_renorm = sum([x[1] for x in ash_cz_wts])
if round(ash_cz_renorm, 2) != 1:
ash_cz_wts = [[x[0], (x[1] / ash_cz_renorm)] for
x in ash_cz_wts]
else:
ash_cz_wts = [[None, 1]]

# Loop through all ASHRAE/IECC climate zones (which load profiles
# are broken out by) that map to the current EMM region
for cz in ash_cz_wts:
Expand All @@ -6517,11 +6567,11 @@ def apply_tsv(self, load_fact, ash_cz_wts, eplus_bldg_wts,
load_fact_climate_key = cz[0]
# Flag for cases where multiple sets of system load shape
# information are germane to the current climate zone
if type(self.handyvars.cz_emm_map[cz[0]]) == int:
if cz[0] and type(self.handyvars.cz_emm_map[cz[0]]) == int:
mult_sysshp = False
mult_sysshp_key_metrics, mult_sysshp_key_save = (
"set 1" for n in range(2))
elif type(self.handyvars.cz_emm_map[cz[0]]) == dict:
elif cz[0] and type(self.handyvars.cz_emm_map[cz[0]]) == dict:
mult_sysshp = True
# Given multiple possible system load shape data
# sets, find the key for the set that is representative
Expand All @@ -6533,15 +6583,20 @@ def apply_tsv(self, load_fact, ash_cz_wts, eplus_bldg_wts,
y[0] for y in self.handyvars.cz_emm_map[cz[0]].items()
if self.handyvars.emm_name_num_map[mskeys[1]]
in y[1][1]][0] for n in range(2))
elif not cz[0]: # Case with no TSV mapping from ASH -> EMM
mult_sysshp, mult_sysshp_key_metrics, \
mult_sysshp_key_save = (None for n in range(3))
else:
raise ValueError(
"Unable to determine representative utility system "
"load data for climate " + cz[0])

# Set the weighting factor to map the current EPlus building
# and ASHRAE/IECC climate to Scout building and EMM region,
# and ASHRAE climate (which measure savings shapes may
# be broken out by) to Scout building and EMM region,
# and set the appropriate baseline load shape (8760 hourly
# fractions of annual load)
# fractions of annual load, broken out by EPlus building type
# and EMM region)
# Set the weighting factor; note EPlus/Scout building types map
# 1:1 for residential and thus no building type weighting is
# necessary here
Expand All @@ -6552,14 +6607,24 @@ def apply_tsv(self, load_fact, ash_cz_wts, eplus_bldg_wts,

# Set the baseline load shape

# Handle case where the load shape is not broken out by
# climate zone
# Handle case where the baseline load shape is not broken out
# by EMM region
try:
base_load_hourly = load_fact[
load_fact_bldg_key]["load shape"][cz[0]]
load_fact_bldg_key]["load shape"][mskeys[1]]
except (KeyError, TypeError):
base_load_hourly = load_fact[
load_fact_bldg_key]["load shape"]
# Ensure that retrieved baseline load data are expected length
if len(base_load_hourly) != 8760:
raise ValueError(
"Baseline load data are of unexpected length " +
"(" + str(len(base_load_hourly)) + " elements) for " +
"end use " + mskeys[4] + ", EPlus building type " +
bldg + ", and EMM region " + mskeys[1] + ". Check "
"file ./supporting_data/tsv_data/tsv_load.gz to "
"ensure that 8760 data values are available for this "
"microsegment")

# Initialize efficient load shape as equal to base load
eff_load_hourly = copy.deepcopy(base_load_hourly)
Expand Down
Loading

0 comments on commit 0ec2ab4

Please sign in to comment.