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

Add endogenous transport option #734

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft

Conversation

s8au
Copy link

@s8au s8au commented Sep 6, 2023

  • select with endogenous_transport in config file
  • it is possible to select transport shares for part of the fuel types
  • creates ICE-vehicle-fleet for baseyear if myopic is selected and no transport share is defined
  • adds constraints for endogenously chosen EVs in solve_network

relies on PR in technology-data repository: BertoGBG:master
Closes #507.

Changes proposed in this Pull Request

Checklist

  • I tested my contribution locally and it seems to work fine.
  • Code and workflow changes are sufficiently documented.
  • Changed dependencies are added to envs/environment.yaml.
  • Changes in configuration options are added in all of config.default.yaml.
  • Changes in configuration options are also documented in doc/configtables/*.csv.
  • A release note doc/release_notes.rst is added.

- select with endogenous_transport in config file
- it is  possible to select transport shares for part of the fuel types
- creates ICE-fleet for baseyear if myopic is selected and no transport share is defined
- adds constraints for endogenously chosen EVs in solve_network
@martavp martavp self-requested a review September 7, 2023 08:52
Copy link
Member

@martavp martavp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks @s8au , this looks great!

I still need to run the new code and I'll do it soon, but in the meantime see some suggestions based on reading the changes.

I think you can check "Changed dependencies are added to envs/environment.yaml." since no changes are required.

Try also to add a release note to doc/release_notes.rst

config/config.default.yaml Outdated Show resolved Hide resolved
config/config.default.yaml Outdated Show resolved Hide resolved
scripts/prepare_sector_network.py Outdated Show resolved Hide resolved
scripts/prepare_sector_network.py Outdated Show resolved Hide resolved
scripts/prepare_sector_network.py Outdated Show resolved Hide resolved
scripts/prepare_sector_network.py Outdated Show resolved Hide resolved
rhs = n.links.p_nom[gas_pipes_i].rename_axis("Link-ext")

n.model.add_constraints(lhs == rhs, name="Link-pipe_retrofit")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do lines 572 to 668 appear as new additions? They are not related to the endogenous transport

scripts/solve_network.py Show resolved Hide resolved
scripts/add_existing_baseyear.py Outdated Show resolved Hide resolved
scripts/add_existing_baseyear.py Outdated Show resolved Hide resolved
@martavp
Copy link
Member

martavp commented Sep 18, 2023

The current definition of p_nom for the link "land transport EV" needs to be changed.
Currently, it is based on the maximum value of the transport demand

n.links.loc[
(n.links.carrier == "land transport EV")
& (n.links.bus1.str.contains(place)),
"p_nom",
] = electric_share * max(p_nom[place])

This represents the minimum number of cars needed to supply that demand, but in reality, cars are most of the time parked and we should use the total number of cars, as it was done before

p_nom = number_cars * options.get("bev_charge_rate", 0.011) * electric_share

This is important to avoid problems when calculating the cost associated with cars.

@martavp
Copy link
Member

martavp commented Sep 18, 2023

There is a bug when adding the storage for EVs exogenous. The following code

n.stores.loc[
(n.stores.carrier == "EV battery storage")
& (n.links.bus1.str.contains(place)),
"e_nom",
] = (
number_cars[place]
* options.get("bev_energy", 0.05)
* options["bev_availability"]
* electric_share
)

Should be:

n.stores.loc[
(n.stores.carrier == "EV battery storage")
& (n.stores.bus.str.contains(place)),
"e_nom",
] = (
number_cars[place]
* options.get("bev_energy", 0.05)
* options["bev_availability"]
* electric_share
)

@martavp
Copy link
Member

martavp commented Sep 18, 2023

The constraint relating the BEV charge rate and the storage capacity of the batteries, needs to take into account that in the config file it can be selected that only a percentage of the BEV provides flexibility

lhs2 = (
n.model.variables["Store-e_nom"].loc[ev_store]
/ n.config["sector"]["bev_energy"]
)

should be:

lhs2 = (
n.model.variables["Store-e_nom"].loc[ev_store]
/ (n.config["sector"]["bev_energy"]*n.config["sector"]["bev_availability"])
)

@martavp
Copy link
Member

martavp commented Sep 18, 2023

EDITED on 20/09/2023 at 8:35

I found a bug in the definition of p_max_pu and p_min_pu of the link "land transport oil"
The following

n.links_t.p_max_pu[
n.links[
(n.links.carrier == "land transport oil")
& (n.links.bus1.str.contains(place))
].index.values[0]
] = ((p_nom[place]) / max(p_nom[place]) * ice_efficiency / p_set[place])

should be:

n.links_t.p_max_pu[
n.links[
(n.links.carrier == "land transport oil")
& (n.links.bus1.str.contains(place))
].index.values[0]
] = p_nom[place]/ (max(p_nom[place]* ice_efficiency)

otherwise, since p_nom and p_set are defined to be equal on a previous line, p_max_pu becomes a constant number and the flow of energy through that link is not able to supply the land transport demand.

The definition of the efficiency in the link should also be modified:

the current definition:

efficiency=1.0
/ options["transport_internal_combustion_efficiency"]
* p_set[nodes],

should be:

efficiency=options["transport_internal_combustion_efficiency"]

IMPORTANT: This should be changed for p_max_pu and p_min pu of both links 'land transport oil' and 'land transport fuel cell'

Fixed efficiencies, captial costs, p_max_pu/p_min_pu of exogenous land transport, removed redundant variable
Fixed EV constraint to only select Links with p_nom_extendable=True
@s8au s8au force-pushed the endogenous-transport branch from 516aec3 to 239bee9 Compare October 5, 2023 09:59
@lisazeyen
Copy link
Contributor

@martavp @s8au thanks a lot for all your work on this!

Tom and I just had a look at the code since we would like to have the endogenous land transport in the main repository quite soon. Two main points came to our mind:

1.) As a first step it made make sense to fix the number of cars and not allow switching between cars by assuming a p_min_pu=p_max_pu=profile transport demand for all three links (EV, H2 Fuel Cell and ICEs). This should also speed up the solving.
2.) Further the transport demand profile p_set should not be shifted since this was only done for smoothing the EV charging. We can first just leave out the shifting completely when defining p_set . In a second step one could think about adding a time-dependent efficiency to the EV links to account for different charging times.

@s8au would you have time to include it in the next two weeks or should we give it a try? Did you have time to check also for newer cost assumptions for EVs?

@s8au
Copy link
Author

s8au commented Oct 27, 2023

@lisazeyen

Tom and I just had a look at the code since we would like to have the endogenous land transport in the main repository quite soon. Two main points came to our mind:

1.) As a first step it made make sense to fix the number of cars and not allow switching between cars by assuming a p_min_pu=p_max_pu=profile transport demand for all three links (EV, H2 Fuel Cell and ICEs). This should also speed up the solving. 2.) Further the transport demand profile p_set should not be shifted since this was only done for smoothing the EV charging. We can first just leave out the shifting completely when defining p_set . In a second step one could think about adding a time-dependent efficiency to the EV links to account for different charging times.

@s8au would you have time to include it in the next two weeks or should we give it a try? Did you have time to check also for newer cost assumptions for EVs?

Thank you both for looking over it and your suggestions. I will try to include the p_min_pu/p_max_pu constraint and remove the smoothing next week.
I looked a bit around but so far I only found a datasheet for commercial freight and passenger transport . Did you have something specific in mind?

@lisazeyen
Copy link
Contributor

@lisazeyen

Tom and I just had a look at the code since we would like to have the endogenous land transport in the main repository quite soon. Two main points came to our mind:
1.) As a first step it made make sense to fix the number of cars and not allow switching between cars by assuming a p_min_pu=p_max_pu=profile transport demand for all three links (EV, H2 Fuel Cell and ICEs). This should also speed up the solving. 2.) Further the transport demand profile p_set should not be shifted since this was only done for smoothing the EV charging. We can first just leave out the shifting completely when defining p_set . In a second step one could think about adding a time-dependent efficiency to the EV links to account for different charging times.
@s8au would you have time to include it in the next two weeks or should we give it a try? Did you have time to check also for newer cost assumptions for EVs?

Thank you both for looking over it and your suggestions. I will try to include the p_min_pu/p_max_pu constraint and remove the smoothing next week. I looked a bit around but so far I only found a datasheet for commercial freight and passenger transport . Did you have something specific in mind?

Great! I think the technology data is a good source for the heavy duty vehicles. For the light vehicles I guess there is a Bloomberg report for at least EVs. But I think every source for light vehicles which is a bit more up to date (like after 2020) should be fine!

s8au added 2 commits November 2, 2023 09:34
…ization

- remove smoothing of land transport demand
- merge with more recent pypsa-eur version
- fix some efficiencies in prepare_sector_network for land transport
- make minor changes for better readability
- fix land transport efficiency in base year
@martavp
Copy link
Member

martavp commented Nov 2, 2023

Great! I think the technology data is a good source for the heavy duty vehicles. For the light vehicles I guess there is a Bloomberg report for at least EVs. But I think every source for light vehicles which is a bit more up to date (like after 2020) should be fine!

BNEF include cost assumption for trucks in Figure 4 in
https://assets.bbhub.io/professional/sites/24/2431510_BNEFElectricVehicleOutlook2023_ExecSummary.pdf
(here, we agree that using DEA is better)

and cost assumptions for EVs until 2030 in Figure 28. https://www.transportenvironment.org/discover/hitting-the-ev-inflection-point/
(because the data only covers until 2030 and the report includes a disclaimer saying that we should not use the data, I suggest we keep using Fraunhofer data), unless @s8au and I have missed some additional BNEF database?

But you are totally right @lisazeyen that is too pessimistic about EV costs. See below the cost comparison for EVs between our current assumptions and BNEF (red dots). We will implement a cost sensitivity analysis soon so that we can learn about the impacts.

BNEF_vs_Fraunhofer

@lisazeyen
Copy link
Contributor

Great! I think the technology data is a good source for the heavy duty vehicles. For the light vehicles I guess there is a Bloomberg report for at least EVs. But I think every source for light vehicles which is a bit more up to date (like after 2020) should be fine!

BNEF include cost assumption for trucks in Figure 4 in https://assets.bbhub.io/professional/sites/24/2431510_BNEFElectricVehicleOutlook2023_ExecSummary.pdf (here, we agree that using DEA is better)

and cost assumptions for EVs until 2030 in Figure 28. https://www.transportenvironment.org/discover/hitting-the-ev-inflection-point/ (because the data only covers until 2030 and the report includes a disclaimer saying that we should not use the data, I suggest we keep using Fraunhofer data), unless @s8au and I have missed some additional BNEF database?

But you are totally right @lisazeyen that is too pessimistic about EV costs. See below the cost comparison for EVs between our current assumptions and BNEF (red dots). We will implement a cost sensitivity analysis soon so that we can learn about the impacts.

BNEF_vs_Fraunhofer

Yes, I guess a cost sensitivity is the best way!

@s8au
Copy link
Author

s8au commented Nov 3, 2023

The current definition of p_nom for the link "land transport EV" needs to be changed. Currently, it is based on the maximum value of the transport demand

n.links.loc[
(n.links.carrier == "land transport EV")
& (n.links.bus1.str.contains(place)),
"p_nom",
] = electric_share * max(p_nom[place])

This represents the minimum number of cars needed to supply that demand, but in reality, cars are most of the time parked and we should use the total number of cars, as it was done before

p_nom = number_cars * options.get("bev_charge_rate", 0.011) * electric_share

This is important to avoid problems when calculating the cost associated with cars.

I updated it for the exogenous land transport EV
I also indicate with the 🚀 that I fixed the problems in the other comments

# set p_nom to fulfill electric share, capital cost to 0
n.links.loc[n.links.carrier=='land transport EV', "p_nom_extendable"] = False

p_nom = number_cars * options.get("bev_charge_rate", 0.011) * electric_share #electric_share * p_set.max(axis=0)
Copy link
Member

@martavp martavp Nov 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This link represents the "capacity" of EVs to supply transport demand.
However, the current definition represents the capacity for charging the cars
since options.get("bev_charge_rate", 0.011) is the capacity to charge a car.
@s8au can you change this? (similarly to the exogenous capacity definition for H2 and ICE cars)?

@martavp
Copy link
Member

martavp commented Nov 8, 2023

Hi @s8au ,

I mentioned to @lisazeyen yesterday that she can start reviewing this.
To facilitate the review, Can you update the PR to the last version of the master branch and check that everything run smoothly? This facilitates the revision (currently endogenous-transport-related and non-related changes are shown and make the revision cumbersome)

There are two additional small issues:

[ ] 1. I think the link representing the "capacity" of EVs to supply transport demand is not correct.
The current definition represents the capacity for charging the cars
since options.get("bev_charge_rate", 0.011) is the capacity to charge a car.
Can you please correct this? (similarly to the exogenous capacity definition for H2 and ICE cars)?

[] 2. I understand how you estimated the capacity of the "land transport oil." In essence, you calculate the minimum capacity needed to supply the demand for land transport today and then decommission a percentage of it in every future planning horizon. The approach is fine for me.

However, in reality, we have many more cars than "needed". Can you calculate the ratio between the existing number of cars and the cars that you calculate that are needed to supply the demand and write it as a log message?
Something like:
"Although, existing ICE vehicles in 2020 account for XXXX, the model assumes only the minimum number of ICE vehicles to supply the transport demand which represents XXX"

- Merge branch 'master' of https://github.com/PyPSA/pypsa-eur into endogenous-transport
- fix EV land transport capacity
- fix brownfield for exogenous land transport bug
- add logger information for baseyear in endogenous land transport

Conflicts:
	.pre-commit-config.yaml
	doc/configtables/sector.csv
	doc/release_notes.rst
	scripts/add_existing_baseyear.py
	scripts/prepare_sector_network.py
	scripts/solve_network.py
@s8au
Copy link
Author

s8au commented Nov 13, 2023

@martavp @lisazeyen I updated the branch with the master branch and I ran a test case for the exogenous and the endogenous transport each (resulting in again being behind by 4 commits). Since these are only minor changes I would prefer to update them with additional changes to my code.

@lisazeyen lisazeyen mentioned this pull request Nov 16, 2023
6 tasks
p_set = (
(
transport[nodes]
+ cycling_shift(transport[nodes], 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this shift of demand was done to reflect that EV charging is smoothed out and not done exactly when the car is driven but it is not necessary e.g. for internal combustion cars. I would suggest in a first step to leave the cycling_shift out of the transport demand. In a second step one could think about adding a time-dependent efficiency to the BEV link to account for different charging times

lifetime = costs.at['Battery electric (passenger cars)', 'lifetime'],
#lifetime?
)

n.madd(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest adding a p_max_pu and p_min_pu which are equal and both follow the demand profile from p_set. The same should be done for the links of the ICE and the H2 fuel cell cars to keep the share constant. I know we discussed that people are also switching cars (e.g. they have an EV for short distances and an ICE for longer distances) but as a first step I think that is the easiest way and might speed up the optimisation as well since there are less decision variables.

carrier="BEV charger",
p_max_pu=avail_profile[nodes],
efficiency=options.get("bev_charge_efficiency", 0.9),
efficiency=1, # instead of options.get("bev_charge_efficiency", 0.9) -> efficiency already accounted for in build_transport_demand
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build_transport_demand script did not change in your PR, so shouldn't it stay as before efficiency=options.get("bev_charge_efficiency", 0.9) ?

- calcualte transport demand in MWh of propulsion
- calculate temperature dependance for land transport in
  vehicle efficiencies in prepare_sector_network
- recalculate p_min_pu,p_max_pu after temporal_resolution
  is set in prepare_sector_network
- update p_min_pu,p_max_pu in add_brownfield
- update to newer pypsa-eur version
- Add clean up suggestions and comments from lisazeyen
Conflicts:
	config/config.default.yaml
	doc/configtables/sector.csv
	doc/release_notes.rst
	scripts/prepare_sector_network.py
	scripts/solve_network.py
@s8au
Copy link
Author

s8au commented Feb 6, 2024

I updated the PR with the newest version, so we can discuss the changes, but I still have to check if the added code in the add_brownfield works because the Gurobi license on our cluster has expired.
@lisazeyen
I had to move the calculation of the profiles for the p_min_pu/p_max_pu constraints after the links have their new temporal resolution because the efficiencies are now time dependent with the temperature corrections. Due to this, taking the mean of the profiles gives wrong values.
Gurobi has some trouble with setting these profiles and then solving the land transport when the links p_nom_extendable is set to True. I am currently trying to only constrain two out of three links directly (which works fine) and then constrain the third link for the myopic pathway in the add_brownfield script (WIP).
The exogenous pathway should work fine, it runs - depending on the CO2 budget - until 2040/2050 before becoming infeasible. But that was also what I experienced with the unmodified code from the repository.

@s8au
Copy link
Author

s8au commented Feb 15, 2024

@martavp I added a commit with the fixed bug, so it should run now

- add_brownfield: Delete land transport links etc if planning horizon contains defined shares
- prepare_sector_network: Relax constraint for BEV_charger in exogenous transport to
  prevent infeasibilites and match endogenous assumption
@lisazeyen
Copy link
Contributor

lisazeyen commented Mar 4, 2024

@s8au @martavp I am currently planning to integrate as the land transport demand as one single node and then the links for the different car types. I made a suggestion, which you can see the PR here #957.

Temperature influence on efficiency is moved from efficiency to p_min_pu constraint in the land transport
s8au added 7 commits April 10, 2024 08:41
- Land transport demand corrected by temperature and in MWh units
- efficiencies for land transport links include temperature dependencies
Removed land transport constraints except storage constraints,
v2g and bev charger are constrained by p_nom_max
constraints that includes n.stores optimize all stores to the same value,
split EV storage constraint in individual constraints to avoid that
e_nom_max of EV battery depends on previously installed capacity
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ⚙️ In progress
Development

Successfully merging this pull request may close these issues.

Allow endogenous optimisation of land transport and shipping
3 participants