Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement realistic timeseries for transport demand #282

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Added (models)

* **ADD** fully-electrified road transportation (#270).
* **ADD** fully-electrified road transportation (#270), (#271).

* **ADD** nuclear power plant technology with capacity limits. Capacity limits can be equal to today or be bound by a minimum and maximum capacity to represent an available range in future. In either case, capacities are allocated at a subnational resolution based on linear scaling from current capacity geolocations, using the JRC power plant database (#78).

Expand Down
25 changes: 18 additions & 7 deletions config/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ data-sources:
eurostat-energy-balance: https://raw.githubusercontent.com/calliope-project/euro-calliope-datasets/feature-sector-coupling/eurostat/nrg_bal_c.tsv.gz # FIXME do not use cached data
swiss-energy-balance: https://www.bfe.admin.ch/bfe/en/home/versorgung/statistik-und-geodaten/energiestatistiken/gesamtenergiestatistik.exturl.html/aHR0cHM6Ly9wdWJkYi5iZmUuYWRtaW4uY2gvZGUvcHVibGljYX/Rpb24vZG93bmxvYWQvNzUxOQ==.html
swiss-industry-energy-balance: https://www.bfe.admin.ch/bfe/en/home/versorgung/statistik-und-geodaten/energiestatistiken/teilstatistiken.exturl.html/aHR0cHM6Ly9wdWJkYi5iZmUuYWRtaW4uY2gvZGUvcHVibGljYX/Rpb24vZG93bmxvYWQvODc4OA==.html
ev-data: https://zenodo.org/record/6579421/files/ramp-ev-consumption-profiles.csv.gz?download=1
root-directory: .
cluster-sync:
url: euler.ethz.ch
Expand Down Expand Up @@ -134,19 +135,29 @@ parameters:
coaches-and-buses: 3248
passenger-cars: 324
motorcycles: 200 # based on passenger car electrical efficiency scaled by relative diesel efficiency
names:
vehicle-type-names:
light-duty-vehicles: Light duty vehicles
heavy-duty-vehicles: Heavy duty vehicles
coaches-and-buses: Motor coaches, buses and trolley buses
passenger-cars: Passenger cars
motorcycles: Powered 2-wheelers
fill-missing-values:
ALB: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
BIH: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
MNE: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
SRB: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
NOR: ['SWE', 'DNK']
CHE: ['DEU', 'AUT', 'FRA', 'ITA']
annual-data:
ALB: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
BIH: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
MNE: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
MKD: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
SRB: ['BGR', 'HRV', 'HUN', 'ROU', 'GRC']
NOR: ['SWE', 'DNK']
CHE: ['DEU', 'AUT', 'FRA', 'ITA']
timeseries-data:
ALB: ['HRV']
MKD: ['HRV']
GRC: ['ROU']
BGR: ['ROU']
BIH: ['HRV', 'HUN']
MNE: ['HRV']
SRB: ['HUN']
entsoe-tyndp:
scenario: National Trends
grid: Reference
Expand Down
41 changes: 31 additions & 10 deletions config/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ properties:
type: string
pattern: ^(https?|http?):\/\/.+
description: Web address of Swiss industry energy balance data.
ev-data:
type: string
pattern: ^(https?|http?):\/\/.+
description: Web address of electric vehicle data.
root-directory:
type: string
description: Path to the root directory of euro-calliope containing scripts and template folders.
Expand Down Expand Up @@ -282,7 +286,7 @@ properties:
motorcycles:
type: number
description: Motorcylces.
names:
vehicle-type-names:
type: object
description: Names of vehicle types in JRC-IDEES.
additionalProperties: false
Expand All @@ -303,17 +307,34 @@ properties:
type: string
description: JRC-IDEES name of motorcycles.
fill-missing-values:
description: Fill missing values in annual transport demand.
type: object
description: Dictionaries to fill missing values
additionalProperties: false
patternProperties:
"^[A-Z][A-Z][A-Z]$":
type: array
description: Country to fill missing values for.
items:
type: string
pattern: ^[A-Z][A-Z][A-Z]$
description: Country to fill missing values from.
properties:
annual-data:
description: Fill missing values in annual transport demand.
type: object
additionalProperties: false
patternProperties:
"^[A-Z][A-Z][A-Z]$":
type: array
description: Country to fill missing values for.
items:
type: string
pattern: ^[A-Z][A-Z][A-Z]$
description: Country to fill missing values from.
timeseries-data:
description: Fill missing countries in timeseries profile.
type: object
additionalProperties: false
patternProperties:
"^[A-Z][A-Z][A-Z]$":
type: array
description: Country to fill missing values for.
items:
type: string
pattern: ^[A-Z][A-Z][A-Z]$
description: Country to fill missing values from.
entsoe-tyndp:
type: object
description: Parameters to define scenario choice for data accessed from the ENTSO-E ten-year network development plan 2020. For more information, see https://2020.entsos-tyndp-scenarios.eu/
Expand Down
6 changes: 5 additions & 1 deletion rules/data.smk
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ rule download_ch_energy_balances:
params:
url = lambda wildcards: config["data-sources"][f"swiss-{wildcards.dataset}"]
output: protected("data/automatic/ch-{dataset}.xlsx")
conda: "../envs/shell.yaml"
wildcard_constraints:
dataset = "((energy-balance)|(industry-energy-balance))"
localrule: True
Expand All @@ -21,6 +22,7 @@ rule download_eurostat_annual_energy_balances:
message: "Download Eurostat Annual Energy Balances from euro-calliope datasets"
params:
url = config["data-sources"]["eurostat-energy-balance"]
conda: "../envs/shell.yaml"
output: protected("data/automatic/eurostat-energy-balance.tsv.gz")
localrule: True
shell: "curl -sLo {output} {params.url}"
Expand Down Expand Up @@ -81,7 +83,9 @@ rule jrc_idees_transport_processed:
country_code=EU28
)
output: "build/data/jrc-idees/transport/processed-{dataset}.csv"
params:
vehicle_type_names = config["parameters"]["transport"]["vehicle-type-names"],
wildcard_constraints:
dataset = "((road-energy)|(road-distance)|(road-vehicles))"
dataset = "road-energy|road-distance|road-vehicles"
conda: "../envs/default.yaml"
script: "../scripts/transport/jrc_idees.py"
38 changes: 24 additions & 14 deletions rules/transport.smk
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
"""Rules to process transport sector data."""

rule download_transport_timeseries:
message: "Get EV data from RAMP"
params:
url = config["data-sources"]["ev-data"]
conda: "../envs/shell.yaml"
output: protected("data/automatic/ramp-ev-consumption-profiles.csv.gz")
localrule: True
shell: "curl -sLo {output} {params.url}"

rule annual_transport_demand:
message: "Calculate future transport energy demand based on JRC IDEES"
Expand All @@ -9,7 +17,7 @@ rule annual_transport_demand:
jrc_road_distance = "build/data/jrc-idees/transport/processed-road-distance.csv",
jrc_road_vehicles = "build/data/jrc-idees/transport/processed-road-vehicles.csv",
params:
fill_missing_values = config["parameters"]["transport"]["fill-missing-values"],
fill_missing_values = config["parameters"]["transport"]["fill-missing-values"]["annual-data"],
efficiency_quantile = config["parameters"]["transport"]["future-vehicle-efficiency-percentile"]
conda: "../envs/default.yaml"
output:
Expand All @@ -23,35 +31,39 @@ rule annual_transport_demand:
rule create_road_transport_timeseries:
message: "Create timeseries for road transport demand"
input:
data = "build/data/transport/annual-road-transport-distance-demand.csv",
annual_data = "build/data/transport/annual-road-transport-distance-demand.csv",
timeseries = "data/automatic/ramp-ev-consumption-profiles.csv.gz"
params:
first_year = config["scope"]["temporal"]["first-year"],
final_year = config["scope"]["temporal"]["final-year"],
power_scaling_factor = config["scaling-factors"]["power"],
conversion_factor = lambda wildcards: config["parameters"]["transport"]["road-transport-conversion-factors"][wildcards.type],
type_name = lambda wildcards: config["parameters"]["transport"]["names"][wildcards.type],
historic = False
conversion_factor = lambda wildcards: config["parameters"]["transport"]["road-transport-conversion-factors"][wildcards.vehicle_type],
historic = False,
countries = config["scope"]["spatial"]["countries"],
country_neighbour_dict= config["parameters"]["transport"]["fill-missing-values"]["timeseries-data"],
conda: "../envs/default.yaml"
wildcard_constraints:
type = "light-duty-vehicles|heavy-duty-vehicles|coaches-and-buses|passenger-cars|motorcycles"
vehicle_type = "light-duty-vehicles|heavy-duty-vehicles|coaches-and-buses|passenger-cars|motorcycles"
output:
main = "build/data/transport/timeseries/timeseries-{type}.csv",
main = "build/data/transport/timeseries/timeseries-{vehicle_type}.csv",
script: "../scripts/transport/road_transport_timeseries.py"


use rule create_road_transport_timeseries as create_road_transport_timeseries_historic_electrification with:
message: "Create timeseries for historic electrified road transport demand"
input:
data = "build/data/transport/annual-road-transport-historic-electrification.csv"
annual_data = "build/data/transport/annual-road-transport-historic-electrification.csv",
timeseries = "data/automatic/ramp-ev-consumption-profiles.csv.gz",
params:
first_year = config["scope"]["temporal"]["first-year"],
final_year = config["scope"]["temporal"]["final-year"],
power_scaling_factor = config["scaling-factors"]["power"],
conversion_factor = lambda wildcards: config["parameters"]["transport"]["road-transport-conversion-factors"][wildcards.type],
type_name = lambda wildcards: config["parameters"]["transport"]["names"][wildcards.type],
historic = True
conversion_factor = lambda wildcards: config["parameters"]["transport"]["road-transport-conversion-factors"][wildcards.vehicle_type],
historic = True,
countries = config["scope"]["spatial"]["countries"],
country_neighbour_dict= config["parameters"]["transport"]["fill-missing-values"]["timeseries-data"],
output:
"build/data/transport/timeseries/timeseries-{type}-historic-electrification.csv"
"build/data/transport/timeseries/timeseries-{vehicle_type}-historic-electrification.csv"


rule aggregate_timeseries: # TODO consider merge with other rules, as this is tiny atm
Expand All @@ -65,8 +77,6 @@ rule aggregate_timeseries: # TODO consider merge with other rules, as this is ti
"build/data/transport/timeseries/timeseries-motorcycles.csv"),
locations = "build/data/regional/units.csv",
populations = "build/data/regional/population.csv"
params:
countries = config["scope"]["spatial"]["countries"]
conda: "../envs/default.yaml"
output:
"build/models/{resolution}/timeseries/demand/electrified-road-transport.csv",
Expand Down
18 changes: 8 additions & 10 deletions scripts/transport/aggregate_timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@
import pycountry


def create_continental_timeseries(paths_to_input: list[str], country_codes: list[str]) -> pd.DataFrame:
ts = create_national_timeseries(paths_to_input, country_codes)
def create_continental_timeseries(paths_to_input: list[str]) -> pd.DataFrame:
ts = create_national_timeseries(paths_to_input)
return ts.sum(axis=1).rename("EUR")


def create_national_timeseries(paths_to_input: list[str], country_codes: list[str]) -> pd.DataFrame:
def create_national_timeseries(paths_to_input: list[str]) -> pd.DataFrame:
all_ts = [
pd.read_csv(path, index_col='utc-timestamp', parse_dates=True)
for path in paths_to_input
]
return sum(all_ts).loc[:, country_codes]
return sum(all_ts)


def create_regional_timeseries(
paths_to_input: list[str],
country_codes: list[str],
region_country_mapping: str,
population: str,
) -> pd.DataFrame:
Expand All @@ -30,7 +29,7 @@ def create_regional_timeseries(
ASSUME all road transport is subnationally distributed in proportion to population.
"""

df_national = create_national_timeseries(paths_to_input, country_codes)
df_national = create_national_timeseries(paths_to_input)

region_country_mapping = (
pd.read_csv(region_country_mapping, index_col=0)
Expand Down Expand Up @@ -66,17 +65,16 @@ def create_regional_timeseries(
if __name__ == "__main__":
resolution=snakemake.wildcards.resolution
paths_to_input=snakemake.input.time_series
country_codes=[pycountry.countries.lookup(c).alpha_3 for c in snakemake.params.countries]
path_to_output=snakemake.output[0]
path_to_locations=snakemake.input.locations
path_to_populations=snakemake.input.populations

if resolution == "continental":
ts = create_continental_timeseries(paths_to_input, country_codes)
ts = create_continental_timeseries(paths_to_input)
elif resolution == "national":
ts = create_national_timeseries(paths_to_input, country_codes)
ts = create_national_timeseries(paths_to_input)
elif resolution == "regional":
ts = create_regional_timeseries(paths_to_input, country_codes, path_to_locations, path_to_populations)
ts = create_regional_timeseries(paths_to_input, path_to_locations, path_to_populations)
else:
raise ValueError("Input resolution not recognised.")

Expand Down
7 changes: 4 additions & 3 deletions scripts/transport/jrc_idees.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
}


def process_jrc_transport_data(paths_to_data: list[str], dataset: object, out_path: str):
def process_jrc_transport_data(paths_to_data: list[str], dataset: object, out_path: str, vehicle_type_names: dict[str, str]) -> None:
paths_to_data = [Path(p) for p in paths_to_data]
processed_data = pd.concat([
read_transport_excel(path, **DATASET_PARAMS[dataset])
Expand All @@ -54,10 +54,10 @@ def process_jrc_transport_data(paths_to_data: list[str], dataset: object, out_pa
.set_index("country_code", append=True)
.stack('year')
.rename('value')
.rename(index = vehicle_type_names, level = 'vehicle_type')
.to_csv(out_path)
)

processed_data


def read_transport_excel(path: Path,
Expand Down Expand Up @@ -176,5 +176,6 @@ def remove_of_which(df: pd.DataFrame, main_carrier: str, of_which_carrier: str)
process_jrc_transport_data(
paths_to_data=snakemake.input.data,
dataset=snakemake.wildcards.dataset,
out_path=snakemake.output[0]
out_path=snakemake.output[0],
vehicle_type_names={v:k for k,v in snakemake.params.vehicle_type_names.items()},
)
Loading
Loading