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

Endogenous transport #960

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

Endogenous transport #960

wants to merge 64 commits into from

Conversation

lisazeyen
Copy link
Contributor

@lisazeyen lisazeyen commented Mar 6, 2024

Changes proposed in this Pull Request

endogenise land transport

TODO

  • extra constraint for capcaities of BEV charger, V2G, BEV DSM
  • research existing car ages
  • check investment costs of cars (take average km per year from Eurostat)

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.

lisazeyen and others added 30 commits March 1, 2024 11:32
@koen-vg
Copy link
Contributor

koen-vg commented Apr 15, 2024

I was experimenting a little bit with this branch since it would be a nice addition to a project I'm working on right now. First of all, thanks a bunch for this work!

I sometimes ran into feasibility issues, which after having a look at an irreducible set of constraints I tracked down to fixed profiles of the transport links in this PR. Have a look at the IIS:

IIS computed: 14 constraints and 0 bounds
IIS runtime: 16087.55 seconds (19223.57 work units)
INFO:__main__:Labels:
[1151, 1221, 1256, 63086, 63191, 63226, 64648, 64753, 64788, 157873, 157978, 158013, 206827, 220187]
Gurobi Optimizer version 11.0.1 build v11.0.1rc0 (linux64 - "Debian GNU/Linux 11 (bullseye)")

CPU model: AMD EPYC Processor, instruction set [SSE2|AVX|AVX2]
Thread count: 48 physical cores, 48 logical processors, using up to 1 threads


