Skip to content

Commit

Permalink
Introduce electricity-only biofuel tech; remove annual biofuel supply…
Browse files Browse the repository at this point in the history
… limit
  • Loading branch information
brynpickering committed Aug 2, 2024
1 parent 2fb31df commit 7e32a08
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 79 deletions.
14 changes: 11 additions & 3 deletions Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -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"]
Expand Down Expand Up @@ -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(
Expand Down
20 changes: 12 additions & 8 deletions docs/model/customisation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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](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](<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](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.
Expand Down Expand Up @@ -104,19 +104,23 @@ 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"

=== "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.
**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"

Expand Down Expand Up @@ -243,7 +247,7 @@ Here, we describe each module in terms of the technologies they contain (`callio

## 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](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
Expand All @@ -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](<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](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:
Expand Down
9 changes: 6 additions & 3 deletions rules/biofuels.smk
Original file line number Diff line number Diff line change
Expand Up @@ -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"
1 change: 1 addition & 0 deletions scripts/biofuels/template_bio.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
26 changes: 4 additions & 22 deletions templates/models/techs/supply/biofuel.yaml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
23 changes: 23 additions & 0 deletions templates/models/techs/supply/electrified-biofuel.yaml.jinja
Original file line number Diff line number Diff line change
@@ -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 %}
84 changes: 51 additions & 33 deletions tests/model/test_model.py
Original file line number Diff line number Diff line change
@@ -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"]),
Expand All @@ -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"]

Expand Down
4 changes: 1 addition & 3 deletions tests/model/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"][
Expand Down
Loading

0 comments on commit 7e32a08

Please sign in to comment.