From df9c9f84ffa89235f71e0912023e8cdb6ffad67e Mon Sep 17 00:00:00 2001 From: Bryn Pickering <17178478+brynpickering@users.noreply.github.com> Date: Thu, 25 Jul 2024 23:23:23 +0200 Subject: [PATCH 1/4] Separate biofuel supply; add biofuel boiler --- CHANGELOG.md | 4 +- Snakefile | 13 ++-- docs/model/customisation.md | 33 ++++++++-- rules/biofuels.smk | 1 - rules/heat.smk | 2 +- scripts/biofuels/allocate.py | 2 +- scripts/biofuels/template_bio.py | 37 +++-------- scripts/summarise_potentials.py | 2 +- .../electricity-from-biofuel.yaml.jinja | 19 ++++++ .../conversion/heat-from-biofuel.yaml.jinja | 29 +++++++++ .../heat-from-electricity.yaml.jinja | 9 +-- templates/models/techs/demand/heat.yaml.jinja | 7 ++ .../models/techs/storage/heat.yaml.jinja | 4 ++ .../models/techs/supply/biofuel.yaml.jinja | 39 +++++++---- tests/model/test_model.py | 65 ++++++++++++------- tests/resources/test.yaml | 14 ++-- 16 files changed, 184 insertions(+), 96 deletions(-) create mode 100644 templates/models/techs/conversion/electricity-from-biofuel.yaml.jinja create mode 100644 templates/models/techs/conversion/heat-from-biofuel.yaml.jinja rename templates/models/techs/{supply => conversion}/heat-from-electricity.yaml.jinja (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index a57be3c8..a9285960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,13 @@ ### Added (models) +* **ADD** Specific biofuel energy carrier in the Calliope model, which can be used to generate electricity or to meet heat demand (#417). + * **ADD** Industry module: iron and steel, "default" combined categories. NOT CONNECTED TO THE MAIN WORKFLOW. (Fixes #308, #309, #310, #347, #345 and #346) * **ADD** Spatial resolution that aligns with the regions defined by the [e-Highway 2050 project](https://cordis.europa.eu/project/id/308908/reporting) (`ehighways`) (#370). -* **ADD** Heat demand (#284, #343, #389, #275) and heat pumps with variable COP to meet that demand (#80, #39). +* **ADD** Heat demand (#284, #343, #389, #275), and heat pumps with variable COP and biofuel boilers to meet that demand (#80, #39). * **ADD** fully-electrified road transportation (#270, #271, #358). A parameter allows to define the share of uncontrolled (timeseries) vs controlled charging (optimised) by the solver (#338). Data for controlled charging constraints is readily available (#356), but corresponding constraints are not yet implemented (#385). diff --git a/Snakefile b/Snakefile index 68be368c..d48b4b0e 100644 --- a/Snakefile +++ b/Snakefile @@ -104,6 +104,7 @@ rule module_with_location_specific_data: capacity_factors = config["capacity-factors"]["average"], max_power_densities = config["parameters"]["maximum-installable-power-density"], heat_pump_shares = config["parameters"]["heat-pump"]["heat-pump-shares"], + biofuel_efficiency = config["parameters"]["biofuel-efficiency"], wildcard_constraints: # Exclude all outputs that have their own `techs_and_locations_template` implementation group_and_tech = "(?!transmission\/|supply\/biofuel).*" @@ -162,10 +163,11 @@ rule model: "techs/supply/rooftop-solar.yaml", "techs/supply/wind-offshore.yaml", "techs/supply/nuclear.yaml", + "techs/conversion/electricity-from-biofuel.yaml" ] ), - heat_supply_timeseries_data = ( - "build/models/{resolution}/timeseries/supply/heat-pump-cop.csv", + heat_timeseries_data = ( + "build/models/{resolution}/timeseries/conversion/heat-pump-cop.csv", "build/models/{resolution}/timeseries/supply/historic-electrified-heat.csv", ), capacityfactor_timeseries_data = expand( @@ -198,7 +200,8 @@ rule model: "techs/demand/heat.yaml", "techs/demand/electrified-heat.yaml", "techs/storage/heat.yaml", - "techs/supply/heat-from-electricity.yaml", + "techs/conversion/heat-from-electricity.yaml", + "techs/conversion/heat-from-biofuel.yaml", "techs/supply/historic-electrified-heat.yaml" ] ) @@ -258,10 +261,10 @@ rule test: electrified_heat_demand = "build/models/{resolution}/timeseries/demand/electrified-heat.csv", heat_demand = "build/models/{resolution}/timeseries/demand/heat.csv", historic_electrified_heat = "build/models/{resolution}/timeseries/supply/historic-electrified-heat.csv", - cop = "build/models/{resolution}/timeseries/supply/heat-pump-cop.csv" + cop = "build/models/{resolution}/timeseries/conversion/heat-pump-cop.csv" params: config = config, - test_args = ["--pdb"] # add e.g. "--pdb" to enter ipdb on test failure + test_args = [] # add e.g. "--pdb" to enter ipdb on test failure log: "build/logs/{resolution}/test-report.html" output: "build/logs/{resolution}/test.success" conda: "./envs/test.yaml" diff --git a/docs/model/customisation.md b/docs/model/customisation.md index d6060e06..87ed0575 100644 --- a/docs/model/customisation.md +++ b/docs/model/customisation.md @@ -15,13 +15,13 @@ You have the following three options: With the Calliope model in your hands, you will be able to change any model parameter, any technology specifics, and the model definition to your liking. This kind of customisation can be useful to get to know the model and its parameters. To create reliable results, we advise making manual changes only to the model definition (`example-model.yaml`) as this makes it possible to trace those changes later. -A typical customisation here would be to change the solver from `gurobi` to an open-source solver, e.g. `cbc` (see [Calliope's documentation](https://calliope.readthedocs.io/en/v{{ calliope_version }}/user/config_defaults.html#run-configuration)). +A typical customisation here would be to change the solver from `gurobi` to an open-source solver, e.g. `cbc` (see [Calliope's documentation]( calliope_version }}/user/config_defaults.html#run-configuration)). We consider all Euro-Calliope model subcomponents (everything other than the model definition itself) as a toolbox from which you can choose to define your model -- see the [Import customisation option](./customisation.md#importing-modules). ## Importing modules The `example-model.yaml` definition file in each resolution sub-directory (e.g. `national/example-model.yaml`) specifies a list of other files to bring together to describe the model (under the `import` key). -This list can be changed by the modeller to select a combination of different files (see also [Calliope's documentation](https://calliope.readthedocs.io/en/v{{ calliope_version }}/user/building.html#files-that-define-a-model)). +This list can be changed by the modeller to select a combination of different files (see also [Calliope's documentation]( calliope_version }}/user/building.html#files-that-define-a-model)). These files represent "modules" of the model definition and contain everything necessary for a given technology or technology group to exist. For instance, `techs/supply/hydro.yaml` defines two technologies (under the `techs` key) which will convert river flows into electricity. It also places that technology in every relevant modelled location (under the `locations` key), along with any location-specific information that is needed; in this case, the maximum capacity of hydropower in that location. @@ -86,6 +86,8 @@ Here, we describe each module in terms of the technologies they contain (`callio **electric_heater_heat_storage_small**: Storage buffer for direct electric heaters which inherits from the `heat_storage_small` abstract technology group, assuming a domestic (small scale) application. + **biofuel_heat_storage_small**: Storage buffer for biofuel boilers which inherits from the `heat_storage_small` abstract technology group, assuming a domestic (small scale) application. + ??? note "storage/hydro.yaml" === "Technologies" @@ -102,9 +104,27 @@ Here, we describe each module in terms of the technologies they contain (`callio === "Technologies" - **biofuel**: Biofuel + **biofuel**: Biofuel supply, limited per model location to a total annual production. + + === "Overrides" + + **biofuel_flow_limits**: Distribute annual total production limit evenly across all hours of the year and allow biofuel storage up to 50% of total annual production. + +??? note "conversion/heat-from-biofuel.yaml" + + === "Technologies" + + **biofuel_boiler**: Biofuel-consuming boiler. + + **biofuel_tech_heat_to_demand**: Dummy technology to convert biofuel boiler output to a carrier that can be used to meet heat demand. -??? note "supply/heat-from-electricity.yaml" +??? note "conversion/electricity-from-biofuel.yaml" + + === "Technologies" + + **electricity_from_biofuel**: Biofuel-consuming electricity production facility (assuming anaerobic digestion). + +??? note "conversion/heat-from-electricity.yaml" === "Technologies" @@ -221,10 +241,9 @@ Here, we describe each module in terms of the technologies they contain (`callio **free_transmission**: Local power transmission - ## Overrides and scenarios -Calliope [overrides](https://calliope.readthedocs.io/en/v{{ calliope_version }}/user/building.html#scenarios-and-overrides) enable models to be easily manipulated. +Calliope [overrides]( calliope_version }}/user/building.html#scenarios-and-overrides) enable models to be easily manipulated. An override named `freeze-hydro-supply-capacities` can be used for example in this way: ``` bash @@ -243,7 +262,7 @@ For instance, `freeze-hydro-supply-capacities` and `freeze-hydro-storage-capacit You can also define your own overrides to manipulate any model component. We recommend you add these overrides into the model definition YAML file, to ensure they are easy to trace. -In Calliope, [scenarios](https://calliope.readthedocs.io/en/v{{ calliope_version }}/user/building.html#scenarios-and-overrides) are groups of overrides and/or other scenarios. +In Calliope, [scenarios]( calliope_version }}/user/building.html#scenarios-and-overrides) are groups of overrides and/or other scenarios. In Euro-Calliope, it can be helpful to define scenarios to help group similar overrides together. For instance, cost overrides from the Danish Energy Agency are defined in various files, since they are loaded in alongside the technologies they affect (the option to override offshore wind costs only exists when you load the `techs/supply/wind-offshore.yaml` module). You can pre-define scenarios in your model definition file, such as: diff --git a/rules/biofuels.smk b/rules/biofuels.smk index 7dcc89c2..84af68a7 100644 --- a/rules/biofuels.smk +++ b/rules/biofuels.smk @@ -60,7 +60,6 @@ rule biofuel_tech_module: ), locations = "build/data/{{resolution}}/biofuel/{scenario}/potential-mwh-per-year.csv".format(scenario=config["parameters"]["jrc-biofuel"]["scenario"]) params: - biofuel_efficiency = config["parameters"]["biofuel-efficiency"], scaling_factors = config["scaling-factors"], conda: "../envs/default.yaml" output: "build/models/{resolution}/techs/supply/biofuel.yaml" diff --git a/rules/heat.smk b/rules/heat.smk index 865a8f35..2a9bfb66 100644 --- a/rules/heat.smk +++ b/rules/heat.smk @@ -123,7 +123,7 @@ rule heat_pump_final_timeseries: conda: "../envs/default.yaml" wildcard_constraints: input_dataset = "heat-pump-cop" - output: "build/models/{resolution}/timeseries/supply/{input_dataset}.csv" + output: "build/models/{resolution}/timeseries/conversion/{input_dataset}.csv" script: "../scripts/heat/heat_pump_final_timeseries.py" diff --git a/scripts/biofuels/allocate.py b/scripts/biofuels/allocate.py index 0e3c23b5..d5e71fbd 100644 --- a/scripts/biofuels/allocate.py +++ b/scripts/biofuels/allocate.py @@ -85,7 +85,7 @@ def biofuel_potential( index_col=["year", "scenario", "country_code", "feedstock"], )["value"] .div(GJ_TO_MWH) - .xs((potential_year, scenario), level=("year", "scenario")) + .xs((cost_year, scenario), level=("year", "scenario")) ) units = pd.read_csv(path_to_units).set_index("id") if (len(units.index) == 1) and ( diff --git a/scripts/biofuels/template_bio.py b/scripts/biofuels/template_bio.py index e6270c35..f2f17f23 100644 --- a/scripts/biofuels/template_bio.py +++ b/scripts/biofuels/template_bio.py @@ -1,35 +1,14 @@ import pandas as pd from eurocalliopelib.template import parametrise_template - -def construct_techs_and_locations( - path_to_template, - path_to_output, - path_to_locations, - path_to_biofuel_costs, - biofuel_efficiency, - scaling_factors, -): - with open(path_to_biofuel_costs) as f_biofuel_costs: - biofuel_fuel_cost = float(f_biofuel_costs.readline()) - locations = pd.read_csv(path_to_locations, index_col=0) - - return parametrise_template( - path_to_template, - path_to_output, - biofuel_fuel_cost=biofuel_fuel_cost, - biofuel_efficiency=biofuel_efficiency, - scaling_factors=scaling_factors, - locations=locations, - ) - - if __name__ == "__main__": - construct_techs_and_locations( - path_to_template=snakemake.input.template, - path_to_locations=snakemake.input.locations, - path_to_biofuel_costs=snakemake.input.biofuel_cost, - biofuel_efficiency=snakemake.params.biofuel_efficiency, + biofuel_cost = float(pd.read_csv(snakemake.input.biofuel_cost).columns[0]) + locations = pd.read_csv(snakemake.input.locations, index_col=0) + + parametrise_template( + snakemake.input.template, + snakemake.output[0], + biofuel_cost=biofuel_cost, scaling_factors=snakemake.params.scaling_factors, - path_to_output=snakemake.output[0], + locations=locations, ) diff --git a/scripts/summarise_potentials.py b/scripts/summarise_potentials.py index 243eff82..e5b15608 100644 --- a/scripts/summarise_potentials.py +++ b/scripts/summarise_potentials.py @@ -42,7 +42,7 @@ def summarise_potentials( list_of_techs = model.inputs.techs.values list_of_locs = model.inputs.locs.values - list_of_potentials = list(considered_potentials.keys()) + list_of_potentials = list(set(considered_potentials).intersection(model.inputs)) summary = np.empty((len(list_of_techs), len(list_of_potentials), len(list_of_locs))) summary[:] = np.nan diff --git a/templates/models/techs/conversion/electricity-from-biofuel.yaml.jinja b/templates/models/techs/conversion/electricity-from-biofuel.yaml.jinja new file mode 100644 index 00000000..5fdb9a4b --- /dev/null +++ b/templates/models/techs/conversion/electricity-from-biofuel.yaml.jinja @@ -0,0 +1,19 @@ +techs: + electricity_from_biofuel: # from [@JRC:2014] Table 48 Anaerobic digestion + essentials: + name: Electricity from anaerobically digested biofuel + parent: conversion + carrier_in: biofuel + carrier_out: electricity + constraints: + energy_eff: {{ biofuel_efficiency }} + lifetime: 20 + costs.monetary: + energy_cap: {{2300000 * scaling_factors.specific_costs}} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MW") }} + om_annual: {{2300000 * 0.041 * scaling_factors.specific_costs}} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MW") }} 4.1% of CAPEX + om_prod: {{ 3.1 * scaling_factors.specific_costs }} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MWh") }} + +locations: + {% for id, location in locations.iterrows() %} + {{ id }}.techs.electricity_from_biofuel: + {% endfor %} \ No newline at end of file diff --git a/templates/models/techs/conversion/heat-from-biofuel.yaml.jinja b/templates/models/techs/conversion/heat-from-biofuel.yaml.jinja new file mode 100644 index 00000000..9b4370a3 --- /dev/null +++ b/templates/models/techs/conversion/heat-from-biofuel.yaml.jinja @@ -0,0 +1,29 @@ +{# TODO: weight single-/multi-family technology costs based on regional dwelling ratio - see https://github.com/calliope-project/euro-calliope/issues/406 #} +{# Costs/efficiency being averaged are given in the order they appear in the spreadsheet (which is the same order as in the inline comments). #} +{# Costs are given by DEA per technology "unit" (1000EUR/unit), so are converted to a cost per capacity (1000EUR/kW_heating) by dividing by the capacity of one unit, as given in the same data table. #} + +techs: + biofuel_boiler: # [@DEA:2017] - Biomass boiler, automatic stoking , wood pellets or wood chips - 2050 + # Costs and efficiency are an average of data for existing single-family, new single-family, existing multi-family, and new multi-family homes. + essentials: + name: Biofuel boiler + parent: conversion + carrier_in: biofuel + carrier_out: biofuel_heat + constraints: + energy_eff: {{ mean([0.88, 0.85, 0.90, 0.90]) }} + lifetime: 20 + costs: + monetary: + energy_cap: {{ mean([5.9 / 10, 5.9 / 8, 76 / 400, 45 / 160]) * 1e6 * scaling_factors.specific_costs }} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2015/MW_heat") }} + om_annual: {{ mean([0.42 / 10, 0.42 / 10, 1.343 / 400, 0.889 / 160]) * 1e6 * scaling_factors.specific_costs }} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2015/MW_heat/year") }} + biofuel_tech_heat_to_demand: + essentials.parent: tech_heat_to_demand + essentials.carrier_in: biofuel_heat + +locations: + {% for id, location in locations.iterrows() %} + {{ id }}.techs: + biofuel_boiler: + biofuel_tech_heat_to_demand: + {% endfor %} diff --git a/templates/models/techs/supply/heat-from-electricity.yaml.jinja b/templates/models/techs/conversion/heat-from-electricity.yaml.jinja similarity index 90% rename from templates/models/techs/supply/heat-from-electricity.yaml.jinja rename to templates/models/techs/conversion/heat-from-electricity.yaml.jinja index 39acf8f7..4184f90e 100644 --- a/templates/models/techs/supply/heat-from-electricity.yaml.jinja +++ b/templates/models/techs/conversion/heat-from-electricity.yaml.jinja @@ -2,13 +2,6 @@ {# Costs being averaged are given in the order they appear in the spreadsheet (which is the same order as in the inline comments). #} {# Costs are given by DEA per technology "unit" (1000EUR/unit), so are converted to a cost per capacity (1000EUR/kW_heating) by dividing by the capacity of one unit, as given in the same data table. #} -tech_groups: - tech_heat_to_demand: - essentials: - name: Technology-specific heat carriers to generic heat carrier converter - parent: conversion - carrier_out: heat - techs: heat_pump: # [@DEA:2017] - 7.3 - 7.6 Air to water & 7.7 - 7.10 Ground source - 2050 essentials: @@ -17,7 +10,7 @@ techs: carrier_in: electricity carrier_out: hp_heat constraints: - energy_eff: file=supply/heat-pump-cop.csv # NOTE: based on data processing pipeline, not [@DEA:2017]. + energy_eff: file=conversion/heat-pump-cop.csv # NOTE: based on data processing pipeline, not [@DEA:2017]. lifetime: 20 costs: monetary: diff --git a/templates/models/techs/demand/heat.yaml.jinja b/templates/models/techs/demand/heat.yaml.jinja index d479a868..164690fa 100644 --- a/templates/models/techs/demand/heat.yaml.jinja +++ b/templates/models/techs/demand/heat.yaml.jinja @@ -1,3 +1,10 @@ +tech_groups: + tech_heat_to_demand: + essentials: + name: Technology-specific heat carriers to generic heat carrier converter + parent: conversion + carrier_out: heat + techs: demand_heat: essentials: diff --git a/templates/models/techs/storage/heat.yaml.jinja b/templates/models/techs/storage/heat.yaml.jinja index 230b08f4..c11d5db4 100644 --- a/templates/models/techs/storage/heat.yaml.jinja +++ b/templates/models/techs/storage/heat.yaml.jinja @@ -19,10 +19,14 @@ techs: electric_heater_heat_storage_small: essentials.parent: heat_storage_small essentials.carrier: electric_heater_heat + biofuel_heat_storage_small: + essentials.parent: heat_storage_small + essentials.carrier: biofuel_heat locations: {% for id, location in locations.iterrows() %} {{ id }}: techs.hp_heat_storage_small: techs.electric_heater_heat_storage_small: + techs.biofuel_heat_storage_small: {% endfor %} diff --git a/templates/models/techs/supply/biofuel.yaml.jinja b/templates/models/techs/supply/biofuel.yaml.jinja index 11dfdeab..15c5b834 100644 --- a/templates/models/techs/supply/biofuel.yaml.jinja +++ b/templates/models/techs/supply/biofuel.yaml.jinja @@ -1,23 +1,34 @@ techs: - biofuel: # from [@JRC:2014] Table 48 Anaerobic digestion + biofuel_supply: essentials: - name: Biofuel + name: Biofuel supply stream parent: supply_plus - carrier: electricity + carrier: biofuel constraints: - energy_eff: 1.0 # efficiency modelled within the input resource stream to avoid poor numerical scaling - lifetime: 20 + lifetime: 1 # arbritrarily chosen to avoid Calliope errors costs.monetary: - energy_cap: {{2300000 * scaling_factors.specific_costs}} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MW") }} - om_annual: {{2300000 * 0.041 * scaling_factors.specific_costs}} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MW") }} 4.1% of CAPEX - om_con: {{ (biofuel_fuel_cost / biofuel_efficiency + 3.1) * scaling_factors.specific_costs }} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MW") }} - om_prod: 0 # 3.1 (EUR2013/MWh) added to om_con because value is very small and causing poor numerical range + om_prod: {{ biofuel_cost * scaling_factors.specific_costs }} # {{ (1 / scaling_factors.specific_costs) | unit("EUR/MWh") }} locations: {% for id, location in locations.iterrows() %} - {{ id }}.techs: - biofuel: - constraints: - resource: {{ location.biofuel_potential_mwh_per_year * biofuel_efficiency / 8760 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MW") }} - storage_cap_equals: {{ location.biofuel_potential_mwh_per_year * biofuel_efficiency / 2 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} (0.5x annual yield) # ASSUME < 1 for numerical range + {{ id }}.techs.biofuel_supply: {% endfor %} + +group_constraints: + {% for id, location in locations.iterrows() %} + biofuel_max_prod_{{ id }}: + locs: [{{ id }}] + techs: [biofuel_supply] + carrier_prod_max: + biofuel: {{ location.biofuel_potential_mwh_per_year * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} + {% endfor %} + +overrides: + biofuel_flow_limits: + locations: + {% for id, location in locations.iterrows() %} + {{ id }}.techs.biofuel_supply: + constraints: + resource: {{ location.biofuel_potential_mwh_per_year / 8760 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MW") }} + storage_cap_equals: {{ location.biofuel_potential_mwh_per_year / 2 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} (0.5x annual yield) # ASSUME < 1 for numerical range + {% endfor %} diff --git a/tests/model/test_model.py b/tests/model/test_model.py index 12417dd4..7aca3473 100644 --- a/tests/model/test_model.py +++ b/tests/model/test_model.py @@ -1,27 +1,44 @@ import pandas as pd import pytest -DEFAULT_TECHNOLOGIES = set([ - "battery", - "hydrogen", - "open_field_pv", - "wind_onshore_competing", - "wind_onshore_monopoly", - "roof_mounted_pv", - "wind_offshore", - "hydro_run_of_river", - "hydro_reservoir", - "pumped_hydro", - "biofuel", - "demand_elec", - "nuclear", -]) -DIRECTIONAL_PV = set([ - "roof_mounted_pv_s_flat", - "roof_mounted_pv_n", - "roof_mounted_pv_e_w", -]) - +DEFAULT_TECHNOLOGIES = set( + [ + "battery", + "hydrogen", + "open_field_pv", + "wind_onshore_competing", + "wind_onshore_monopoly", + "roof_mounted_pv", + "wind_offshore", + "hydro_run_of_river", + "hydro_reservoir", + "pumped_hydro", + "biofuel_supply", + "electricity_from_biofuel", + "demand_elec", + "nuclear", + ] +) +DIRECTIONAL_PV = set( + [ + "roof_mounted_pv_s_flat", + "roof_mounted_pv_n", + "roof_mounted_pv_e_w", + ] +) +HEAT_TECHS = set( + [ + "biofuel_boiler", + "biofuel_tech_heat_to_demand", + "heat_pump", + "heat_pump_tech_heat_to_demand", + "electric_heater", + "electric_heater_tech_heat_to_demand", + "hp_heat_storage_small", + "electric_heater_heat_storage_small", + "biofuel_heat_storage_small", + ] +) # Only includes scenarios with non-default technology sets TECHNOLOGIES = { "connected_all_neighbours": DEFAULT_TECHNOLOGIES | set(["ac_transmission"]), @@ -30,9 +47,11 @@ - set(["roof_mounted_pv"]), "shed-load": DEFAULT_TECHNOLOGIES | set(["load_shedding"]), "all-overrides": ( - (DEFAULT_TECHNOLOGIES | DIRECTIONAL_PV | set(["load_shedding"])) + (DEFAULT_TECHNOLOGIES | DIRECTIONAL_PV | set(["load_shedding"]) | HEAT_TECHS) - set(["roof_mounted_pv"]) ), + "electrified-heat": DEFAULT_TECHNOLOGIES | set(["historic_electrified_heat"]), + "heat": DEFAULT_TECHNOLOGIES | HEAT_TECHS, } OPTIONAL_LOCATIONAL_TECHNOLOGIES = ["nuclear"] @@ -69,7 +88,7 @@ def test_technologies_are_available(energy_cap, location, technologies): def test_heat_carrier_exists(model, scenario): - if scenario == "heat": + if scenario in ["heat", "all-overrides"]: assert "heat" in model.inputs.carriers else: assert "heat" not in model.inputs.carriers diff --git a/tests/resources/test.yaml b/tests/resources/test.yaml index 891e5846..6ddbb267 100644 --- a/tests/resources/test.yaml +++ b/tests/resources/test.yaml @@ -20,15 +20,16 @@ test-model: keep-historic-transport: ["keep-historic-electricity-demand-from-road-transport"] heat: ["heat_end_use"] electrified-heat: ["electrified_heat"] + biofuel-flow-limited: ["biofuel_flow_limits"] regional: default: ["connect_all_neighbours", "run_barrier_no_crossover"] all-overrides: ["connect_all_neighbours", "directional-rooftop-pv", "exclusive-energy-to-power-ratios", - "dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost", "freeze-hydro-supply-capacities", "freeze-hydro-storage-capacities", "load-shedding", "heat_end_use"] + "dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost", "freeze-hydro-supply-capacities", "freeze-hydro-storage-capacities", "load-shedding", "heat_end_use", "biofuel_flow_limits"] ehighways: default: ["connect_all_neighbours", "run_barrier_no_crossover"] all-overrides: ["connect_all_neighbours", "directional-rooftop-pv", "exclusive-energy-to-power-ratios", - "dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost", "freeze-hydro-supply-capacities", "freeze-hydro-storage-capacities", "load-shedding", "heat_end_use"] + "dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost", "freeze-hydro-supply-capacities", "freeze-hydro-storage-capacities", "load-shedding", "heat_end_use", "biofuel_flow_limits"] overrides: continental: {} national: @@ -46,7 +47,8 @@ test-model: import: - "build/models/national/techs/demand/heat.yaml" - "build/models/national/techs/storage/heat.yaml" - - "build/models/national/techs/supply/heat-from-electricity.yaml" + - "build/models/national/techs/conversion/heat-from-electricity.yaml" + - "build/models/national/techs/conversion/heat-from-biofuel.yaml" - "build/models/national/techs/supply/historic-electrified-heat.yaml" regional: @@ -57,7 +59,8 @@ test-model: import: - "build/models/regional/techs/demand/heat.yaml" - "build/models/regional/techs/storage/heat.yaml" - - "build/models/regional/techs/supply/heat-from-electricity.yaml" + - "build/models/regional/techs/conversion/heat-from-electricity.yaml" + - "build/models/regional/techs/conversion/heat-from-biofuel.yaml" - "build/models/regional/techs/supply/historic-electrified-heat.yaml" run_barrier_no_crossover: @@ -70,7 +73,8 @@ test-model: import: - "build/models/ehighways/techs/demand/heat.yaml" - "build/models/ehighways/techs/storage/heat.yaml" - - "build/models/ehighways/techs/supply/heat-from-electricity.yaml" + - "build/models/ehighways/techs/conversion/heat-from-electricity.yaml" + - "build/models/ehighways/techs/conversion/heat-from-biofuel.yaml" - "build/models/ehighways/techs/supply/historic-electrified-heat.yaml" run_barrier_no_crossover: From 2fb31dfeda2b5a16aa321756443ce63ec0013335 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 21:25:33 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../electricity-from-biofuel.yaml.jinja | 2 +- tests/model/test_model.py | 70 +++++++++---------- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/templates/models/techs/conversion/electricity-from-biofuel.yaml.jinja b/templates/models/techs/conversion/electricity-from-biofuel.yaml.jinja index 5fdb9a4b..37f0c5a7 100644 --- a/templates/models/techs/conversion/electricity-from-biofuel.yaml.jinja +++ b/templates/models/techs/conversion/electricity-from-biofuel.yaml.jinja @@ -16,4 +16,4 @@ techs: locations: {% for id, location in locations.iterrows() %} {{ id }}.techs.electricity_from_biofuel: - {% endfor %} \ No newline at end of file + {% endfor %} diff --git a/tests/model/test_model.py b/tests/model/test_model.py index 7aca3473..20c69cbc 100644 --- a/tests/model/test_model.py +++ b/tests/model/test_model.py @@ -1,44 +1,38 @@ import pandas as pd import pytest -DEFAULT_TECHNOLOGIES = set( - [ - "battery", - "hydrogen", - "open_field_pv", - "wind_onshore_competing", - "wind_onshore_monopoly", - "roof_mounted_pv", - "wind_offshore", - "hydro_run_of_river", - "hydro_reservoir", - "pumped_hydro", - "biofuel_supply", - "electricity_from_biofuel", - "demand_elec", - "nuclear", - ] -) -DIRECTIONAL_PV = set( - [ - "roof_mounted_pv_s_flat", - "roof_mounted_pv_n", - "roof_mounted_pv_e_w", - ] -) -HEAT_TECHS = set( - [ - "biofuel_boiler", - "biofuel_tech_heat_to_demand", - "heat_pump", - "heat_pump_tech_heat_to_demand", - "electric_heater", - "electric_heater_tech_heat_to_demand", - "hp_heat_storage_small", - "electric_heater_heat_storage_small", - "biofuel_heat_storage_small", - ] -) +DEFAULT_TECHNOLOGIES = set([ + "battery", + "hydrogen", + "open_field_pv", + "wind_onshore_competing", + "wind_onshore_monopoly", + "roof_mounted_pv", + "wind_offshore", + "hydro_run_of_river", + "hydro_reservoir", + "pumped_hydro", + "biofuel_supply", + "electricity_from_biofuel", + "demand_elec", + "nuclear", +]) +DIRECTIONAL_PV = set([ + "roof_mounted_pv_s_flat", + "roof_mounted_pv_n", + "roof_mounted_pv_e_w", +]) +HEAT_TECHS = set([ + "biofuel_boiler", + "biofuel_tech_heat_to_demand", + "heat_pump", + "heat_pump_tech_heat_to_demand", + "electric_heater", + "electric_heater_tech_heat_to_demand", + "hp_heat_storage_small", + "electric_heater_heat_storage_small", + "biofuel_heat_storage_small", +]) # Only includes scenarios with non-default technology sets TECHNOLOGIES = { "connected_all_neighbours": DEFAULT_TECHNOLOGIES | set(["ac_transmission"]), From 7e32a083e5fca1f689bac681ee56ade9025b66fb Mon Sep 17 00:00:00 2001 From: Bryn Pickering <17178478+brynpickering@users.noreply.github.com> Date: Fri, 2 Aug 2024 10:35:28 +0100 Subject: [PATCH 3/4] Introduce electricity-only biofuel tech; remove annual biofuel supply limit --- Snakefile | 14 +++- docs/model/customisation.md | 20 +++-- rules/biofuels.smk | 9 +- scripts/biofuels/template_bio.py | 1 + .../models/techs/supply/biofuel.yaml.jinja | 26 +----- .../supply/electrified-biofuel.yaml.jinja | 23 +++++ tests/model/test_model.py | 84 +++++++++++-------- tests/model/test_runner.py | 4 +- tests/resources/test.yaml | 36 ++++++-- 9 files changed, 138 insertions(+), 79 deletions(-) create mode 100644 templates/models/techs/supply/electrified-biofuel.yaml.jinja diff --git a/Snakefile b/Snakefile index d48b4b0e..05a8377b 100644 --- a/Snakefile +++ b/Snakefile @@ -10,6 +10,7 @@ root_dir = config["root-directory"] + "/" if config["root-directory"] not in ["" __version__ = open(f"{root_dir}VERSION").readlines()[0].strip() test_dir = f"{root_dir}tests/" model_test_dir = f"{test_dir}model" +resources_test_dir = f"{test_dir}resources" template_dir = f"{root_dir}templates/" model_template_dir = f"{template_dir}models/" techs_template_dir = f"{model_template_dir}techs/" @@ -107,7 +108,7 @@ rule module_with_location_specific_data: biofuel_efficiency = config["parameters"]["biofuel-efficiency"], wildcard_constraints: # Exclude all outputs that have their own `techs_and_locations_template` implementation - group_and_tech = "(?!transmission\/|supply\/biofuel).*" + group_and_tech = "(?!transmission\/|supply\/biofuel|supply\/electrified-biofuel).*" conda: "envs/default.yaml" output: "build/models/{resolution}/techs/{group_and_tech}.yaml" script: "scripts/template_techs.py" @@ -156,14 +157,12 @@ rule model: "techs/demand/electrified-transport.yaml", "techs/storage/electricity.yaml", "techs/storage/hydro.yaml", - "techs/supply/biofuel.yaml", "techs/supply/hydro.yaml", "techs/supply/load-shedding.yaml", "techs/supply/open-field-solar-and-wind-onshore.yaml", "techs/supply/rooftop-solar.yaml", "techs/supply/wind-offshore.yaml", "techs/supply/nuclear.yaml", - "techs/conversion/electricity-from-biofuel.yaml" ] ), heat_timeseries_data = ( @@ -204,6 +203,14 @@ rule model: "techs/conversion/heat-from-biofuel.yaml", "techs/supply/historic-electrified-heat.yaml" ] + ), + optional_biofuel_modules = expand( + "build/models/{{resolution}}/{module}", + module=[ + "techs/supply/biofuel.yaml", + "techs/supply/electrified-biofuel.yaml", + "techs/conversion/electricity-from-biofuel.yaml" + ] ) params: year = config["scope"]["temporal"]["first-year"] @@ -252,6 +259,7 @@ rule test: message: "Run tests" input: test_dir = model_test_dir, + test_resources_dir = resources_test_dir, tests = map(str, Path(model_test_dir).glob("**/test_*.py")), example_model = "build/models/{resolution}/example-model.yaml", capacity_factor_timeseries = expand( diff --git a/docs/model/customisation.md b/docs/model/customisation.md index 87ed0575..3d57faf3 100644 --- a/docs/model/customisation.md +++ b/docs/model/customisation.md @@ -15,13 +15,13 @@ You have the following three options: With the Calliope model in your hands, you will be able to change any model parameter, any technology specifics, and the model definition to your liking. This kind of customisation can be useful to get to know the model and its parameters. To create reliable results, we advise making manual changes only to the model definition (`example-model.yaml`) as this makes it possible to trace those changes later. -A typical customisation here would be to change the solver from `gurobi` to an open-source solver, e.g. `cbc` (see [Calliope's documentation]( calliope_version }}/user/config_defaults.html#run-configuration)). +A typical customisation here would be to change the solver from `gurobi` to an open-source solver, e.g. `cbc` (see [Calliope's documentation](https://calliope.readthedocs.io/en/v{{ calliope_version }}/user/config_defaults.html#run-configuration)). We consider all Euro-Calliope model subcomponents (everything other than the model definition itself) as a toolbox from which you can choose to define your model -- see the [Import customisation option](./customisation.md#importing-modules). ## Importing modules The `example-model.yaml` definition file in each resolution sub-directory (e.g. `national/example-model.yaml`) specifies a list of other files to bring together to describe the model (under the `import` key). -This list can be changed by the modeller to select a combination of different files (see also [Calliope's documentation]( calliope_version }}/user/building.html#files-that-define-a-model)). +This list can be changed by the modeller to select a combination of different files (see also [Calliope's documentation](https://calliope.readthedocs.io/en/v{{ calliope_version }}/user/building.html#files-that-define-a-model)). These files represent "modules" of the model definition and contain everything necessary for a given technology or technology group to exist. For instance, `techs/supply/hydro.yaml` defines two technologies (under the `techs` key) which will convert river flows into electricity. It also places that technology in every relevant modelled location (under the `locations` key), along with any location-specific information that is needed; in this case, the maximum capacity of hydropower in that location. @@ -104,11 +104,15 @@ Here, we describe each module in terms of the technologies they contain (`callio === "Technologies" - **biofuel**: Biofuel supply, limited per model location to a total annual production. + **biofuel**: Biofuel supply, limited per model location to an hourly flow that can be stored before use in downstream technologies. - === "Overrides" +??? note "supply/electrified-biofuel.yaml" + + === "Technologies" - **biofuel_flow_limits**: Distribute annual total production limit evenly across all hours of the year and allow biofuel storage up to 50% of total annual production. + **electrified_biofuel**: Electrified biofuel supply (assuming anaerobic digestion). + This should be used in an electricity-only model, where biofuel supply is assumed to only be used for direct generation of electricity. + This simplifies the model compared to using `supply/biofuel.yaml` and `conversion/electricity-from-biofuel.yaml` by not introducing the `biofuel` energy carrier. ??? note "conversion/heat-from-biofuel.yaml" @@ -116,7 +120,7 @@ Here, we describe each module in terms of the technologies they contain (`callio **biofuel_boiler**: Biofuel-consuming boiler. - **biofuel_tech_heat_to_demand**: Dummy technology to convert biofuel boiler output to a carrier that can be used to meet heat demand. + **biofuel_tech_heat_to_demand**: "Dummy" technology to convert biofuel boiler output to a carrier that can be used to meet heat demand. ??? note "conversion/electricity-from-biofuel.yaml" @@ -243,7 +247,7 @@ Here, we describe each module in terms of the technologies they contain (`callio ## Overrides and scenarios -Calliope [overrides]( calliope_version }}/user/building.html#scenarios-and-overrides) enable models to be easily manipulated. +Calliope [overrides](https://calliope.readthedocs.io/en/v{{ calliope_version }}/user/building.html#scenarios-and-overrides) enable models to be easily manipulated. An override named `freeze-hydro-supply-capacities` can be used for example in this way: ``` bash @@ -262,7 +266,7 @@ For instance, `freeze-hydro-supply-capacities` and `freeze-hydro-storage-capacit You can also define your own overrides to manipulate any model component. We recommend you add these overrides into the model definition YAML file, to ensure they are easy to trace. -In Calliope, [scenarios]( calliope_version }}/user/building.html#scenarios-and-overrides) are groups of overrides and/or other scenarios. +In Calliope, [scenarios](https://calliope.readthedocs.io/en/v{{ calliope_version }}/user/building.html#scenarios-and-overrides) are groups of overrides and/or other scenarios. In Euro-Calliope, it can be helpful to define scenarios to help group similar overrides together. For instance, cost overrides from the Danish Energy Agency are defined in various files, since they are loaded in alongside the technologies they affect (the option to override offshore wind costs only exists when you load the `techs/supply/wind-offshore.yaml` module). You can pre-define scenarios in your model definition file, such as: diff --git a/rules/biofuels.smk b/rules/biofuels.smk index 84af68a7..303f2905 100644 --- a/rules/biofuels.smk +++ b/rules/biofuels.smk @@ -52,15 +52,18 @@ rule biofuels: rule biofuel_tech_module: - message: "Create biofuel tech definition file from template." + message: "Create {wildcards.tech_module} tech definition file from template." input: - template = techs_template_dir + "supply/biofuel.yaml.jinja", + template = techs_template_dir + "supply/{tech_module}.yaml.jinja", biofuel_cost = "build/data/regional/biofuel/{scenario}/costs-eur-per-mwh.csv".format( scenario=config["parameters"]["jrc-biofuel"]["scenario"] ), locations = "build/data/{{resolution}}/biofuel/{scenario}/potential-mwh-per-year.csv".format(scenario=config["parameters"]["jrc-biofuel"]["scenario"]) params: scaling_factors = config["scaling-factors"], + biofuel_efficiency = config["parameters"]["biofuel-efficiency"] conda: "../envs/default.yaml" - output: "build/models/{resolution}/techs/supply/biofuel.yaml" + output: "build/models/{resolution}/techs/supply/{tech_module}.yaml" + wildcard_constraints: + tech_module = "biofuel|electrified-biofuel" script: "../scripts/biofuels/template_bio.py" diff --git a/scripts/biofuels/template_bio.py b/scripts/biofuels/template_bio.py index f2f17f23..23060277 100644 --- a/scripts/biofuels/template_bio.py +++ b/scripts/biofuels/template_bio.py @@ -10,5 +10,6 @@ snakemake.output[0], biofuel_cost=biofuel_cost, scaling_factors=snakemake.params.scaling_factors, + biofuel_efficiency=snakemake.params.biofuel_efficiency, locations=locations, ) diff --git a/templates/models/techs/supply/biofuel.yaml.jinja b/templates/models/techs/supply/biofuel.yaml.jinja index 15c5b834..1a2630ac 100644 --- a/templates/models/techs/supply/biofuel.yaml.jinja +++ b/templates/models/techs/supply/biofuel.yaml.jinja @@ -4,31 +4,13 @@ techs: name: Biofuel supply stream parent: supply_plus carrier: biofuel - constraints: - lifetime: 1 # arbritrarily chosen to avoid Calliope errors costs.monetary: om_prod: {{ biofuel_cost * scaling_factors.specific_costs }} # {{ (1 / scaling_factors.specific_costs) | unit("EUR/MWh") }} locations: {% for id, location in locations.iterrows() %} {{ id }}.techs.biofuel_supply: - {% endfor %} - -group_constraints: - {% for id, location in locations.iterrows() %} - biofuel_max_prod_{{ id }}: - locs: [{{ id }}] - techs: [biofuel_supply] - carrier_prod_max: - biofuel: {{ location.biofuel_potential_mwh_per_year * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} - {% endfor %} - -overrides: - biofuel_flow_limits: - locations: - {% for id, location in locations.iterrows() %} - {{ id }}.techs.biofuel_supply: - constraints: - resource: {{ location.biofuel_potential_mwh_per_year / 8760 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MW") }} - storage_cap_equals: {{ location.biofuel_potential_mwh_per_year / 2 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} (0.5x annual yield) # ASSUME < 1 for numerical range - {% endfor %} + constraints: + resource: {{ location.biofuel_potential_mwh_per_year / 8760 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} + storage_cap_equals: {{ location.biofuel_potential_mwh_per_year / 2 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} (0.5x annual yield) # ASSUME < 1 for numerical range + {% endfor %} \ No newline at end of file diff --git a/templates/models/techs/supply/electrified-biofuel.yaml.jinja b/templates/models/techs/supply/electrified-biofuel.yaml.jinja new file mode 100644 index 00000000..7ebd3b4c --- /dev/null +++ b/templates/models/techs/supply/electrified-biofuel.yaml.jinja @@ -0,0 +1,23 @@ +techs: + electrified_biofuel: # from [@JRC:2014] Table 48 Anaerobic digestion + essentials: + name: Biofuel-derived electricity + parent: supply_plus + carrier: electricity + constraints: + energy_eff: 1.0 # efficiency modelled within the input resource stream to avoid poor numerical scaling + lifetime: 20 + costs.monetary: + energy_cap: {{2300000 * scaling_factors.specific_costs}} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MW") }} + om_annual: {{2300000 * 0.041 * scaling_factors.specific_costs}} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MW") }} 4.1% of CAPEX + om_con: {{ (biofuel_cost / biofuel_efficiency + 3.1) * scaling_factors.specific_costs }} # {{ (1 / scaling_factors.specific_costs) | unit("EUR2013/MW") }} + om_prod: 0 # 3.1 (EUR2013/MWh) added to om_con because value is very small and causing poor numerical range + +locations: + {% for id, location in locations.iterrows() %} + {{ id }}.techs: + electrified_biofuel: + constraints: + resource: {{ location.biofuel_potential_mwh_per_year * biofuel_efficiency / 8760 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MW") }} + storage_cap_equals: {{ location.biofuel_potential_mwh_per_year * biofuel_efficiency / 2 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} (0.5x annual yield) # ASSUME < 1 for numerical range + {% endfor %} diff --git a/tests/model/test_model.py b/tests/model/test_model.py index 20c69cbc..013518cf 100644 --- a/tests/model/test_model.py +++ b/tests/model/test_model.py @@ -1,38 +1,48 @@ import pandas as pd import pytest -DEFAULT_TECHNOLOGIES = set([ - "battery", - "hydrogen", - "open_field_pv", - "wind_onshore_competing", - "wind_onshore_monopoly", - "roof_mounted_pv", - "wind_offshore", - "hydro_run_of_river", - "hydro_reservoir", - "pumped_hydro", - "biofuel_supply", - "electricity_from_biofuel", - "demand_elec", - "nuclear", -]) -DIRECTIONAL_PV = set([ - "roof_mounted_pv_s_flat", - "roof_mounted_pv_n", - "roof_mounted_pv_e_w", -]) -HEAT_TECHS = set([ - "biofuel_boiler", - "biofuel_tech_heat_to_demand", - "heat_pump", - "heat_pump_tech_heat_to_demand", - "electric_heater", - "electric_heater_tech_heat_to_demand", - "hp_heat_storage_small", - "electric_heater_heat_storage_small", - "biofuel_heat_storage_small", -]) +DEFAULT_TECHNOLOGIES = set( + [ + "battery", + "hydrogen", + "open_field_pv", + "wind_onshore_competing", + "wind_onshore_monopoly", + "roof_mounted_pv", + "wind_offshore", + "hydro_run_of_river", + "hydro_reservoir", + "pumped_hydro", + "demand_elec", + "nuclear", + ] +) +DIRECTIONAL_PV = set( + [ + "roof_mounted_pv_s_flat", + "roof_mounted_pv_n", + "roof_mounted_pv_e_w", + ] +) +HEAT_TECHS = set( + [ + "biofuel_boiler", + "biofuel_tech_heat_to_demand", + "heat_pump", + "heat_pump_tech_heat_to_demand", + "electric_heater", + "electric_heater_tech_heat_to_demand", + "hp_heat_storage_small", + "electric_heater_heat_storage_small", + "biofuel_heat_storage_small", + ] +) +BIOFUEL_TECHS = set( + [ + "biofuel_supply", + "electricity_from_biofuel", + ] +) # Only includes scenarios with non-default technology sets TECHNOLOGIES = { "connected_all_neighbours": DEFAULT_TECHNOLOGIES | set(["ac_transmission"]), @@ -41,11 +51,19 @@ - set(["roof_mounted_pv"]), "shed-load": DEFAULT_TECHNOLOGIES | set(["load_shedding"]), "all-overrides": ( - (DEFAULT_TECHNOLOGIES | DIRECTIONAL_PV | set(["load_shedding"]) | HEAT_TECHS) + ( + DEFAULT_TECHNOLOGIES + | DIRECTIONAL_PV + | set(["load_shedding"]) + | HEAT_TECHS + | BIOFUEL_TECHS + ) - set(["roof_mounted_pv"]) ), "electrified-heat": DEFAULT_TECHNOLOGIES | set(["historic_electrified_heat"]), + "electrified-biofuel": DEFAULT_TECHNOLOGIES | set(["electrified_biofuel"]), "heat": DEFAULT_TECHNOLOGIES | HEAT_TECHS, + "biofuel": DEFAULT_TECHNOLOGIES | BIOFUEL_TECHS, } OPTIONAL_LOCATIONAL_TECHNOLOGIES = ["nuclear"] diff --git a/tests/model/test_runner.py b/tests/model/test_runner.py index d2ff8447..880e6487 100644 --- a/tests/model/test_runner.py +++ b/tests/model/test_runner.py @@ -9,9 +9,7 @@ def run_test(snakemake): - with open( - os.path.join(snakemake.input.test_dir, "..", "resources", "test.yaml") - ) as f: + with open(os.path.join(snakemake.input.test_resources_dir, "test.yaml")) as f: test_config = yaml.safe_load(f) override_dict = test_config["test-model"]["overrides"][ diff --git a/tests/resources/test.yaml b/tests/resources/test.yaml index 6ddbb267..ada35c7d 100644 --- a/tests/resources/test.yaml +++ b/tests/resources/test.yaml @@ -18,18 +18,19 @@ test-model: alternative-cost: ["dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost"] shed-load: ["load-shedding"] keep-historic-transport: ["keep-historic-electricity-demand-from-road-transport"] - heat: ["heat_end_use"] + heat: ["heat_carrier"] electrified-heat: ["electrified_heat"] - biofuel-flow-limited: ["biofuel_flow_limits"] + biofuel: ["biofuel_carrier"] + electrified-biofuel: ["electrified_biofuel"] regional: default: ["connect_all_neighbours", "run_barrier_no_crossover"] all-overrides: ["connect_all_neighbours", "directional-rooftop-pv", "exclusive-energy-to-power-ratios", - "dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost", "freeze-hydro-supply-capacities", "freeze-hydro-storage-capacities", "load-shedding", "heat_end_use", "biofuel_flow_limits"] + "dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost", "freeze-hydro-supply-capacities", "freeze-hydro-storage-capacities", "load-shedding", "heat_carrier", "biofuel_carrier"] ehighways: default: ["connect_all_neighbours", "run_barrier_no_crossover"] all-overrides: ["connect_all_neighbours", "directional-rooftop-pv", "exclusive-energy-to-power-ratios", - "dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost", "freeze-hydro-supply-capacities", "freeze-hydro-storage-capacities", "load-shedding", "heat_end_use", "biofuel_flow_limits"] + "dea-renewable-cost-pv-open-field", "dea-renewable-cost-wind-onshore", "dea-renewable-cost-wind-offshore", "dea-renewable-cost-pv-roof-mounted", "schroeder-hydro-cost", "freeze-hydro-supply-capacities", "freeze-hydro-storage-capacities", "load-shedding", "heat_carrier", "biofuel_carrier"] overrides: continental: {} national: @@ -43,25 +44,39 @@ test-model: import: - "build/models/national/techs/demand/electrified-heat.yaml" - "build/models/national/techs/supply/historic-electrified-heat.yaml" - heat_end_use: + heat_carrier: import: - "build/models/national/techs/demand/heat.yaml" - "build/models/national/techs/storage/heat.yaml" - "build/models/national/techs/conversion/heat-from-electricity.yaml" - "build/models/national/techs/conversion/heat-from-biofuel.yaml" - "build/models/national/techs/supply/historic-electrified-heat.yaml" + electrified_biofuel: + import: + - "build/models/national/techs/supply/electrified-biofuel.yaml" + biofuel_carrier: + import: + - "build/models/national/techs/supply/biofuel.yaml" + - "build/models/national/techs/conversion/electricity-from-biofuel.yaml" regional: connect_all_neighbours: import: - 'build/models/regional/techs/transmission/electricity-linked-neighbours.yaml' - heat_end_use: + heat_carrier: import: - "build/models/regional/techs/demand/heat.yaml" - "build/models/regional/techs/storage/heat.yaml" - "build/models/regional/techs/conversion/heat-from-electricity.yaml" - "build/models/regional/techs/conversion/heat-from-biofuel.yaml" - "build/models/regional/techs/supply/historic-electrified-heat.yaml" + electrified_biofuel: + import: + - "build/models/regional/techs/supply/electrified-biofuel.yaml" + biofuel_carrier: + import: + - "build/models/regional/techs/supply/biofuel.yaml" + - "build/models/regional/techs/conversion/electricity-from-biofuel.yaml" run_barrier_no_crossover: run.solver_options: {Method: 2, Crossover: 0} @@ -69,13 +84,20 @@ test-model: connect_all_neighbours: import: - 'build/models/ehighways/techs/transmission/electricity-linked-neighbours.yaml' - heat_end_use: + heat_carrier: import: - "build/models/ehighways/techs/demand/heat.yaml" - "build/models/ehighways/techs/storage/heat.yaml" - "build/models/ehighways/techs/conversion/heat-from-electricity.yaml" - "build/models/ehighways/techs/conversion/heat-from-biofuel.yaml" - "build/models/ehighways/techs/supply/historic-electrified-heat.yaml" + electrified_biofuel: + import: + - "build/models/ehighways/techs/supply/electrified-biofuel.yaml" + biofuel_carrier: + import: + - "build/models/ehighways/techs/supply/biofuel.yaml" + - "build/models/ehighways/techs/conversion/electricity-from-biofuel.yaml" run_barrier_no_crossover: run.solver_options: {Method: 2, Crossover: 0} From 0cb62097f2f15c732a2fdea3651090d8bb750a8f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 09:35:39 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../models/techs/supply/biofuel.yaml.jinja | 2 +- tests/model/test_model.py | 76 +++++++++---------- 2 files changed, 35 insertions(+), 43 deletions(-) diff --git a/templates/models/techs/supply/biofuel.yaml.jinja b/templates/models/techs/supply/biofuel.yaml.jinja index 1a2630ac..8a8e353b 100644 --- a/templates/models/techs/supply/biofuel.yaml.jinja +++ b/templates/models/techs/supply/biofuel.yaml.jinja @@ -13,4 +13,4 @@ locations: constraints: resource: {{ location.biofuel_potential_mwh_per_year / 8760 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} storage_cap_equals: {{ location.biofuel_potential_mwh_per_year / 2 * scaling_factors.power }} # {{ (1 / scaling_factors.power) | unit("MWh") }} (0.5x annual yield) # ASSUME < 1 for numerical range - {% endfor %} \ No newline at end of file + {% endfor %} diff --git a/tests/model/test_model.py b/tests/model/test_model.py index 013518cf..802b1477 100644 --- a/tests/model/test_model.py +++ b/tests/model/test_model.py @@ -1,48 +1,40 @@ import pandas as pd import pytest -DEFAULT_TECHNOLOGIES = set( - [ - "battery", - "hydrogen", - "open_field_pv", - "wind_onshore_competing", - "wind_onshore_monopoly", - "roof_mounted_pv", - "wind_offshore", - "hydro_run_of_river", - "hydro_reservoir", - "pumped_hydro", - "demand_elec", - "nuclear", - ] -) -DIRECTIONAL_PV = set( - [ - "roof_mounted_pv_s_flat", - "roof_mounted_pv_n", - "roof_mounted_pv_e_w", - ] -) -HEAT_TECHS = set( - [ - "biofuel_boiler", - "biofuel_tech_heat_to_demand", - "heat_pump", - "heat_pump_tech_heat_to_demand", - "electric_heater", - "electric_heater_tech_heat_to_demand", - "hp_heat_storage_small", - "electric_heater_heat_storage_small", - "biofuel_heat_storage_small", - ] -) -BIOFUEL_TECHS = set( - [ - "biofuel_supply", - "electricity_from_biofuel", - ] -) +DEFAULT_TECHNOLOGIES = set([ + "battery", + "hydrogen", + "open_field_pv", + "wind_onshore_competing", + "wind_onshore_monopoly", + "roof_mounted_pv", + "wind_offshore", + "hydro_run_of_river", + "hydro_reservoir", + "pumped_hydro", + "demand_elec", + "nuclear", +]) +DIRECTIONAL_PV = set([ + "roof_mounted_pv_s_flat", + "roof_mounted_pv_n", + "roof_mounted_pv_e_w", +]) +HEAT_TECHS = set([ + "biofuel_boiler", + "biofuel_tech_heat_to_demand", + "heat_pump", + "heat_pump_tech_heat_to_demand", + "electric_heater", + "electric_heater_tech_heat_to_demand", + "hp_heat_storage_small", + "electric_heater_heat_storage_small", + "biofuel_heat_storage_small", +]) +BIOFUEL_TECHS = set([ + "biofuel_supply", + "electricity_from_biofuel", +]) # Only includes scenarios with non-default technology sets TECHNOLOGIES = { "connected_all_neighbours": DEFAULT_TECHNOLOGIES | set(["ac_transmission"]),