IIS computed: 14 constraints and 0 bounds
IIS runtime: 0.01 seconds (0.00 work units)
Link-ext-p_nom-lower[EE6 0 BEV charger-2045]: +1 Link-p_nom[EE6 0 BEV charger-2045] _ -0
Link-ext-p_nom-lower[EE6 0 land transport fuel cell-2045]: +1 Link-p_nom[EE6 0 land transport fuel cell-2045] _ -0
Link-ext-p_nom-lower[EE6 0 land transport ICE-2045]: +1 Link-p_nom[EE6 0 land transport ICE-2045] _ -0
Link-fix-p-lower[2020-07-30 09:00:00, EE6 0 land transport EV-2040]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport EV-2040] _ 1653.20728134
Link-fix-p-lower[2020-07-30 09:00:00, EE6 0 land transport fuel cell-2040]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport fuel cell-2040] _ 0.00104664401445
Link-fix-p-lower[2020-07-30 09:00:00, EE6 0 land transport ICE-2040]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport ICE-2040] _ 0.000264252029532
Link-fix-p-lower[2020-07-30 09:00:00, EE6 0 land transport EV-2035]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport EV-2035] _ 291.742540817
Link-fix-p-lower[2020-07-30 09:00:00, EE6 0 land transport fuel cell-2035]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport fuel cell-2035] _ 2.16162245605e-05
Link-fix-p-lower[2020-07-30 09:00:00, EE6 0 land transport ICE-2035]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport ICE-2035] _ 7.927397329e-06
Link-ext-p-lower[2020-07-30 09:00:00, EE6 0 land transport EV-2045]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport EV-2045] - 0.8798 Link-p_nom[EE6 0 land transport EV-2045] _ -0
Link-ext-p-lower[2020-07-30 09:00:00, EE6 0 land transport fuel cell-2045]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport fuel cell-2045] - 0.9467 Link-p_nom[EE6 0 land transport fuel cell-2045] _ 
-0
Link-ext-p-lower[2020-07-30 09:00:00, EE6 0 land transport ICE-2045]: +1 Link-p[2020-07-30 09:00:00, EE6 0 land transport ICE-2045] - 0.9467 Link-p_nom[EE6 0 land transport ICE-2045] _ -0
Bus-nodal_balance[EE6 0 land transport, 2020-07-30 09:00:00]: +0.6794 Link-p[2020-07-30 09:00:00, EE6 0 land transport EV-2045] + 0.4949 Link-p[2020-07-30 09:00:00, EE6 0 land transport fuel cell-2045] +
 0.297 Link-p[2020-07-30 09:00:00, EE6 0 land transport ICE-2045] ... +0.6794 Link-p[2020-07-30 09:00:00, EE6 0 land transport EV-2035] + 0.4949 Link-p[2020-07-30 09:00:00, EE6 0 land transport fuel cell
-2035] + 0.297 Link-p[2020-07-30 09:00:00, EE6 0 land transport ICE-2035] = 1321.31860446
p_nom-EV-BEV[EE6 0 land transport EV-2045]: +1 Link-p_nom[EE6 0 land transport EV-2045] - 0.297 Link-p_nom[EE6 0 BEV charger-2045] = -0
ERROR:root:Uncaught exception
Traceback (most recent call last):
  File "/mnt/research/eu-hydrogen/.snakemake/shadow/tmp1dg1rh_h/.snakemake/scripts/tmptpa_8n6s.solve_network.py", line 1028, in <module>
    n = solve_network(
        ^^^^^^^^^^^^^^
  File "/mnt/research/eu-hydrogen/.snakemake/shadow/tmp1dg1rh_h/.snakemake/scripts/tmptpa_8n6s.solve_network.py", line 982, in solve_network
    raise RuntimeError("Solving status 'infeasible'")
RuntimeError: Solving status 'infeasible'

The problem seems to be that, due to numerical inaccuracy, the existing EV capacity which has to run due to the fixed p_min_pu/p_max_pu profile is ever so slightly larger than the set load, leading to infeasibility.

I had turned off the penalty generation or and negative load generators for this; that would probably solve the problem too.

A fix that also worked for me is allowing old vehicles to be phased out:

@@ -81,6 +81,18 @@ def add_brownfield(n, n_p, year):
         c.df[f"{attr}_nom"] = c.df[f"{attr}_nom_opt"]
         c.df[f"{attr}_nom_extendable"] = False
 
+        # Allow the model to phase out vehicles early if needed to avoid infeasibility
+        transport_carriers = [
+            "land transport oil",
+            "land transport fuel cell",
+            "land transport EV",
+            "BEV charger",
+            "V2G",
+        ]
+        vehicle_i = c.df.index[c.df.carrier.isin(transport_carriers)]
+        c.df.loc[vehicle_i, f"{attr}_nom_max"] = c.df.loc[vehicle_i, f"{attr}_nom"]
+        c.df.loc[vehicle_i, f"{attr}_nom_extendable"] = True
+
         n.import_components_from_dataframe(c.df, c.name)
 

Maybe this a reasonable approach for making the transport sector more obviously feasible / solvable from an optimisation perspective? On the other hand, if you think dummy generators are more elegant, I'm sure that solves more problems too.

@lisazeyen
Copy link
Contributor Author

@koen-vg I am not sure what the best way is. I am currently having an additional load shedding generator and extended the clipping in the config to avoid numerical problems. Allowing earlier phase outs is an option as well, my suspicion is that one get's a too quick transition of the car fleet if you allow for an earlier phase-out. One could also think about only fixing the profiles of EVs and H2 fuel cell cars and letting the internal combustion cars without a profile.

@s8au
Copy link

s8au commented Apr 15, 2024

@lisazeyen
In case it is interesting to you: I also had the problem with infeasibilities in my branch for the endogenous transport. Currently, I remove the temperature dependence to get time independent efficiencies, which fixes the problem. I also thought about removing the profiles for one car type, but I was worried, that the system then prefers that type and/or is not able to solve it without it.

@koen-vg
Copy link
Contributor

koen-vg commented Apr 15, 2024

I can see what I get in terms of phase-outs and if this is a real problem.

If I understand it correctly, the model could actually gain money by "selling off" (i.e. phasing out) vehicles, and so one thing that would surely be needed would be to set the capital cost of vehicles that can be phased out to zero or even something negative so that it's costly to phase vehicles out directly. However, this would result in some annoyance when analysing the network afterwards since this cost data will technically be wrong. Maybe one could even reset the capital cost again afterwards / before analysis.

In principle, if it turns out that it's cost effective to switch very quickly to EVs even though there's significant capital cost (and sunk cost in terms of ICE vehicles) tied to that, that's an interesting result in its own right?

@koen-vg
Copy link
Contributor

koen-vg commented Apr 15, 2024

(By the way, feel free to have a look at and use my fork of this branch where I merged the latest master branch, including multiyear support. I haven't been able to test it in much detail but it more or less seems to work: https://github.com/koen-vg/pypsa-eur/tree/endogenous-transport)

@koen-vg
Copy link
Contributor

koen-vg commented Apr 15, 2024

With the configuration I'm working with current, which is not too far from default, I'm getting the following transport transition when phasing out 20% of existing vehicles is allowed at a cost (i.e. p_nom_min set to 0.8 * p_nom):
image
So it does look like the model will use the phase-out if it can. This was even with the capital cost multiplied by -30% to apply a penalty to phasing out.

(Note that the graph also points at the incongruence of measuring transportation demand and supply in 100km units, not MWh; the energy balances need to be corrected for this somehow.)

P.S. For reference, I remembered that I had remove the dummy generator and thus ran into feasibility issues originally because the dummy generator itself created numerical instability in the context of MGA optimisations.

@koen-vg
Copy link
Contributor

koen-vg commented Sep 5, 2024

Hi! I think this is a great feature, and was wondering what might be needed to get this merged? I was just having a quick look at the merge conflicts and it seems that the master branch is diverging more and more from this PR. There is still the issue that this PR works with transportation demand in distance driven (which makes sense for comparing different types of cars, but creates "downstream" problems with plotting, summaries, etc. which assume energy units), whereas the master branch uses an energy demand. It looks like the handling of IDEES and Eurostat data on transportation has also been updated in the master branch while this PR is a bit different.

I'd be happy to have a go at pushing this PR to completion if there's a clear idea of where to go with it :) We might then consider if it makes sense to try to merge master into this branch, or just start with a clean slate and extract the relevant parts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants