From 653c898a3e6c2cf7b7d73b59b5738ecf30e12a57 Mon Sep 17 00:00:00 2001 From: Jared Langevin Date: Fri, 25 Oct 2024 09:46:30 -0400 Subject: [PATCH] Add reporting of efficient-envelope fractions Follows approach suggested in issue #413. --- scout/ecm_prep.py | 240 +++++++++++++++++++++++++++++++++++++++++----- scout/run.py | 185 ++++++++++++++++++++++++++--------- 2 files changed, 355 insertions(+), 70 deletions(-) diff --git a/scout/ecm_prep.py b/scout/ecm_prep.py index 37ce3638..37745d4d 100644 --- a/scout/ecm_prep.py +++ b/scout/ecm_prep.py @@ -11406,6 +11406,8 @@ def __init__(self, measure_list_package, p, bens, handyvars, handyfiles, if self.usr_opts["no_eff_capt"] is not True: self.markets[adopt_scheme]["master_mseg"]["energy"]["total"][ "efficient-captured"] = None + self.markets[adopt_scheme]["master_mseg"]["energy"]["total"][ + "efficient-captured-envelope"] = None # Add fugitive emissions key to output dict if fugitive # emissions option is set @@ -11460,8 +11462,12 @@ def __init__(self, measure_list_package, p, bens, handyvars, handyfiles, # variable if self.usr_opts["no_eff_capt"] is not True: self.markets[adopt_scheme][ - "mseg_out_break"]["energy"]["efficient-captured"] = \ - copy.deepcopy(self.handyvars.out_break_in) + "mseg_out_break"]["energy"]["efficient-captured"], \ + self.markets[adopt_scheme][ + "mseg_out_break"]["energy"][ + "efficient-captured-envelope"] = \ + (copy.deepcopy(self.handyvars.out_break_in) for n in + range(2)) def merge_measures(self, opts): """Merge the markets information of multiple individual measures. @@ -11695,6 +11701,16 @@ def merge_measures(self, opts): # required for the current adoption scenario if self.handyvars.full_dat_out[adopt_scheme]: mseg_out_break_fin = m[adopt_scheme]["breakouts"] + # If efficient-captured data for buildings with both + # HVAC and envelope impacts are being isolated, add + # a new branch to the energy breakout dict to use in + # reporting these data + if "efficient-captured-envelope" in \ + self.markets[adopt_scheme]["mseg_out_break"][ + "energy"].keys(): + mseg_out_break_fin["energy"][ + "efficient-captured-envelope"] = copy.deepcopy( + mseg_out_break_fin["energy"]["efficient-captured"]) # Set shorthand for data used to track annual electricity # use that concerns the measure's sector shape after # package adjustments @@ -11777,10 +11793,16 @@ def merge_measures(self, opts): # this reporting variable is not suppressed by user if v == "energy" and self.usr_opts[ "no_eff_capt"] is not True: - # Merge out breaks for captured efficient energy - self.merge_out_break(self.markets[adopt_scheme][ - "mseg_out_break"]["energy"]["efficient-captured"], - mseg_out_break_fin["energy"]["efficient-captured"]) + # Merge out breaks for captured efficient energy; + # for packages, includes variable that isolates + # portion of efficient-captured energy where + # HVAC/envelope are deployed together + for v_sub in ["efficient-captured", + "efficient-captured-envelope"]: + self.merge_out_break( + self.markets[adopt_scheme][ + "mseg_out_break"]["energy"][v_sub], + mseg_out_break_fin["energy"][v_sub]) # Adjust individual measure's contributing sector shape # information to account for overlaps with other measures in @@ -11971,6 +11993,31 @@ def htcl_adj_rec(self, opts): len(cm_keys_dmd[m]))]) for m in range(len(dmd_match_ECMs))]) for yr in self.handyvars.aeo_years} + if "efficient-captured" in m.markets[ + adopt_scheme]["master_mseg"][ + "energy"]["total"].keys(): + dmd_eff_capt = {yr: sum([sum([( + dmd_match_ECMs[m].markets[adopt_scheme][ + "mseg_adjust"][ + "contributing mseg keys and values"][ + cm_keys_dmd[m][k]]["energy"][ + "total"]["efficient-captured"][yr]) + for k in range(len(cm_keys_dmd[m]))]) for + m in range(len(dmd_match_ECMs))]) for yr in + self.handyvars.aeo_years} + dmd_eff = {yr: sum([sum([( + dmd_match_ECMs[m].markets[adopt_scheme][ + "mseg_adjust"][ + "contributing mseg keys and values"][ + cm_keys_dmd[m][k]]["energy"][ + "total"]["efficient"][yr]) + for k in range(len(cm_keys_dmd[m]))]) for + m in range(len(dmd_match_ECMs))]) for yr in + self.handyvars.aeo_years} + else: + dmd_eff_capt, dmd_eff = ( + None for n in range(2)) + # If the user opts to include envelope costs in # the total costs of the HVAC/envelope package, # record those overlapping costs @@ -12003,13 +12050,15 @@ def htcl_adj_rec(self, opts): adopt_scheme]["data"].keys(): # Data include the overlapping energy savings # and baselines recorded for the equipment/ - # envelope measures above, as well as the - # total energy use that could have overlapped, - # pulled from pre-calculated values + # envelope measures above, as well as stock + # and stock costs if available, pulled from + # pre-calculated values self.htcl_overlaps[adopt_scheme]["data"][ cm_key_store] = { "affected savings": dmd_save, "total affected": dmd_base, + "efficient-captured": dmd_eff_capt, + "total efficient": dmd_eff, "stock costs": dmd_stk_cost, "stock": dmd_stk} @@ -12074,9 +12123,10 @@ def merge_direct_overlaps( # operations in the loop msegs_meas_init = copy.deepcopy(msegs_meas) # Find base and efficient adjustment fractions - base_adj, eff_adj, eff_adj_c = self.find_base_eff_adj_fracs( - msegs_meas_init, cm_key, adopt_scheme, - name_meas, htcl_key_match, overlap_meas) + base_adj, eff_adj, eff_adj_c, eff_capt_env_frac = \ + self.find_base_eff_adj_fracs( + msegs_meas_init, cm_key, adopt_scheme, + name_meas, htcl_key_match, overlap_meas) # Adjust stock, energy, carbon, and energy/carbon cost data # based on savings contribution of the measure and overlapping # measure(s) in this contributing microsegment, as well as the @@ -12087,8 +12137,8 @@ def merge_direct_overlaps( tot_eff_capt_orig, tot_save_orig, tot_base_orig_ecost, \ tot_eff_orig_ecost, tot_save_orig_ecost = \ self.make_base_eff_adjs( - k, cm_key, msegs_meas, base_adj, - eff_adj, eff_adj_c) + k, cm_key, msegs_meas, base_adj, + eff_adj, eff_adj_c, eff_capt_env_frac) # Make adjustments to energy/carbon/cost output breakouts if # full data reporting is required for the current adoption # scenario @@ -12098,7 +12148,7 @@ def merge_direct_overlaps( tot_base_orig, tot_eff_orig, tot_eff_capt_orig, tot_save_orig, tot_base_orig_ecost, tot_eff_orig_ecost, tot_save_orig_ecost, key_list, fuel_switch_to, - fs_eff_splt) + fs_eff_splt, eff_capt_env_frac) # Special handling for cost merge when add-on measure is packaged if meas_typ == "add-on": # Determine whether any of the measures overlapping with the @@ -12261,7 +12311,8 @@ def merge_htcl_overlaps( msegs_meas_init = copy.deepcopy(msegs_meas) # Find base and efficient adjustment fractions; directly # overlapping measures are none in this case - base_adj, eff_adj, eff_adj_c = self.find_base_eff_adj_fracs( + base_adj, eff_adj, eff_adj_c, eff_capt_env_frac = \ + self.find_base_eff_adj_fracs( msegs_meas_init, cm_key, adopt_scheme, name_meas, htcl_key_match, overlap_meas="") # Adjust energy, carbon, and energy/carbon cost data based on @@ -12274,7 +12325,8 @@ def merge_htcl_overlaps( tot_eff_capt_orig, tot_save_orig, tot_base_orig_ecost, \ tot_eff_orig_ecost, tot_save_orig_ecost = \ self.make_base_eff_adjs( - k, cm_key, msegs_meas, base_adj, eff_adj, eff_adj_c) + k, cm_key, msegs_meas, base_adj, + eff_adj, eff_adj_c, eff_capt_env_frac) # Make adjustments to energy/carbon/cost output breakouts if # full data reporting is required for the current adoption # scenario @@ -12285,7 +12337,7 @@ def merge_htcl_overlaps( tot_base_orig, tot_eff_orig, tot_eff_capt_orig, tot_save_orig, tot_base_orig_ecost, tot_eff_orig_ecost, tot_save_orig_ecost, key_list, fuel_switch_to, - fs_eff_splt) + fs_eff_splt, eff_capt_env_frac) # If necessary, adjust fugitive emissions data @@ -12566,6 +12618,11 @@ def find_base_eff_adj_fracs(self, msegs_meas, cm_key, adopt_scheme, # for the overlapping tech type in the current contributing mseg overlp_data = self.htcl_overlaps[adopt_scheme]["data"][ htcl_key_match] + # If efficient-captured data are reported, initialize dict that + # isolates the fraction of efficient-captured energy where both + # envelope and HVAC measures are having an impact + if overlp_data["efficient-captured"]: + eff_capt_env_frac = {yr: 0 for yr in self.handyvars.aeo_years} # Loop through all years in the modeling time horizon and use # data above to develop baseline/efficient adjustment factors @@ -12595,6 +12652,53 @@ def find_base_eff_adj_fracs(self, msegs_meas, cm_key, adopt_scheme, rp_overlp_htcl[yr] < 0: rp_overlp_htcl[yr] = 0 + # If needed to isolate efficient-captured totals for cases + # where both envelope and HVAC measures are having impacts, + # pull the ratio of the efficient-captured energy use for + # all overlapping envelope measures vs. the total efficient + # energy use for these measures (this is later applied + # to the efficient-captured totals for the pkg.) + if overlp_data["efficient-captured"]: + # Find efficient-captured-envelope fraction for the + # overlapping measure(s); handle zero denominator + try: + eff_capt_env_frac[yr] = ( + overlp_data["efficient-captured"][yr] / + overlp_data["total efficient"][yr]) + # Ensure that total efficient-captured energy is + # never greater than total efficient energy for + # overlapping envelope measures in a given region, + # building type/vintage, and end use combination + if eff_capt_env_frac[yr] > 1: + eff_capt_env_frac[yr] = 1 + # In certain cases with aggressive envelope + # improvements where the measure enters the market + # later in the horizon, total efficient energy + # (representing energy use of all the baseline + # stock captured before the measure entered the + # market) will be positive while total efficient- + # captured energy (representing energy use of all + # the stock the measure captures after market entry + # ) will be negative – or vice versa, depending on + # the direction of the env. component impact. For + # this case, compare the absolute value of the + # efficient-captured to the absolute value of the + # total sum of efficient-captured and total + # efficient values, to avoid a negative fraction. + elif eff_capt_env_frac[yr] < 0: + eff_capt_env_frac[yr] = (abs(overlp_data[ + "efficient-captured"][yr]) / ( + abs(overlp_data[ + "efficient-captured"][yr]) + + abs(overlp_data["total efficient"][yr]))) + except ZeroDivisionError: + eff_capt_env_frac[yr] = 0 + # Handle numpy NaNs + if not numpy.isfinite(eff_capt_env_frac[yr]): + eff_capt_env_frac[yr] = 0 + else: + eff_capt_env_frac = None + # Overlapping envelope measures only affect the efficient case # energy/carbon results for an HVAC equipment measure; set base # adjustment to 1 @@ -12820,15 +12924,18 @@ def find_base_eff_adj_fracs(self, msegs_meas, cm_key, adopt_scheme, # Implement competed efficient adjustment eff_adj_comp = {yr: base_adj[yr] * rp_overlp_comp[yr] for yr in self.handyvars.aeo_years} + eff_capt_env_frac = None # If neither case 1 or 2 above, set baseline/efficient adjustments to 1 else: base_adj, eff_adj, eff_adj_comp = ( {yr: 1 for yr in self.handyvars.aeo_years} for n in range(2)) + eff_capt_env_frac = None - return base_adj, eff_adj, eff_adj_comp + return base_adj, eff_adj, eff_adj_comp, eff_capt_env_frac def make_base_eff_adjs( - self, k, cm_key, msegs_meas, base_adj, eff_adj, eff_adj_c): + self, k, cm_key, msegs_meas, base_adj, eff_adj, eff_adj_c, + eff_capt_env_frac): """Apply overlap adjustments for measure mseg in a package. Args: @@ -12840,6 +12947,8 @@ def make_base_eff_adjs( base_adj (dict): Overlap adjustments for baseline data. eff_adj (dict): Overlap adjustments for total efficient data. eff_adj_c (dict): Overlap adjustments for competed efficient data. + eff_capt_env_frac (dict): Efficient-captured portion of efficient + energy total across all envelope measures in HVAC/envelope pkg. Returns: Adjusted baseline/efficient energy and carbon data that accounts @@ -12909,6 +13018,16 @@ def make_base_eff_adjs( self.adj_pkg_mseg_keyvals( mseg_cost_adj, base_adj, eff_adj, eff_adj_c, base_eff_flag=None, comp_flag=None) + # If necessary, for energy, add further tracking to isolate + # efficient-captured data where both envelope and HVAC measures are + # having impacts within an HVAC/envelope pkg.; calculated as + # efficient-captured total for the HVAC/envelope pkg. multiplied by + # the efficient-captured portion of the efficient total across all + # envelope measures that contribute to the package + if k == "energy" and eff_capt_env_frac: + mseg_adj["total"]["efficient-captured-envelope"] = { + yr: mseg_adj["total"]["efficient-captured"][yr] * + eff_capt_env_frac[yr] for yr in self.handyvars.aeo_years} return mseg_adj, mseg_cost_adj, tot_base_orig, tot_eff_orig, \ tot_eff_capt_orig, tot_save_orig, tot_base_orig_ecost, \ @@ -12918,7 +13037,7 @@ def find_adj_out_break_cats( self, k, cm_key, msegs_ecarb, msegs_ecarb_cost, mseg_out_break_adj, tot_base_orig, tot_eff_orig, tot_eff_capt_orig, tot_save_orig, tot_base_orig_ecost, tot_eff_orig_ecost, tot_save_orig_ecost, - key_list, fuel_switch_to, fs_eff_splt): + key_list, fuel_switch_to, fs_eff_splt, eff_capt_env_frac): """Adjust output breakouts after removing energy/carbon data overlaps. Args: @@ -12941,6 +13060,8 @@ def find_adj_out_break_cats( measure switches to (if applicable). fs_eff_splt (dict): If applicable, the fuel splits for efficient- case measure energy/carb/cost (used to adj. output breakouts). + eff_capt_env_frac (dict): Efficient-captured portion of efficient + energy total across all envelope measures in HVAC/envelope pkg. Returns: Updated energy, carbon, and energy cost output breakouts adjusted @@ -13108,11 +13229,29 @@ def find_adj_out_break_cats( # efficient case remains w/ original fuel by definition if k != "stock": fs_eff_splt_var = { - yr: (fs_eff_splt[k][0][yr] / fs_eff_splt[k][1][yr]) if - fs_eff_splt[k][1][yr] != 0 else 1 + yr: ((fs_eff_splt[k][0][yr] + fs_eff_splt[k][1][yr]) / + fs_eff_splt[k][2][yr]) if + fs_eff_splt[k][2][yr] != 0 else 1 for yr in self.handyvars.aeo_years} + # Check for whether baseline fuel use is present in the + # efficient-captured stock (e.g., for dual fuel measure + # operations) – this is signified by a boolean flag in the + # last element of the fs_eff_splt["energy"] variable. + # If so, pull fraction of efficient-captured energy that + # remains serviced by original fuel; if not, set to None + if eff_capt and fs_eff_splt[k][-1]: + fs_eff_splt_var_capt = { + yr: fs_eff_splt[k][1][yr] / fs_eff_splt[k][3][yr] if + fs_eff_splt[k][3][yr] != 0 else 1 + for yr in self.handyvars.aeo_years} + elif eff_capt: + fs_eff_splt_var_capt = { + yr: 0 for yr in self.handyvars.aeo_years} + else: + fs_eff_splt_var_capt = None else: fs_eff_splt_var = {yr: 0 for yr in self.handyvars.aeo_years} + fs_eff_splt_var_capt = None # Stock/energy/carbon; original fuel mseg_out_break_adj[k]["baseline"][ out_cz][out_bldg][out_eu][out_fuel_save], \ @@ -13129,9 +13268,31 @@ def find_adj_out_break_cats( eff_orig[yr] - eff_adj[yr]) * fs_eff_splt_var[yr] for yr in self.handyvars.aeo_years}] - # Note: no measure-captured efficient energy in the base fuel - # needs adjustment here given that by definition fuel switching - # measures only operate via switched to fuel (no data to adjust) + # If measure-captured efficient energy is partially serviced by + # baseline fuel, update results accordingly; otherwise, no + # adjustment is needed + if eff_capt and fs_eff_splt[k][-1]: + # Remove adjusted efficient-captured case that remains with + # base fuel + mseg_out_break_adj[k]["efficient-captured"][ + out_cz][out_bldg][out_eu][out_fuel_save] = { + yr: mseg_out_break_adj[k]["efficient-captured"][ + out_cz][out_bldg][out_eu][out_fuel_save][yr] - ( + eff_capt_orig[yr] - eff_capt_adj[yr]) * + fs_eff_splt_var_capt[yr] for + yr in self.handyvars.aeo_years} + # Update efficient captured for envelope portion of pkg. if + # this is being tracked; calculated as the efficient-captured + # total for the HVAC/envelope pkg. multiplied by the efficient- + # captured portion of the efficient total across all envelope + # measures that contribute to the package + if eff_capt_env_frac: + mseg_out_break_adj[k]["efficient-captured-envelope"][ + out_cz][out_bldg][out_eu][out_fuel_save] = { + yr: mseg_out_break_adj[k]["efficient-captured"][ + out_cz][out_bldg][out_eu][out_fuel_save][yr] * + eff_capt_env_frac[yr] for yr in + self.handyvars.aeo_years} # No savings breakouts for stock variable if k != "stock": @@ -13168,6 +13329,15 @@ def find_adj_out_break_cats( out_cz][out_bldg][out_eu][out_fuel_gain][yr] - ( eff_capt_orig[yr] - eff_capt_adj[yr]) for yr in self.handyvars.aeo_years} + # Update efficient captured for envelope portion of pkg. if + # this is being tracked + if eff_capt_env_frac: + mseg_out_break_adj[k]["efficient-captured-envelope"][ + out_cz][out_bldg][out_eu][out_fuel_gain] = { + yr: mseg_out_break_adj[k]["efficient-captured"][ + out_cz][out_bldg][out_eu][out_fuel_gain][yr] * + eff_capt_env_frac[yr] for yr in + self.handyvars.aeo_years} # No savings breakouts for stock variable if k != "stock": # Adjusted efficient is added to the existing savings for @@ -13273,6 +13443,15 @@ def find_adj_out_break_cats( out_cz][out_bldg][out_eu][out_fuel_save][yr] - ( eff_capt_orig[yr] - eff_capt_adj[yr]) for yr in self.handyvars.aeo_years} + # Update efficient captured for envelope portion of pkg. if + # this is being tracked + if eff_capt_env_frac: + mseg_out_break_adj[k]["efficient-captured-envelope"][ + out_cz][out_bldg][out_eu][out_fuel_save] = { + yr: mseg_out_break_adj[k]["efficient-captured"][ + out_cz][out_bldg][out_eu][out_fuel_save][yr] * + eff_capt_env_frac[yr] for yr in + self.handyvars.aeo_years} # No savings breakouts for stock variable if k != "stock": # Adjusted savings is difference between adjusted @@ -13343,6 +13522,15 @@ def find_adj_out_break_cats( out_cz][out_bldg][out_eu][yr] - ( eff_capt_orig[yr] - eff_capt_adj[yr]) for yr in self.handyvars.aeo_years} + # Update efficient captured for envelope portion of pkg. if + # this is being tracked + if eff_capt_env_frac: + mseg_out_break_adj[k]["efficient-captured-envelope"][ + out_cz][out_bldg][out_eu] = { + yr: mseg_out_break_adj[k]["efficient-captured"][ + out_cz][out_bldg][out_eu][yr] * + eff_capt_env_frac[yr] for yr in + self.handyvars.aeo_years} # No savings breakouts for stock variable if k != "stock": # Adjusted savings is difference between adjusted diff --git a/scout/run.py b/scout/run.py index b9225dc3..3925a740 100644 --- a/scout/run.py +++ b/scout/run.py @@ -2039,6 +2039,9 @@ def secondary_adj( mast, adj_out_break, adj, mast_list_base, mast_list_eff, \ adj_list_eff, adj_list_base, adj_stk_trk = \ self.compete_adj_dicts(m, mseg_key, adopt_scheme) + # Determine whether efficient-captured energy is being reported + eff_capt = ( + "efficient-captured" in adj["energy"]["total"].keys()) # Adjust secondary energy/carbon/cost totals based on the measure's # competed market share for an associated primary contributing @@ -2116,9 +2119,7 @@ def secondary_adj( else "" for v in ["baseline", "efficient"]] # Energy data may include unique efficient captured # tracking if efficient breakout data are present - if "efficient" in vs_list and var == "energy" and \ - "efficient-captured" in adj_out_break[ - "base fuel"]["energy"].keys(): + if "efficient" in vs_list and var == "energy" and eff_capt: vs_list.append("efficient-captured") for var_sub in [x for x in vs_list if x]: # Select correct fuel split data @@ -2212,9 +2213,7 @@ def secondary_adj( # Update efficient result # Energy data may include efficient-captured # tracking - if var == "energy" and "efficient-captured" in \ - adj_out_break["switched fuel"][ - "energy"].keys(): + if var == "energy" and eff_capt: vs_list = ["efficient", "efficient-captured"] else: vs_list = ["efficient"] @@ -2451,6 +2450,9 @@ def htcl_adj(self, measures_htcl_adj, adopt_scheme, htcl_adj_data): mast, adj_out_break, adj, mast_list_base, mast_list_eff, \ adj_list_eff, adj_list_base, adj_stk_trk = \ self.compete_adj_dicts(m, mseg, adopt_scheme) + # Determine whether efficient-captured energy is being reported + eff_capt = ( + "efficient-captured" in adj["energy"]["total"].keys()) # Adjust contributing and master energy/carbon/cost # data to remove recorded supply-demand overlaps for yr in self.handyvars.aeo_years: @@ -2599,8 +2601,7 @@ def htcl_adj(self, measures_htcl_adj, adopt_scheme, htcl_adj_data): # Energy data may include unique efficient captured # tracking if efficient breakout data are present if "efficient" in vs_list and var == "energy" and \ - "efficient-captured" in adj_out_break[ - "base fuel"]["energy"].keys(): + eff_capt: vs_list.append("efficient-captured") for var_sub in [x for x in vs_list if x]: # Set appropriate post-competition adjustment frac. @@ -2705,10 +2706,7 @@ def htcl_adj(self, measures_htcl_adj, adopt_scheme, htcl_adj_data): # Update efficient result # Energy data may include efficient-captured # tracking - if var == "energy" and \ - "efficient-captured" in \ - adj_out_break["switched fuel"][ - "energy"].keys(): + if var == "energy" and eff_capt: vs_list = ["efficient", "efficient-captured"] else: @@ -3001,8 +2999,9 @@ def compete_adj_dicts(self, m, mseg_key, adopt_scheme): elif var != "energy": var_list = ["baseline", "efficient", "savings"] else: # efficient captured data for energy - var_list = ["baseline", "efficient", - "efficient-captured", "savings"] + var_list = ["baseline", "efficient", "savings"] + if eff_capt: + var_list.extend(["efficient-captured"]) # Adjust stock/energy/carbon/cost data for var_sub in var_list: # Handle case where potential efficient-captured energy @@ -4146,7 +4145,7 @@ def finalize_outputs( # Initialize markets and savings totals across all ECMs # Set total # of market variables that could be reported across ECMs - n_vars_all = 15 + n_vars_all = 16 # Initialize summary variable values at zero summary_vals_all_ecms = [{ yr: 0 for yr in focus_yrs} for n in range(n_vars_all)] @@ -4174,8 +4173,32 @@ def finalize_outputs( # Set competed measure markets and savings and financial metrics mkts = m.markets[adopt_scheme]["competed"]["master_mseg"] # Set shorthand for efficient-captured market data if these are - # available, and if they are not, set shorthand to None - eff_capt = mkts["energy"]["total"].get("efficient-captured", "") + # available, and if they are not set shorthand to None + try: + eff_capt = mkts["energy"]["total"]["efficient-captured"] + # Recalculate efficient-captured-envelope data based on + # adjusted efficient captured data, if applicable + try: + # Check if efficient-captured-envelope data are in keys + eff_capt_env = mkts["energy"]["total"][ + "efficient-captured-envelope"] + # Reset these data based on the original (pre-competition) + # ratio between efficient-captured-envelope and + # efficient-captured, applied to adjusted (post-comp.) + # efficient-captured data + pre_comp_mkts = m.markets[adopt_scheme][ + "uncompeted"]["master_mseg"]["energy"]["total"] + eff_capt_env = { + yr: (eff_capt[yr] * ( + pre_comp_mkts["efficient-captured-envelope"][yr] / + pre_comp_mkts["efficient-captured"][yr])) if + pre_comp_mkts["efficient-captured"][yr] != 0 + else 0 for yr in self.handyvars.aeo_years} + except KeyError: + eff_capt_env = "" + except KeyError: + eff_capt, eff_capt_env = ("" for n in range(2)) + save = m.savings[adopt_scheme]["competed"] metrics_finance = m.financial_metrics @@ -4189,7 +4212,7 @@ def finalize_outputs( mkts["cost"]["carbon"]["total"]["baseline"], mkts["stock"]["total"]["measure"], mkts["energy"]["total"]["efficient"], - eff_capt, + eff_capt, eff_capt_env, mkts["carbon"]["total"]["efficient"], mkts["cost"]["energy"]["total"]["efficient"], mkts["cost"]["carbon"]["total"]["efficient"], @@ -4208,10 +4231,13 @@ def finalize_outputs( # Order the year entries in the above markets, savings, # and portfolio metrics outputs summary_vals_init = [OrderedDict( - sorted(x.items())) for x in summary_vals] + sorted(x.items())) if isinstance(x, dict) else x + for x in summary_vals] # Apply focus year range, if applicable summary_vals = [{ - yr: summary_vals_init[v][yr] for yr in focus_yrs} + yr: summary_vals_init[v][yr] if + isinstance(summary_vals_init[v], dict) else 0 + for yr in focus_yrs} for v in range(len(summary_vals))] # Add ECM markets and savings totals to totals across all ECMs summary_vals_all_ecms = [{ @@ -4274,33 +4300,34 @@ def finalize_outputs( # Mean of outputs stk_base_avg, energy_base_avg, carb_base_avg, \ - energy_cost_base_avg, carb_cost_base_avg, stk_eff_avg, \ - energy_eff_avg, energy_eff_capt_avg, carb_eff_avg, \ - energy_cost_eff_avg, carb_cost_eff_avg, energy_save_avg, \ - energy_costsave_avg, carb_save_avg, carb_costsave_avg, \ - cce_avg, cce_c_avg, ccc_avg, ccc_e_avg, \ - irr_e_avg, irr_ec_avg, payback_e_avg, \ - payback_ec_avg = [{ + energy_cost_base_avg, carb_cost_base_avg, stk_eff_avg, \ + energy_eff_avg, energy_eff_capt_avg, energy_eff_capt_avg_env, \ + carb_eff_avg, energy_cost_eff_avg, carb_cost_eff_avg, \ + energy_save_avg, energy_costsave_avg, carb_save_avg, \ + carb_costsave_avg, cce_avg, cce_c_avg, ccc_avg, ccc_e_avg, \ + irr_e_avg, irr_ec_avg, payback_e_avg, payback_ec_avg = [{ k: numpy.mean(v) if v is not None else None for k, v in z.items()} for z in summary_vals] # 5th percentile of outputs stk_base_low, energy_base_low, carb_base_low, \ energy_cost_base_low, carb_cost_base_low, stk_eff_low, \ - energy_eff_low, energy_eff_capt_low, carb_eff_low, \ - energy_cost_eff_low, carb_cost_eff_low, energy_save_low, \ - energy_costsave_low, carb_save_low, carb_costsave_low, \ - cce_low, cce_c_low, ccc_low, ccc_e_low, \ + energy_eff_low, energy_eff_capt_low, energy_eff_capt_low_env, \ + carb_eff_low, energy_cost_eff_low, carb_cost_eff_low, \ + energy_save_low, energy_costsave_low, carb_save_low, \ + carb_costsave_low, cce_low, cce_c_low, ccc_low, ccc_e_low, \ irr_e_low, irr_ec_low, payback_e_low, payback_ec_low = [{ k: numpy.percentile(v, 5) if v is not None else None for k, v in z.items()} for z in summary_vals] # 95th percentile of outputs stk_base_high, energy_base_high, carb_base_high, \ energy_cost_base_high, carb_cost_base_high, stk_eff_high, \ - energy_eff_high, energy_eff_capt_high, carb_eff_high, \ + energy_eff_high, energy_eff_capt_high, \ + energy_eff_capt_high_env, carb_eff_high, \ energy_cost_eff_high, carb_cost_eff_high, energy_save_high, \ - energy_costsave_high, carb_save_high, carb_costsave_high, \ - cce_high, cce_c_high, ccc_high, ccc_e_high, \ - irr_e_high, irr_ec_high, payback_e_high, payback_ec_high = [{ + energy_costsave_high, carb_save_high, \ + carb_costsave_high, cce_high, cce_c_high, \ + ccc_high, ccc_e_high, irr_e_high, irr_ec_high, \ + payback_e_high, payback_ec_high = [{ k: numpy.percentile(v, 95) if v is not None else None for k, v in z.items()} for z in summary_vals] @@ -4337,7 +4364,7 @@ def finalize_outputs( n in range(2)) # Add efficient-captured data to reporting if present - if eff_capt and energy_eff_capt_avg is not None: + if eff_capt: self.output_ecms[m.name][ "Markets and Savings (Overall)"][adopt_scheme][ "Efficient Energy Use, Measure (MMBtu)"], \ @@ -4346,6 +4373,19 @@ def finalize_outputs( adopt_scheme][ "Efficient Energy Use, Measure (MMBtu)"] = ( energy_eff_capt_avg for n in range(2)) + # Add efficient-captured-envelope data to reporting if + # present + if eff_capt_env: + self.output_ecms[m.name][ + "Markets and Savings (Overall)"][adopt_scheme][ + "Efficient Energy Use, Measure-" + "Envelope (MMBtu)"], \ + self.output_ecms[m.name][ + "Markets and Savings (by Category)"][ + adopt_scheme][ + "Efficient Energy Use, Measure-" + "Envelope (MMBtu)"] = ( + energy_eff_capt_avg_env for n in range(2)) # Determine stock units, if necessary (for the Scout stock # reporting option and/or for mapping to GCAM data format) if opts.report_stk is True or opts.gcam_out is True: @@ -4451,6 +4491,11 @@ def finalize_outputs( if eff_capt: mkt_eff_keys.append( "Efficient Energy Use, Measure (MMBtu)") + # Add efficient-captured-envelope to efficient breakout + # names if present + if eff_capt_env: + mkt_eff_keys.append( + "Efficient Energy Use, Measure-Envelope (MMBtu)") # Add baseline/efficient keys for stock reporting, if needed if opts.report_stk is True: mkt_base_keys.append(base_stk_key) @@ -4477,7 +4522,7 @@ def finalize_outputs( ("Avoided CO2 Emissions (MMTons)". translate(sub), carb_save_avg) ]) for n in range(2)) - if eff_capt and energy_eff_capt_avg is not None: + if eff_capt: self.output_ecms[m.name][ "Markets and Savings (Overall)"][adopt_scheme][ "Efficient Energy Use, Measure (MMBtu)"], \ @@ -4486,6 +4531,18 @@ def finalize_outputs( adopt_scheme][ "Efficient Energy Use, Measure (MMBtu)"] = ( energy_eff_capt_avg for n in range(2)) + if eff_capt_env: + self.output_ecms[m.name][ + "Markets and Savings (Overall)"][adopt_scheme][ + "Efficient Energy Use, Measure-" + "Envelope (MMBtu)"], \ + self.output_ecms[m.name][ + "Markets and Savings (by Category)"][ + adopt_scheme][ + "Efficient Energy Use, Measure-" + "Envelope (MMBtu)"] = ( + energy_eff_capt_avg_env for n in range(2)) + # Record list of baseline variable names for use in finalizing # output breakouts below mkt_base_keys = [ @@ -4500,6 +4557,11 @@ def finalize_outputs( if eff_capt: mkt_eff_keys.append( "Efficient Energy Use, Measure (MMBtu)") + # Add efficient-captured-envelope to efficient breakout + # names if present + if eff_capt_env: + mkt_eff_keys.append( + "Efficient Energy Use, Measure-Envelope (MMBtu)") # Record list of savings variable names for use in finalizing # output breakouts below save_keys = [ @@ -4656,7 +4718,7 @@ def finalize_outputs( m.markets[adopt_scheme]["competed"]["mseg_out_break"][ "energy"]["efficient"], energy_eff_avg, focus_yrs, divide=True) - # Calculate efficient captured energy fractions by output breakout + # Calculate efficient-captured energy fractions by output breakout # category if efficient-captured energy data are present if eff_capt: frac_eff_energy_capt = self.out_break_walk( @@ -4846,6 +4908,16 @@ def finalize_outputs( mkt_sv[ "Efficient Energy Use, Measure (high) (MMBtu)"] = \ energy_eff_capt_high + # Record efficient-captured-envelope data if present + if eff_capt_env: + mkt_sv[ + "Efficient Energy Use, Measure-Envelope" + " (low) (MMBtu)"] = \ + energy_eff_capt_low_env + mkt_sv[ + "Efficient Energy Use, Measure-Envelope" + " (high) (MMBtu)"] = \ + energy_eff_capt_high_env # Record updated financial metrics in Engine 'output' attribute; # yield low and high estimates on the metrics if available @@ -5427,7 +5499,8 @@ def finalize_outputs( stock_base_all_avg, energy_base_all_avg, carb_base_all_avg, \ energy_cost_base_all_avg, carb_cost_base_all_avg, \ stock_eff_all_avg, energy_eff_all_avg, energy_eff_all_capt_avg, \ - carb_eff_all_avg, energy_cost_eff_all_avg, carb_cost_eff_all_avg, \ + energy_eff_all_capt_avg_env, carb_eff_all_avg, \ + energy_cost_eff_all_avg, carb_cost_eff_all_avg, \ energy_save_all_avg, energy_costsave_all_avg, carb_save_all_avg, \ carb_costsave_all_avg = [{ k: numpy.mean(v) if v is not None else v @@ -5436,7 +5509,8 @@ def finalize_outputs( stock_base_all_low, energy_base_all_low, carb_base_all_low, \ energy_cost_base_all_low, carb_cost_base_all_low, \ stock_eff_all_low, energy_eff_all_low, energy_eff_all_capt_low, \ - carb_eff_all_low, energy_cost_eff_all_low, carb_cost_eff_all_low, \ + energy_eff_all_capt_low_env, carb_eff_all_low, \ + energy_cost_eff_all_low, carb_cost_eff_all_low, \ energy_save_all_low, energy_costsave_all_low, carb_save_all_low, \ carb_costsave_all_low = [{ k: numpy.percentile(v, 5) if v is not None else v @@ -5445,10 +5519,11 @@ def finalize_outputs( stock_base_all_high, energy_base_all_high, carb_base_all_high, \ energy_cost_base_all_high, carb_cost_base_all_high, \ stock_eff_all_high, energy_eff_all_high, \ - energy_eff_all_capt_high, carb_eff_all_high, \ - energy_cost_eff_all_high, carb_cost_eff_all_high, \ - energy_save_all_high, energy_costsave_all_high, \ - carb_save_all_high, carb_costsave_all_high = [{ + energy_eff_all_capt_high, energy_eff_all_capt_high_env, \ + carb_eff_all_high, energy_cost_eff_all_high, \ + carb_cost_eff_all_high, energy_save_all_high, \ + energy_costsave_all_high, carb_save_all_high, \ + carb_costsave_all_high = [{ k: numpy.percentile(v, 95) if v is not None else v for k, v in z.items()} for z in summary_vals_all_ecms] @@ -5478,6 +5553,13 @@ def finalize_outputs( self.output_all["All ECMs"]["Markets and Savings (Overall)"][ adopt_scheme]["Efficient Energy Use, Measure (MMBtu)"] = \ energy_eff_all_capt_avg + # Record efficient-captured-envelope data across all ECMs if + # present + if eff_capt_env and energy_eff_all_capt_avg_env is not None: + self.output_all["All ECMs"]["Markets and Savings (Overall)"][ + adopt_scheme][ + "Efficient Energy Use, Measure-Envelope (MMBtu)"] = \ + energy_eff_all_capt_avg_env # Record updated (post-competed) fugitive emissions results across all # ECMs if applicable @@ -5552,6 +5634,21 @@ def finalize_outputs( "Markets and Savings (Overall)"][adopt_scheme][ "Efficient Energy Use, Measure (high) (MMBtu)"] = \ [energy_eff_all_capt_low, energy_eff_all_capt_high] + # Record low/high efficient-captured-envelope data across all + # ECMs if present + if eff_capt_env and all([x is not None for x in [ + energy_eff_all_capt_low_env, + energy_eff_all_capt_high_env]]): + self.output_all["All ECMs"][ + "Markets and Savings (Overall)"][adopt_scheme][ + "Efficient Energy Use, Measure-Envelope" + " (low) (MMBtu)"], \ + self.output_all["All ECMs"][ + "Markets and Savings (Overall)"][adopt_scheme][ + "Efficient Energy Use, Measure-Envelope" + "(high) (MMBtu)"] = [ + energy_eff_all_capt_low_env, + energy_eff_all_capt_high_env] def out_break_walk(self, adjust_dict, adjust_vals, focus_yrs, divide, mkt_frac=False):