Skip to content

Conversation

@tsmbland
Copy link
Collaborator

@tsmbland tsmbland commented Oct 13, 2025

Not for merging. I'm just keeping this open as a draft PR so we have something to work with during while we try to add support for circular commodity flows

Temporarily turned off graph validation so it can get to the dispatch run in the first year. It will panic after this when it gets to investment

@tsmbland tsmbland mentioned this pull request Oct 16, 2025
10 tasks
@tsmbland
Copy link
Collaborator Author

@ahawkes I've added your second model with a bigger circularity

Attaching the graph generated by MUSE to help visualise the model (with CO2 removed to simplify it a bit)

graphviz (8)

There's another problem with this that OAGRSV has multiple SED outputs (GASPRD and OILCRD), and we have to select one of them as a primary output. Both of these commodities are owned by the same agent, which makes a lot of sense, however since agents consider each commodity one at a time, and only consider processes with that commodity as the primary output:

  • If we choose OILCRD as the primary output, then this process won't be considered to meet GASPRD demands, so the H2YPRD->GASPRD loop connection will never be used
  • If we choose GASPRD as the primary output, then there will be no processes available for OILCRD investment, and the simulation will fail.

Just thought I'd mention since, even if we come up with a viable solution for loops, the big loop in this model will never be used, unless we rethink the whole "primary commodity" thing.

Ideally I think we should allow agents to consider all of their commodities simultaneously, and consider any process that can produce any of these commodities. If we did that then OILCRD effectively becomes part of the loop as well (or, at least, needs to be considered alongside it)

@ahawkes
Copy link
Contributor

ahawkes commented Oct 17, 2025 via email

@ahawkes
Copy link
Contributor

ahawkes commented Oct 17, 2025

Is there a way to do something like a topological sort that produces something useable when there are circularities? - e.g. a commodity order that would minimise the need for iteration across commodities. Or anything useful - I'm sure someone somewhere has had similar problems at some point....

Possible broad approach (which no doubt has many complications):

  1. I think our ironing out loop needs to be changed so that it ALWAYS uses previous MSY prices, and instead of converging on price, converges on commodity demand.
  2. Then, use that loop to revisit the commodities that are part of circularities and re-do investment.

@tsmbland
Copy link
Collaborator Author

Is there a way to do something like a topological sort that produces something useable when there are circularities? - e.g. a commodity order that would minimise the need for iteration across commodities. Or anything useful - I'm sure someone somewhere has had similar problems at some point....

Possible broad approach (which no doubt has many complications):

  1. I think our ironing out loop needs to be changed so that it ALWAYS uses previous MSY prices, and instead of converging on price, converges on commodity demand.
  2. Then, use that loop to revisit the commodities that are part of circularities and re-do investment.

Yes, we can identify strongly connected components, compress them into a single "group node" and then do a topological sort. You'd get something like this:

graphviz (9)

(Note there are many valid topological sorts, this is just one of them)

