Add link delay for output ports#1569
Conversation
- Add get_delay_source_indexer using cumulative generator weightings - Add check_link_delays consistency check for negative/excessive delays - Fix non_delayed.any() -> not non_delayed.empty for pd.Index - Use c_name variable to avoid str/Component confusion in descriptors - Handle MultiIndex active_assets in nodal balance constraints
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Add user guide section, energy balance formulation, link-delay notebook with bar plots, hydro-reservoirs delay section, and navigation entries.
docs/user-guide/components/links.md
Outdated
|
|
||
| Links can model time-delayed energy transport via the `delay` and `cyclic_delay` attributes. This is useful for representing transport delays in gas pipelines, hydrogen shipping, long-distance HVDC transmission, or any process where energy withdrawn at `bus0` arrives at output ports after a configurable time lag. | ||
|
|
||
| - **`delay`**: The delay in units of elapsed time, referencing `n.snapshot_weightings.generators`. Energy withdrawn from `bus0` at snapshot `t` arrives at `bus1` at `t + delay`. That is, for uniform hourly snapshots with unit weightings, `delay=3` means a 3-hour delay. In case of snapshot weightings greater than 1, the energy arrives at `bus1` the first snapshot at whose time step at least `delay` units later. |
There was a problem hiding this comment.
a) I don't understand the second part about snapshot weightings > 1
b) What is we have snapshots with weighting = 0? Are they skipped?
c) What if port0 is activated in snapshot t with a delay=1, and t+1 has a different weighting. How are e.g. marginal_costs or efficiency affected? Assume efficiency is snapshot dependent? Would that give an incentive to operate the link at port0 in t? Not sure if that is fine or not.
d) In many instances we have started to allow pd.Series instead of scalar values for properties. Would it be possible or meaningful to do the same here? I.e. allow for a Series of delays, specifying different delays during different snapshots.
e) If the users changes their snapshots, they will also need to adjust the delay, right? I think it should fail rather than trying to automatically fix it and aligning it with a guessed snapshot.
There was a problem hiding this comment.
a) there is a "as" too much, I will add an example to clearify.
b) weighting of 0? interesting use case. they are not skip, the would just not contribute to the cumulative sum of the snapshot weightings when comparing delays and time elapsed. if you want to have that case bullet proof I can add a test for that. It could be that atm the a delay of 1 and a weighting of 0 would lead to energy traveling back in time.
c) marginal_cost is always referencing p0 so this is implicitly handled by the optimization, efficiency is only touching outputs p1+. if the efficiency is time-dependent, it will reference the efficiency at the arrival time, ie. the target snapshot. after thinking about it, I found this cleaner then the other way round, but open to change if this seems counter-intuitive.
d) honestly I wouldn't do it. I would be quite a complex thing. the cumulative comparison is already complex and this would make it even more complicated to review later.
e) do you mean if the user change the snapshots or their weightings? Since the delay takes the time elapsed into account, changing the snapshot alongside with the snapshot weighting is no problem. suppose delay is 3 and snapshot resolution is hourly (sn weights = 1) . now you are resampling to 3 hourly resolution (adjusting the sn weights to 3), everything works out of the box without touching the delay.
| The **incidence matrices** $K_{n,l}$ for the [`Line`][pypsa.components.Lines] and [`Transformer`][pypsa.components.Transformers] components and $L_{n,l,t}$ for the [`Link`][pypsa.components.Links] components govern flows between buses. The incidence matrix $K_{n,l}$ takes non-zero values $-1$ if the line or transformer $l$ starts at bus $n$ and $1$ if it ends at | ||
| bus $n$. If $p_{l,t}>0$ it withdraws from the starting bus. The time-varying incidence matrix $L_{n,l,t}$ takes non-zero values $-1$ if the link $l$ starts at bus $n$ and efficiency $\eta_{n,l,t}$ if it ends at bus $n$. If $f_{l,t}>0$ it withdraws from `bus0` and feeds in $\eta_{n,l,t} f_{l,t}$ to `bus1`. For a link with more than two outputs (e.g. a combined heat and power plant), the incidence matrix $L_{n,l,t}$ has more than two non-zero entries with efficiencies $\eta_{n,l,t}$ for `bus2`, `bus3`, etc.. The entries may also be negative to denote additional inputs rather than multiple outputs. | ||
|
|
||
| When a link has a non-zero `delay` attribute, the output flow at snapshot $t$ corresponds to the input at an earlier source snapshot $s(t)$ rather than at $t$ itself. The source snapshot $s(t)$ is the latest snapshot such that $\tau(s) \leq \tau(t) - \delta$, where $\tau$ denotes the cumulative start time derived from `snapshot_weightings.generators` and $\delta$ is the delay value. The delayed contribution to the energy balance at output bus $n$ becomes: |
There was a problem hiding this comment.
I think we should enforce that the delay ends on an existing snapshot, not matching it to the next snapshot. The user should explicitly need to handle this.
euronion
left a comment
There was a problem hiding this comment.
I love seeing this feature.
Left some comments without looking at it in more detail.
Another concern: How does this combine with the statistics module, especially if aggregate_time=False. Do we then need to specify at_port to get the right results for time-series, as they will be different depending on which port is selected.
lkstrp
left a comment
There was a problem hiding this comment.
Following the discussion yesterday, I fully agree with delays weighted by snapshot weightings. It adds extra complexity but with strict consistency checks we can just handle this
I have a couple of code simplifications. Instead of adding all the comments, can I just commit a bit in here?
thanks for the review @euronion. before we make the decision of killing the flexibility of having run it with or have a look at the rdt built in a minute when it (hopefully) runs through About the stastics module. that is working right away as it uses the already delayed dispatch time series at |
I am not too sure about the use cases. The convention looks clean and is also explained in a good way, but is it just convenience and it would always be better to make delays match with weights? I feel that this can be hard to investigate/ understand. If we keep the convention we could add warnings that some delay rounding is going on |
|
@lkstrp should we then introduce a consistency check? I still think |
|
I changed a couple of things and added multi horizon support (raised by @Irieo). @FabianHofmann If you could have another look? Otherwise two more things:
|
Related to the convention / automatic matching: But I would be keen on hearing the comments from others. |
@lkstrp fine with pulling in now? |
This would still need to be checked then |
Add tests for zero-weight cyclic delay error and empty snapshots.
I it already checked by the consistency check but only warns. should we make that particular check always strict, ie. always raising? it is currently inside a function with another check, so setting the |
Changes proposed in this Pull Request
Adds configurable time delays for Link output ports. Energy withdrawn from
bus0at snapshottarrives at output ports after a delay measured insnapshot_weightings.generators(best choice I could make wo introducing a new field) units. This enables modeling transport delays (e.g. gas pipelines, shipping).New Link attributes:
delay/delay2,delay3, ... — delay in generator weighting time units per portcyclic_delay/cyclic_delay2,cyclic_delay3, ... — whether delayed energy wraps cyclically or is lost at boundariesImplementation:
Links.get_delay_source_indexer()computes per-snapshot source mappings using cumulative generator weightings, supporting both cyclic and non-cyclic modesLinks.split_by_port_delay()groups links by delay/cyclicity for efficient constraint generationp1,p2, ... DataFramescheck_link_delaysconsistency check validates delay values are non-negative and within the snapshot horizonBug fix: Link-port attribute expansion (
bus2,bus3,efficiency2, ...) no longer mutates shared Link defaults across Network instances. A targeted test was added for this.Checklist
docs.docs/release-notes.mdof the upcoming release is included.