diff --git a/config/config.default.yaml b/config/config.default.yaml index 9e8f57a66..bf4fafdb3 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -446,6 +446,7 @@ sector: decentral: 3 central: 180 boilers: true + resistive_heaters: true oil_boilers: false biomass_boiler: true chp: true diff --git a/doc/configtables/sector.csv b/doc/configtables/sector.csv index 5e2514e42..7c2406bf7 100644 --- a/doc/configtables/sector.csv +++ b/doc/configtables/sector.csv @@ -62,7 +62,8 @@ tes,--,"{true, false}",Add option for storing thermal energy in large water pits tes_tau,,,The time constant used to calculate the decay of thermal energy in thermal energy storage (TES): 1- :math:`e^{-1/24τ}`. -- decentral,days,float,The time constant in decentralized thermal energy storage (TES) -- central,days,float,The time constant in centralized thermal energy storage (TES) -boilers,--,"{true, false}",Add option for transforming electricity into heat using resistive heater +boilers,--,"{true, false}",Add option for transforming gas into heat using gas boilers +resistive_heaters,--,"{true, false}",Add option for transforming electricity into heat using resistive heaters (independently from gas boilers) oil_boilers,--,"{true, false}",Add option for transforming oil into heat using boilers biomass_boiler,--,"{true, false}",Add option for transforming biomass into heat using boilers chp,--,"{true, false}",Add option for using Combined Heat and Power (CHP) diff --git a/rules/common.smk b/rules/common.smk index 0e85b620e..2298ff912 100644 --- a/rules/common.smk +++ b/rules/common.smk @@ -4,7 +4,7 @@ import os, sys, glob -helper_source_path = [match for match in glob.glob('**/_helpers.py', recursive=True)] +helper_source_path = [match for match in glob.glob("**/_helpers.py", recursive=True)] for path in helper_source_path: path = os.path.dirname(os.path.abspath(path)) diff --git a/scripts/build_retro_cost.py b/scripts/build_retro_cost.py old mode 100644 new mode 100755 index 3ca2b1741..d2aae1404 --- a/scripts/build_retro_cost.py +++ b/scripts/build_retro_cost.py @@ -533,16 +533,16 @@ def prepare_temperature_data(): """ temperature = xr.open_dataarray(snakemake.input.air_temperature).to_pandas() d_heat = ( - temperature.groupby(temperature.columns.str[:2], axis=1) + temperature.T.groupby(temperature.columns.str[:2]) .mean() - .resample("1D") + .T.resample("1D") .mean() < t_threshold ).sum() temperature_average_d_heat = ( - temperature.groupby(temperature.columns.str[:2], axis=1) + temperature.T.groupby(temperature.columns.str[:2]) .mean() - .apply( + .T.apply( lambda x: get_average_temperature_during_heating_season(x, t_threshold=15) ) ) @@ -610,7 +610,7 @@ def calculate_costs(u_values, l, cost_retro, window_assumptions): cost_retro.loc[x.name[3], "cost_var"] * 100 * float(l) - * l_weight.loc[x.name[3]][0] + * l_weight.loc[x.name[3]].iloc[0] + cost_retro.loc[x.name[3], "cost_fix"] ) * x.A_element @@ -720,6 +720,7 @@ def map_to_lstrength(l_strength, df): .swaplevel(axis=1) .dropna(axis=1) ) + return pd.concat([df.drop([2, 3], axis=1, level=1), l_strength_df], axis=1) @@ -800,6 +801,7 @@ def calculate_heat_losses(u_values, data_tabula, l_strength, temperature_factor) * data_tabula.A_envelope / data_tabula.A_C_Ref ) + heat_transfer_perm2 = pd.concat( [ heat_transfer_perm2, diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py old mode 100644 new mode 100755 index ea0c4f3fb..0fb9729a3 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -455,7 +455,7 @@ def update_wind_solar_costs(n, costs): logger.info( "Added connection cost of {:0.0f}-{:0.0f} Eur/MW/a to {}".format( - connection_cost[0].min(), connection_cost[0].max(), tech + connection_cost.min(), connection_cost.max(), tech ) ) @@ -497,6 +497,9 @@ def add_carrier_buses(n, carrier, nodes=None): capital_cost=capital_cost, ) + if carrier == "gas": + return + n.madd( "Generator", nodes, @@ -1870,7 +1873,7 @@ def add_heat(n, costs): lifetime=costs.at[name_type + " water tank storage", "lifetime"], ) - if options["boilers"]: + if options["resistive_heaters"]: key = f"{name_type} resistive heater" n.madd( @@ -1885,6 +1888,7 @@ def add_heat(n, costs): lifetime=costs.at[key, "lifetime"], ) + if options["boilers"]: key = f"{name_type} gas boiler" n.madd( @@ -2022,7 +2026,7 @@ def add_heat(n, costs): ) w_space["tot"] = ( heat_demand_r["services space"] + heat_demand_r["residential space"] - ) / heat_demand_r.groupby(level=[1], axis=1).sum() + ) / heat_demand_r.T.groupby(level=[1]).sum().T for name in n.loads[ n.loads.carrier.isin([x + " heat" for x in heat_systems]) @@ -2031,11 +2035,21 @@ def add_heat(n, costs): ct = pop_layout.loc[node, "ct"] # weighting 'f' depending on the size of the population at the node - f = urban_fraction[node] if "urban" in name else (1 - urban_fraction[node]) + if "urban central" in name: + f = dist_fraction[node] + elif "urban decentral" in name: + f = urban_fraction[node] - dist_fraction[node] + else: + f = 1 - urban_fraction[node] if f == 0: continue # get sector name ("residential"/"services"/or both "tot" for urban central) - sec = [x if x in name else "tot" for x in sectors][0] + if "urban central" in name: + sec = "tot" + if "residential" in name: + sec = "residential" + if "services" in name: + sec = "services" # get floor aread at node and region (urban/rural) in m^2 floor_area_node = ( @@ -2079,14 +2093,15 @@ def add_heat(n, costs): strengths = strengths.drop(s) # reindex normed time profile of space heat demand back to hourly resolution - space_pu = space_pu.reindex(index=heat_demand.index).fillna(method="ffill") + space_pu = space_pu.reindex(index=heat_demand.index).ffill() # add for each retrofitting strength a generator with heat generation profile following the profile of the heat demand for strength in strengths: + node_name = " ".join(name.split(" ")[2::]) n.madd( "Generator", [node], - suffix=" retrofitting " + strength + " " + name[6::], + suffix=" retrofitting " + strength + " " + node_name, bus=name, carrier="retrofitting", p_nom_extendable=True, @@ -2998,8 +3013,9 @@ def add_industry(n, costs): if options["co2_spatial"] or options["co2network"]: p_set = ( - -industrial_demand.loc[nodes, "process emission"] - .rename(index=lambda x: x + " process emissions") + -industrial_demand.loc[nodes, "process emission"].rename( + index=lambda x: x + " process emissions" + ) / nhours ) else: @@ -3417,7 +3433,7 @@ def define_clustering(attributes, aggregate_dict): ), inplace=True, ) - pnl[k] = pnl[k].groupby(level=0, axis=1).agg(agg[k], **agg_group_kwargs) + pnl[k] = pnl[k].T.groupby(level=0).agg(agg[k], **agg_group_kwargs).T # remove unclustered assets of service/residential to_drop = c.df.index.difference(df.index) @@ -3674,7 +3690,7 @@ def lossy_bidirectional_links(n, carrier, efficiencies={}): if "I" in opts: add_industry(n, costs) - if "I" in opts and "H" in opts: + if "H" in opts: add_waste_heat(n) if "A" in opts: # requires H and I diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 203d8b0fe..aa802ea81 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -856,7 +856,7 @@ def solve_network(n, config, solving, opts="", **kwargs): kwargs["assign_all_duals"] = cf_solving.get("assign_all_duals", False) if kwargs["solver_name"] == "gurobi": - logging.getLogger('gurobipy').setLevel(logging.CRITICAL) + logging.getLogger("gurobipy").setLevel(logging.CRITICAL) rolling_horizon = cf_solving.pop("rolling_horizon", False) skip_iterations = cf_solving.pop("skip_iterations", False)