Closed ela-kotulska-frequenz closed 1 year ago
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.
I suggest to use the type datetime.timedelta
for duration_sec
and rename this field to duration
as Marenz did in the ringbuffer.
I suggest to design something that allows plugging in different consensus building algorithms. It would be great if we could abstract it in a way that it would allow to replace the matrjoschka / russian doll algorithm
with something else like a voting-based mechanism as @andrew-stevenson-frequenz suggested.
In this implementation, each actor is prompted to provide a minimum and maximum charging or discharging power they are willing to accept. The average rating for each option is calculated only for ratings that fall within each actor's acceptable range, using the same weighting system as before. Finally, the option with the highest average rating is chosen as the winner.
Down below is an example of what a voting based-approach could look like. In this example multiple actor negotiate about the (dis)charging power of a battery.
charging_power_options = ['-100kW', '-50kW', '0kW', '50kW', '100kW']
num_actors = 3
# Initialize a dictionary to store the ratings for each option
ratings = {option: [] for option in charging_power_options}
# Assign weights to each actor
weights = {1: 1, 2: 2, 3: 1}
# Prompt each actor to rate each option
for i in range(num_actors):
print("Actor", i+1, "please rate each option for charging power (-100kW to 100kW):")
for option in charging_power_options:
rating = int(input(option + ": "))
# Multiply the rating by the actor's weight
rating *= weights[i+1]
if i == 2: # if actor is discharging, then invert the rating
rating *= -1
ratings[option].append(rating)
# Prompt each actor to provide their acceptable range for charging or discharging power
acceptable_ranges = {}
for i in range(num_actors):
print("Actor", i+1, "please provide your acceptable range for charging or discharging power:")
min_power = int(input("Minimum power: "))
max_power = int(input("Maximum power: "))
acceptable_ranges[i+1] = (min_power, max_power)
# Calculate the average rating for each option that falls within each actor's acceptable range
total_weight = sum(weights.values())
averages = {}
for option in charging_power_options:
# Compute the weighted average of the ratings for each option
rating_sum = 0
weight_sum = 0
for i in range(num_actors):
rating = ratings[option][i]
weight = weights[i+1]
if i == 2: # if actor is discharging, then invert the rating
rating *= -1
# Check if the rating falls within the actor's acceptable range
min_power, max_power = acceptable_ranges[i+1]
if min_power <= rating <= max_power:
rating_sum += rating * weight
weight_sum += weight
rating_avg = rating_sum / weight_sum if weight_sum > 0 else 0
averages[option] = rating_avg
# Find the option with the highest average rating
winner = max(averages, key=averages.get)
print("The chosen option is:", winner)
What I would like to achieve is to create an interface for the PowerManager that allows both approaches to work; the russian doll priority-based approach as well as the voting based one.
What's needed?
PowerManager is a tool for controlling requests to the microgrid from the sdk side.
Requests:
Problem
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:
So each actor sends power and the power tolerance:
min_power
andmax_power
. That means: I suggest to setpower
but all values betweenmin_power
andmax_power
are also ok.PowerManager would receive requests and set the bounds in the way described below:
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.internal bound
becomes invalid, theninternal bounds
would be the same as microgrid bounds.set power 0
command will be send to microgrid, to cancel previousset 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 theinternal 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.