Skip to content

Agent share constraints in dispatch - #1566

Draft
tsmbland wants to merge 1 commit into
mainfrom
agent_share_constraints
Draft

tsmbland wants to merge 1 commit into
mainfrom
agent_share_constraints

Conversation

@tsmbland

Copy link
Copy Markdown
Collaborator

Description

Please include a summary of the change and which issue is fixed (if any). Please also
include relevant motivation and context. List any dependencies that are required for
this change.

Fixes # (issue)

Type of change

  • Bug fix (non-breaking change to fix an issue)
  • New feature (non-breaking change to add functionality)
  • Refactoring (non-breaking, non-functional change to improve maintainability)
  • Optimization (non-breaking change to speed up the code)
  • Breaking change (whatever its nature)
  • Documentation (improve or add documentation)

Key checklist

  • All tests pass: $ cargo test
  • The documentation builds and looks OK: $ cargo doc
  • Update release notes for the latest release if this PR adds a new feature or fixes a bug
    present in the previous release

Further checks

  • Code is commented, particularly in hard-to-understand areas
  • Tests added that prove fix is effective or that feature works

@codecov

codecov Bot commented Oct 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 40.00000% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.58%. Comparing base (1612d17) to head (bebe2eb).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
src/simulation/optimisation/constraints.rs 40.00% 33 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1566      +/-   ##
==========================================
- Coverage   90.88%   90.58%   -0.31%     
==========================================
  Files          61       61              
  Lines        9121     9176      +55     
  Branches     9121     9176      +55     
==========================================
+ Hits         8290     8312      +22     
- Misses        509      542      +33     
  Partials      322      322              

☔ View full report in Codecov by Harness.
📢 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.

@tsmbland

Copy link
Copy Markdown
Collaborator Author

From a conversation with Claude:


Some thoughts on refining this, from a discussion about how agents should ideally behave (see the new docs/model/agents.md).

Keeping the overall commodity balance and adding share-ratio constraints between agents seems like a good approach. The dual of the overall balance is still a sensible market price: an extra unit of external demand has to be split between agents by share, so the dual is effectively the share-weighted average of the agents' marginal costs. Demand induced by other agents' assets is also handled automatically.

There are three gaps compared with the intended behaviour:

  1. Induced demand. The ratio is applied to each agent's gross output, so demand that an agent's own assets create for the commodity (e.g. its electrolysers consuming its electricity) is also split by share. The intent is that an agent serves its own induced demand in full, with its share applying only to external demand. Fix: constrain net output (output minus the agent's own consumption of that commodity), which is still linear in activity.
  2. Zero shares. An agent with share 0 is forced to zero output, so it can't supply itself. Also, if the reference (first) agent has share 0, then p_ref * aux_i = p_i * aux_ref reduces to aux_ref = 0 and the other agents are no longer constrained relative to each other.
  3. Oversupply. There's no way for an agent to produce more than its share, e.g. when a fixed-ratio multi-output technology produces a surplus of a side product. The surplus shouldn't be passed to other agents ("no leakage"), but it also shouldn't make the problem infeasible.

A symmetric formulation that addresses all three: for each market and time-slice selection at the commodity's balance level, add a variable E (external demand) and, for each responsible agent a, a non-negative spill variable Spill_a, with

Output_a - OwnUse_a - Spill_a = s_a * E

alongside the overall balance, with spill subtracted from supply. Then:

  • there's no reference agent, so a share of 0 just means the agent only supplies itself;
  • spill lets an agent oversupply without the surplus counting towards anyone else's obligations;
  • the overall balance dual is still the market price.

Spill could have zero cost, or a small one to keep the solution unique.

Next step: population agents

The same idea applied one level down would handle population agents, including those responsible for several commodities with fixed-ratio equipment (e.g. a boiler providing both space heating and hot water).

Each asset belongs to a group of entities (e.g. a set of households). Equipment bought together as a bundle forms one group. For each group g, commodity c and time slice t:

sum over assets j in g of (k_jc * x_jt) - Spill_gct = Demand_gct,    Spill_gct >= 0

where x_jt is activity and k_jc is the output coefficient.

  • For a single-commodity agent, this reduces to each asset following the demand profile (today's population behaviour).
  • A combined boiler runs up to whichever need is larger in each time slice, and spills the surplus of the other commodity. The surplus isn't credited.
  • A boiler plus a separate water heater in one group just has each commodity's constraint binding on its own asset, with no spill.
  • Spill is per group, so one group's surplus can't serve another group's needs ("no leakage" within the agent). The agent's credited output is the sum over groups, which is what the agent share constraints above act on.
  • Spill can cost nothing (positive operating costs already discourage it). A small penalty would guard against side products with negative costs making over-production attractive.

The main design choice is how Demand_gct is defined:

  1. Fixed group size (preferred). When a group is commissioned, it records the annual needs of its entities for each commodity. Its demand profile is that amount spread over the agent's current demand profile for each commodity. This is physical (households are tied to their equipment) and matches "growth = more entities": existing groups keep their size and new demand forms new groups. Investment has to ensure that the groups' needs sum to the agent's demand, adding groups for growth and handling shrinking demand through retirement or partial decommissioning.
  2. Group fraction as a dispatch variable. A yearly variable phi_g for each group, the same across time slices, with sum phi_g = 1 and Demand_gct = phi_g * D_ct. This needs no bookkeeping, but dispatch can effectively move households between bundles whenever there's spare capacity, which isn't physical.

Consequences: assets need a group identifier, and population agents appraise whole bundles covering all of their commodities (e.g. a combined boiler versus each pairing of a space heater with a water heater).

This branch has not been deployed

No deployments
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.

1 participant