diff --git a/.flake8 b/.flake8 index 070188f8..b37ae0f2 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] max-line-length = 119 -ignore = E261 +ignore = E261, W503 exclude = Snakefile, *.smk builtins = snakemake # for using the snakemake injection in scripts diff --git a/Snakefile b/Snakefile index c4a0229f..78cc0506 100644 --- a/Snakefile +++ b/Snakefile @@ -8,6 +8,10 @@ LAND_ELIGIBILITY = "src/data/national-eligibility.csv" localrules: all, raw_load, model, clean, copy_template +onstart: + shell("mkdir -p build/logs") + + rule all: message: "Generate Euro Calliope and run tests." input: diff --git a/src/locations.py b/src/locations.py index 0c8d2223..602cab32 100644 --- a/src/locations.py +++ b/src/locations.py @@ -2,6 +2,13 @@ import jinja2 import pandas as pd +FLAT_ROOF_SHARE = 0.302 # TODO add source (own publication) +MAXIMUM_INSTALLABLE_POWER_DENSITY = { + "pv-on-tilted-roofs": 160, # [W/m^2] from (Gagnon:2016, Klauser:2016), i.e. 16% efficiency + "pv-on-flat-areas": 80, # [W/m^2] from (Gagnon:2016, Klauser:2016, Wirth:2017) # FIXME also defined in renewable-techs.yaml + "onshore-wind": 8, # [W/m^2] from (European Environment Agency, 2009) # FIXME also defined in renewable-techs.yaml + "offshore-wind": 15 # [W/m^2] from (European Environment Agency, 2009) +} TEMPLATE = """locations: {{ eligibilities.index | join(', ') }}: @@ -9,49 +16,21 @@ demand_elec: battery: hydrogen: + open_field_pv: + wind_onshore_competing: {% for country, eligibility in eligibilities.iterrows() %} - {{ country }}_pv_or_wind_farm: - available_area: {{ eligibility.eligibility_onshore_wind_and_pv_km2 }} + {{ country }}: + available_area: {{ eligibility.eligibility_onshore_wind_and_pv_km2 }} # usable by onshore wind or open field pv techs: - open_field_pv: + wind_onshore_monopoly: constraints: - resource: file=capacityfactors-open-field-pv.csv:{{ country }} - wind_onshore: - constraints: - resource: file=capacityfactors-wind-onshore.csv:{{ country }} - {{ country }}_roof_mounted_pv: - available_area: {{ eligibility.eligibility_rooftop_pv_km2 }} - techs: + energy_cap_max: {{ eligibility.eligibility_onshore_wind_monopoly_kw }} roof_mounted_pv: constraints: - resource: file=capacityfactors-rooftop-pv.csv:{{ country }} - {{ country }}_wind_offshore: - available_area: {{ eligibility.eligibility_offshore_wind_km2 }} - techs: + energy_cap_max: {{ eligibility.eligibility_rooftop_pv_kw }} wind_offshore: constraints: - resource: file=capacityfactors-wind-offshore.csv:{{ country }} - {{ country }}_wind_onshore: - available_area: {{ eligibility.eligibility_onshore_wind_km2 }} - techs: - wind_onshore: - constraints: - resource: file=capacityfactors-wind-onshore.csv:{{ country }} - {% endfor %} -links: - {% for country, _ in eligibilities.iterrows() %} - {{ country }},{{ country}}_pv_or_wind_farm: - techs: - free_transmission: - {{ country }},{{ country}}_roof_mounted_pv: - techs: - free_transmission: - {{ country }},{{ country}}_wind_offshore: - techs: - free_transmission: - {{ country }},{{ country}}_wind_onshore: - techs: - free_transmission: + energy_cap_max: {{ eligibility.eligibility_offshore_wind_kw }} {% endfor %} """ @@ -60,12 +39,31 @@ def construct_locations(path_to_land_eligibility_km2, path_to_result): """Generate a file that represents locations in Calliope.""" template = jinja2.Template(TEMPLATE) rendered = template.render( - eligibilities=pd.read_csv(path_to_land_eligibility_km2, index_col=0) + eligibilities=_from_area_to_installed_capacity( + land_eligibiligy_km2=pd.read_csv(path_to_land_eligibility_km2, index_col=0), + flat_roof_share=FLAT_ROOF_SHARE, + maximum_installable_power_density=MAXIMUM_INSTALLABLE_POWER_DENSITY + ) ) with open(path_to_result, "w") as result_file: result_file.write(rendered) +def _from_area_to_installed_capacity(land_eligibiligy_km2, flat_roof_share, + maximum_installable_power_density): + cap = land_eligibiligy_km2.copy() + factor_rooftop = ( + maximum_installable_power_density["pv-on-flat-areas"] * flat_roof_share + + maximum_installable_power_density["pv-on-tilted-roofs"] * (1 - flat_roof_share) + ) * 1000 # MW/km2 to kW/km2 + factor_onshore = maximum_installable_power_density["onshore-wind"] * 1000 # MW/km2 to kW/km2 + factor_offshore = maximum_installable_power_density["offshore-wind"] * 1000 # MW/km2 to kW/km2 + cap["eligibility_rooftop_pv_kw"] = cap["eligibility_rooftop_pv_km2"] * factor_rooftop + cap["eligibility_offshore_wind_kw"] = cap["eligibility_offshore_wind_km2"] * factor_offshore + cap["eligibility_onshore_wind_monopoly_kw"] = cap["eligibility_onshore_wind_km2"] * factor_onshore + return cap + + if __name__ == "__main__": construct_locations( path_to_land_eligibility_km2=snakemake.input.land_eligibility_km2, diff --git a/src/template/renewable-techs.yaml b/src/template/renewable-techs.yaml index a68093b6..f84affbb 100644 --- a/src/template/renewable-techs.yaml +++ b/src/template/renewable-techs.yaml @@ -5,8 +5,6 @@ tech_groups: carrier: electricity parent: supply constraints: - resource_area_per_energy_cap: 0.0000125 # [km^2/kW] from (Gagnon:2016, Klauser:2016, Wirth:2017) - resource_area_max: inf # see https://github.com/calliope-project/calliope/pull/160 energy_cap_max: inf # see https://github.com/calliope-project/calliope/issues/161 lifetime: 20 costs: @@ -19,9 +17,19 @@ tech_groups: carrier: electricity parent: supply constraints: - resource_area_max: inf # see https://github.com/calliope-project/calliope/pull/160 energy_cap_max: inf # see https://github.com/calliope-project/calliope/issues/161 lifetime: 20 + wind_onshore: + essentials: + name: Onshore wind + parent: wind + constraints: + resource: file=capacityfactors-wind-onshore.csv + resource_unit: energy_per_cap + costs: + monetary: + energy_cap: 1316.28 # EUR2016/kW from IRENA (Figure 5.2), valid for 2016 + om_annual: 49.72 # EUR2016/kW from IRENA (Table 5.1) techs: open_field_pv: @@ -29,7 +37,10 @@ techs: name: Open field PV parent: pv constraints: + # open_field_pv and wind_onshore_competing are the only technologies with area footprints + # as they are the only technologies competing on the same land. resource_area_per_energy_cap: 0.0000125 # [km^2/kW] from (Gagnon:2016, Klauser:2016, Wirth:2017) + resource_area_max: inf # see https://github.com/calliope-project/calliope/pull/160 resource: file=capacityfactors-open-field-pv.csv resource_unit: energy_per_cap roof_mounted_pv: @@ -37,27 +48,26 @@ techs: name: Roof mounted PV parent: pv constraints: - resource_area_per_energy_cap: 0.00000625 # [km^2/kW] from (Gagnon:2016, Klauser:2016) resource: file=capacityfactors-rooftop-pv.csv resource_unit: energy_per_cap - wind_onshore: + wind_onshore_monopoly: essentials: - name: Onshore wind - parent: wind + name: Onshore wind without land competition + parent: wind_onshore + wind_onshore_competing: + essentials: + name: Onshore wind competing with open field PV on land + parent: wind_onshore constraints: + # open_field_pv and wind_onshore_competing are the only technologies with area footprints + # as they are the only technologies competing on the same land. resource_area_per_energy_cap: 0.000125 # [km^2/kW] from (European Environment Agency, 2009) - resource: file=capacityfactors-wind-onshore.csv - resource_unit: energy_per_cap - costs: - monetary: - energy_cap: 1316.28 # EUR2016/kW from IRENA (Figure 5.2), valid for 2016 - om_annual: 49.72 # EUR2016/kW from IRENA (Table 5.1) + resource_area_max: inf # see https://github.com/calliope-project/calliope/pull/160 wind_offshore: essentials: name: Offshore wind parent: wind constraints: - resource_area_per_energy_cap: 0.000066667 # [km^2/kW] from (European Environment Agency, 2009) resource: file=capacityfactors-wind-offshore.csv resource_unit: energy_per_cap costs: