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

Adaption of multilink implementation #549

Open
FabianHofmann opened this issue Feb 3, 2023 · 2 comments · May be fixed by #738
Open

Adaption of multilink implementation #549

FabianHofmann opened this issue Feb 3, 2023 · 2 comments · May be fixed by #738

Comments

@FabianHofmann
Copy link
Collaborator

Q: Should we refactor the multilink representation from wide table format to a long table format?

Some discussion points:

Not or partially supported functionalities and problematic extensions with current multilinks implementation:

IO

Atm we have to use override_component_attrs to load any pypsa-eur-sec network. There are also overrides not related to multilinks (https://github.com/PyPSA/pypsa-eur-sec/tree/master/data/override_component_attrs) but these are actually not necessarily needed as tracked by the IO of pypsa anyway (custom static columns are exported and imported). We discussed how to automatically support overrides when loading from disk #321 but it is tedious and requires deep dive into the io.py module. For each data format we would need a parse_override function to be called in the __init__ of the network before the override section and a write_override for each export.
This becomes problematic as soon as we want to publish sector networks on Zenodo as stand alone files.

Clustering

Clustering does not support multilinks. This would need to be done when adding multilinks at an earlier stage in PyPSA-EUR.

Plotting

It is not possible to plot secondary links in the plotting routine. One had to create a new network in order to show intersector links and the sub topology of a location. Hiding links in the plot on the other hand is easy.

Statics

The statistics module is not aware of multilinks. Biggest hurdles are groupbys and negative efficiency's and emtpy rows. This would require quite some case distinctions and unnecessarily bloated code.

Power flow

The PF calculation does not support p_set of multilinks. In our workflows, we do not have electricity as secondary outputs, but users might rely on it.

Time-dependent efficiency's

This will be quiet tricky to generalize. Efficiency's would also be needed to go into the time series dict eventually with a subset of the link index. The get_switchable_as_dense function would need to be adapted quite clevery.


Possible advantages of the alternative data structure using a coupling column:

  • Nearly all of the above issues are resolved. The only thing I see is the clustering which needs to ignore secondary links (n.links[n.links.coupling.isnull()]) and after the clustering a mapping of the coupling column to the linkmap.
  • It is arbitrarily extendable.
  • Carriers can be added to secondary links, e.g. it would be possible to actually use the carrier for a carrier specification and not a technology specification.
  • Negative efficiencies could be avoided by switching the direction of the secondary link. Actually generalized couplings accross a network could be created.
  • Marginal cost could be added to secondary links and would be automatically considered in the objective function: n.links.groupby(n.links.coupling.fillna(n.links.index)).marginal_cost.sum()

Possible disadvantages:

  • p0 has a subset of the link index in presence of secondary links.
  • some columns are obselete for secondary links, e.g. capital_cost and need warning messages when used.
  • code in prepare_sector_network.py needs to be adapted. Instead of
n.madd("Link",
    nodes,
    suffix=" Haber-Bosch",
    bus0=nodes,
    bus1=spatial.ammonia.nodes,
    bus2=nodes + " H2",
    p_nom_extendable=True,
    carrier="Haber-Bosch",
    efficiency=1 / (cf_industry["MWh_elec_per_tNH3_electrolysis"] / cf_industry["MWh_NH3_per_tNH3"]), # output: MW_NH3 per MW_elec
    efficiency2=-cf_industry["MWh_H2_per_tNH3_electrolysis"] / cf_industry["MWh_elec_per_tNH3_electrolysis"], # input: MW_H2 per MW_elec
    capital_cost=costs.at["Haber-Bosch synthesis", "fixed"],
    lifetime=costs.at["Haber-Bosch synthesis", 'lifetime']
)

the new patters would be

n.madd("Link",
    nodes,
    suffix=" Haber-Bosch",
    bus0=nodes,
    bus1=spatial.ammonia.nodes,
    p_nom_extendable=True,
    carrier="Haber-Bosch",
    efficiency=1 / (cf_industry["MWh_elec_per_tNH3_electrolysis"] / cf_industry["MWh_NH3_per_tNH3"]), # output: MW_NH3 per MW_elec
    capital_cost=costs.at["Haber-Bosch synthesis", "fixed"],
    lifetime=costs.at["Haber-Bosch synthesis", 'lifetime']
)

n.madd("Link",
    nodes,
    suffix=" Haber-Bosch H2",
    bus0=nodes,
    bus1=nodes + " H2",
    coupling=nodes + " Haber-Bosch",
    carrier="Haber-Bosch",
    efficiency=-cf_industry["MWh_H2_per_tNH3_electrolysis"] / cf_industry["MWh_elec_per_tNH3_electrolysis"], # input: MW_H2 per MW_elec
)

@fneum @nworbmot @lisazeyen

@nworbmot
Copy link
Member

nworbmot commented Feb 5, 2023

I'm a bit sceptical, but interested to hear what others think. My thoughts:

  • Both schemes cause some friction, it's not clear which is better.
  • Changing the scheme will upset users who've been using current scheme for the last 5 years, for unclear gain.
  • I don't think that much boilerplate is needed in existing scheme; check out this code to accommodate energy balances - the multi-links aren't adding to code bloat.
  • The new scheme forces the user to add boilerplate (see example above for Haber-Bosch with multiple objects); we try to avoid making code more complicated for the user.
  • We have a PyPSA principle, one row per object; now if you inspect a link row, you have to hunt in the rest of the DataFrame to see if anything is coupled to it.
  • Duplicated rows contain redundant information (capital_cost, p_nom_extendable, marginal_cost, unit commitment) with unclear priority.

@loongmxbt
Copy link
Contributor

loongmxbt commented Jun 29, 2023

The problems we currently encounter when using multilink is:

The corresponding relationship between bus0, bus1, bus2, bus3 and efficiency, eff2, eff3 is not clear, it is easy to confuse, and some output or input need to use the positive and negative difference of eff, it is recommended to use a parameter to distinguish, similar to sign
For example, I think $\sum p_{in} * eff_{in} = \sum p_{out} * eff_{out}$ would be straightforward
Similar to CO2 + 3H2 ==>CH3OH + H2O
p_co2 * 1 + p_h2 * 3 = p_ch3oh * 1 + p_h2o * 1
I can intuitively define the eff of each input and output without conversion them to bus0->bus1, and taking care of input and output eff sign.

I think a brand new component would be better than extending link.

@FabianHofmann FabianHofmann linked a pull request Sep 26, 2023 that will close this issue
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants