diff --git a/config/config.default.yaml b/config/config.default.yaml index b9e8dc91..17eac834 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -457,6 +457,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 90979180..b6a10d43 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/doc/release_notes.rst b/doc/release_notes.rst index 7a045866..f1b9bcb9 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -28,6 +28,13 @@ Upcoming Release * Default to approximating transmission losses in HVAC lines (``transmission_losses: 2``). +* Add separate option to add resistive heaters to the technology choices + (``sector: resistive_heaters:``). Previously they were always added when + boilers were added. + +* Resolve code issues for endogenous building retrofitting. Select correct + sector names, address deprecations, distinguish between district heating, decentral + heating in urban areas or rural areas for floor area calculations. * Remove all negative loads on the ``co2 atmosphere`` bus representing emissions for e.g. fixed fossil demands for transport oil. Instead these are handled diff --git a/scripts/build_retro_cost.py b/scripts/build_retro_cost.py old mode 100644 new mode 100755 index 3ca2b174..d2aae140 --- 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 51ab52d9..37d6f0d2 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -460,7 +460,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 ) ) @@ -1910,7 +1910,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( @@ -1925,6 +1925,7 @@ def add_heat(n, costs): lifetime=costs.at[key, "lifetime"], ) + if options["boilers"]: key = f"{name_type} gas boiler" n.madd( @@ -2062,7 +2063,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]) @@ -2071,11 +2072,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 = ( @@ -2119,14 +2130,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, @@ -3464,7 +3476,7 @@ def cluster_heat_buses(n): def renamer(s): return s.replace("residential ", "").replace("services ", "") - pnl[k] = pnl[k].groupby(renamer, axis=1).agg(agg[k], **agg_group_kwargs) + pnl[k] = pnl[k].T.groupby(renamer).agg(agg[k], **agg_group_kwargs).T # remove unclustered assets of service/residential to_drop = c.df.index.difference(df.index) @@ -3722,7 +3734,7 @@ if __name__ == "__main__": 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