You could then run through the nodes in order, as we currently do, then when you get to a group node you could run an ironing out loop within that group (in this case I don't think it matters what order we solve the commodities in). If we're not converging on commodity prices then I don't think we have to run an ironing-out loop over the whole system, just within the group(s) of strongly-connected commodities.

Two common scenarios I can imagine:

  • it tends towards a balanced solution but takes a very long time to get there
  • it bounces around two or more different solutions and never stabilises

We'll obviously have a limit on how many times we can iterate, but ultimately we'll probably have to accept some amount of unmet demand somewhere.

Yes I think that's a known issue with choosing a primary commodity - weird things may happen with the other SED outputs.
But isn't the 1st option here not that bad? Or at least consistent with the primary commodity assumption? We're assuming we don't care about the H2YPRD-GASPRD loop.
And isn't it a rule that for each SED/SVD commodity, there must be at least one process that has it as the primary output commodity? So GASPRD demand will still be considered for investment.

Yeah there's no problem, I'm just saying that the loop is kind of irrelevant in this example so perhaps not very useful for development

And the big loop in the model will be used, because there is OILCRD demand, which requires H2YPRD input, which in turn has implications for H2YPRD demand/capacity. So once one reaches the end of the first pass of investment appraisal, one needs to go back to H2YPRD and re-do investment to make sure there's enough capacity there?

Kind of. But if this H2YPRD requires extra GASPRD, then this won't be met by OILCRD as this isn't a primary producer of GASPRD. Apart from the H2YPRO/H2YGEN loop, all of the H2YPRD demand is known upfront once we've solved OILCRD.

I've tried to clarify this by using dashed lines for non-primary flows in the above graph. Really, if we're sticking to the current primary commodity approach, then we only need to consider primary outputs when determining the investment order, which would give us something like this:

graphviz (10)

So no big loop when it comes to investments, just a small one

@tsmbland
Copy link
Collaborator Author

We'll also have to be careful that loops can't "create energy out of nowhere", otherwise I could very well imagine a self-reinforcing loop that meets the full commodity demands without any inputs

@ahawkes
Copy link
Contributor

ahawkes commented Oct 17, 2025

OK - yes - can you think of an example model that has a better big loop?

The supernode thing could be interesting for this.

Also there appears to be methods to approximate the minimum feedback arc set - essentially trying to find the order that requires the minimum number of edges to be cut to remove all cycles from the graph. petgraph does not appear to have a method for this, but Gemini (so should not be trusted!) says:

  1. Perform a DFS Traversal: Traverse your graph using a depth-first search.

  2. Order by Finishing Time: As the DFS completes its exploration from each node (i.e., when it's "finished" with a node and all its descendants), place that node at the front of a list.

  3. Reverse the List: The final order is the reverse of this list.

This algorithm attempts to place nodes that are part of a cycle close to each other. The edges that go "backward" in this final ordering are the feedback arc set. It's not guaranteed to be the absolute minimum feedback arc set, but could be a good approximation and apparently is computationally very fast.

@ahawkes
Copy link
Contributor

ahawkes commented Oct 21, 2025

I think we should try the topological sort using the supernode thing.

The next question is how to deal with investment within each supernode. I'm not sure how much it matters other than that the pure internal nodes (i.e. that are not entry points to the supernode) probably should not be first for investment? Other than that, maybe start with the one with the greatest commodity demand that comes from outside the supernode? Also I was reading on various graph theory measures of connectedness (betweenness centrality, closeness centrality, and degree centrality), but not clear to me how much these might matter.

Any thoughts @tsmbland ? Overall we want the best chance of fast convergence (both within and between supernodes). Maybe could even give the user options in the setup file - investment prioritisation based on betweenness centrality, closeness centrality, degree centrality, and maximum entry demand, etc etc?

@tsmbland
Copy link
Collaborator Author

I think we should try the topological sort using the supernode thing.

👍

The next question is how to deal with investment within each supernode. I'm not sure how much it matters other than that the pure internal nodes (i.e. that are not entry points to the supernode) probably should not be first for investment? Other than that, maybe start with the one with the greatest commodity demand that comes from outside the supernode? Also I was reading on various graph theory measures of connectedness (betweenness centrality, closeness centrality, and degree centrality), but not clear to me how much these might matter.

Any thoughts @tsmbland ? Overall we want the best chance of fast convergence (both within and between supernodes). Maybe could even give the user options in the setup file - investment prioritisation based on betweenness centrality, closeness centrality, degree centrality, and maximum entry demand, etc etc?

If we're happy to let it keep iterating until it converges, then I don't think the order matters. But yes, to help it converge quickly, or if we're inevitably going to have to trade off convergence/unmet demand, then it will require more thought.

Definitely makes sense to solve entry nodes before internal nodes. Beyond that I'm not really sure. First guess I'd probably rank based on minimum distance to an entry node, but there may be more sophisticated things we can do. Obviously becomes more of an issue the larger the cycle is. I think it will require some experimenting

@ahawkes
Copy link
Contributor

ahawkes commented Oct 21, 2025

Additional thought:

  • Currently we converge on price in the ironing out loop. I think instead we should converge on commodity demand in the supernodes (and always use previous MSY prices). But can we not throw out the code that converges on price, as it may be useful at a later point?
  • So - ironing out would now apply only to supernodes - and we have no final price-based ironing out as per before.

Would require changes to docs too of course!

@codecov
Copy link

codecov bot commented Nov 3, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.95%. Comparing base (8099a3f) to head (343f7ac).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #914   +/-   ##
=======================================
  Coverage   83.95%   83.95%           
=======================================
  Files          50       50           
  Lines        5696     5696           
  Branches     5696     5696           
=======================================
  Hits         4782     4782           
  Misses        685      685           
  Partials      229      229           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

3 participants