Skip to content

PowerManager #161

@ela-kotulska-frequenz

Description

@ela-kotulska-frequenz

What's needed?

PowerManager is a tool for controlling requests to the microgrid from the sdk side.

Requests:

  • Charge/Discharge Batteries
  • Charge/Discharge EvChargers
  • Set bounds for EvChargers (Power can only be adjusted if it has been invoked by a car, however this might change in the future with hybrid charging and is part of the VW project)
  • Charge/Discharge ChpPools (Discharge only for natural gas power plants but also charge with hydrogen electrolyzers which will also be something upcoming)

Problem

  • Many actors might be running at the same time.
  • Each actors should be allowed to send requests to the PowerManager. Requests might be mutually exclusive. For example at the same time actor1 might request to charge battery1 and actor2 might request to discharge that battery.
  • Each actor should be allowed for exclusive lock for some duration time. In this scenario no other actor should interfere with its request.
  • There are many actors running independently but some actors might be more important then others. Requests from actors with higher priority should be have prior right.

Proposed solution

We called it matrjoschka algorithm. The name comes from Russian nested dolls that construction makes easier to understand the algorithm.

For simplicity lets consider PowerManager for controlling batteries only. Actors can send requests to charge or discharge batteries.

Each actor should have assigned priority. Priority tells how important is request from that actor. Priority should be configurable dynamically in the UI.

Request would look like that:

class BatteryPowerRequest:
    priority: float # Actors priority
    batteries: Set[int] # What batteries should be charged/discharged
    power: float # Total power recommended by actor to be distributed between given batteries. Negative value would discharge batteries, positive value would charge them.This value should be between min_power and max_power.
    min_power: float # min power bound (described below)
    max_power: float # max power bound (described below)
    duration_sec: float # how long the request should stay.

So each actor sends power and the power tolerance: min_power and max_power. That means:
I suggest to set power but all values between min_power and max_power are also ok.

PowerManager would receive requests and set the bounds in the way described below:

  • power tolerance from the requests with highest priority are the most important.
  • power tolerance from the requests with lower priority should be inside the range given by actors with higher priority.
  • If requested power tolerance are outside the range created by actors with higher priority, then this request will be rejected.
  • If requested power tolerance are within the range created by actors with higher priority, then this request will limit the existing bounds. Power from that request will be applied.
  • If request with higher priority is outside the range set by actor with higher priority, then this request will be rejected.

Let internal bound be bound created based on the requests from the actor.

PowerManager will stream every change of internal bound to the subscribed users using channels.

Beyond the internal bound, microgrid have it own bounds that are streamed by components. Microgrid Bounds has the highest priority. That priority can't be overwrite.

  • If any value in the requested bound will exceed microgrid bounds, then request will be ignored.
  • If microgrid will change bound and the current internal bound becomes invalid, then internal bounds would be the same as microgrid bounds. set power 0 command will be send to microgrid, to cancel previous set power command.

If actor needs an exclusive lock, then he should send request with both power, min_power, max_power set to the same level. Exclusive lock will be ignored if the min_power or max_power are outside the internal bound. Exclusive lock can be interrupted if actor with higher priority sends request that changes bounds in a way that the exclusive lock bounds don't apply.

Examples

Lets assume we have 2 actors: Actor_1, Actor_2. Actor1 has higher priority then Actor2.

sequenceDiagram
    actor Actor1
    actor Actor2
    actor PowerManager
    actor Microgrid

    Microgrid->>PowerManager: charge_bounds: [0, 1000]
    Microgrid->>PowerManager: discharge_bounds: [-1000, 0]
    activate PowerManager
    Note right of PowerManager: internal discharge bounds = [-1000, 0]
    Note right of PowerManager: internal charge bounds = [0, 1000]
    deactivate PowerManager

    Actor1-)PowerManager: Request(priority=1, power=500, min_power=200, max_power=800)
    PowerManager--)Actor1: Request Succeed: <br/> Because bounds were within internal bounds.
    activate PowerManager
    Note right of PowerManager: internal discharge bounds = [0, 0]
    Note right of PowerManager: internal charge bounds = [200, 800]
    deactivate PowerManager
    PowerManager-) Microgrid: set_power(500)

    Actor2-)PowerManager: Request(priority=2, power=100, min_power=0, max_power=100)
    PowerManager--)Actor2: Request Failed: Because requested bounds were outside the internal bounds. <br/> Actor 2 has lower priority then Actor1 so he can't increase the bounds range. <br/> He can only limit them (as showed in the next request).

    Actor2-)PowerManager: Request(priority=2, power=400, min_power=300, max_power=600)
    PowerManager--)Actor2: Request Succeed:<br/>Because requested bounds just limit the range that actors with higher priority set.
    activate PowerManager
    Note right of PowerManager: internal discharge bounds = [0, 0]
    Note right of PowerManager: internal charge bounds = [300, 600]
    deactivate PowerManager
    PowerManager-) Microgrid: set_power(400)


    Actor2-)PowerManager: Request(priority=2, power=210, min_power=210, max_power=210)
    PowerManager--)Actor2: Request Succeed:<br/> Because requested bounds are in range that actors with higher priority set
    activate PowerManager
    Note right of PowerManager: internal discharge bounds = [0, 0]
    Note right of PowerManager: internal charge bounds = [210, 210]
    Note over PowerManager,Actor2: Actor 2 has exclusive lock, actors with lower priority can't change bounds.
    deactivate PowerManager
    PowerManager-) Microgrid: set_power(210)


    Actor1-)PowerManager: Request(priority=1, power=-500, min_power=-800, max_power=0)
    PowerManager--)Actor1: Request Succeed. Because actor1 has higher priority so his request can override the bounds set by actors with lower priority.
    activate PowerManager
    Note right of PowerManager: internal discharge bounds = [-800, 0]
    Note right of PowerManager: internal charge bounds = [0, 0]
    deactivate PowerManager
    PowerManager-) Microgrid: set_power(-500)
Loading

Metadata

Metadata

Labels

part:microgridAffects the interactions with the microgridpriority:highAddress this as soon as possiblescope:breaking-changeBreaking change, users will need to update their codetype:enhancementNew feature or enhancement visitble to users

Type

No type

Projects

Status

Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions