Skip to content

Commit

Permalink
Make all examples validate correctly
Browse files Browse the repository at this point in the history
Signed-off-by: Mathias L. Baumann <[email protected]>
  • Loading branch information
Marenz committed May 12, 2023
1 parent a65ebe6 commit 6c94354
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 106 deletions.
73 changes: 41 additions & 32 deletions src/frequenz/sdk/actor/_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ def actor(cls: Type[Any]) -> Type[Any]:
TypeError: when the class doesn't have a `run` method as per spec.
Example (one actor receiving from two receivers):
``` python
```python
from frequenz.channels import Receiver, Sender, Broadcast
from frequenz.channels.util import Select
@actor
class EchoActor:
def __init__(
Expand All @@ -101,25 +103,29 @@ async def run(self) -> None:
await self._output.send(msg.inner)
input_chan_1: Broadcast[bool] = Broadcast("input_chan_1")
input_chan_2: Broadcast[bool] = Broadcast("input_chan_2")
async def main() -> None:
input_chan_1: Broadcast[bool] = Broadcast("input_chan_1")
input_chan_2: Broadcast[bool] = Broadcast("input_chan_2")
echo_chan: Broadcast[bool] = Broadcast("EchoChannel")
echo_chan: Broadcast[bool] = Broadcast("EchoChannel")
echo_actor = EchoActor(
"EchoActor",
recv1=input_chan_1.new_receiver(),
recv2=input_chan_2.new_receiver(),
output=echo_chan.new_sender(),
)
echo_rx = echo_chan.new_receiver()
echo_actor = EchoActor(
"EchoActor",
recv1=input_chan_1.new_receiver(),
recv2=input_chan_2.new_receiver(),
output=echo_chan.new_sender(),
)
echo_rx = echo_chan.new_receiver()
await input_chan_2.new_sender().send(True)
msg = await echo_rx.receive()
await input_chan_2.new_sender().send(True)
msg = await echo_rx.receive()
asyncio.run(main())
```
Example (two Actors composed):
``` python
```python
from frequenz.channels import Receiver, Sender, Broadcast
@actor
class Actor1:
def __init__(
Expand Down Expand Up @@ -154,24 +160,27 @@ async def run(self) -> None:
await self._output.send(msg)
input_chan: Broadcast[bool] = Broadcast("Input to A1")
a1_chan: Broadcast[bool] = Broadcast["A1 stream"]
a2_chan: Broadcast[bool] = Broadcast["A2 stream"]
a1 = Actor1(
name="ActorOne",
recv=input_chan.new_receiver(),
output=a1_chan.new_sender(),
)
a2 = Actor2(
name="ActorTwo",
recv=a1_chan.new_receiver(),
output=a2_chan.new_sender(),
)
a2_rx = a2_chan.new_receiver()
await input_chan.new_sender().send(True)
msg = await a2_rx.receive()
async def main() -> None:
input_chan: Broadcast[bool] = Broadcast("Input to A1")
a1_chan: Broadcast[bool] = Broadcast("A1 stream")
a2_chan: Broadcast[bool] = Broadcast("A2 stream")
a_1 = Actor1(
name="ActorOne",
recv=input_chan.new_receiver(),
output=a1_chan.new_sender(),
)
a_2 = Actor2(
name="ActorTwo",
recv=a1_chan.new_receiver(),
output=a2_chan.new_sender(),
)
a2_rx = a2_chan.new_receiver()
await input_chan.new_sender().send(True)
msg = await a2_rx.receive()
asyncio.run(main())
```
"""
Expand Down
40 changes: 28 additions & 12 deletions src/frequenz/sdk/actor/power_distributing/power_distributing.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,10 @@ class PowerDistributingActor:
printed.
Example:
``` python
import grpc.aio as grpcaio
from frequenz.sdk.microgrid.graph import _MicrogridComponentGraph
```python
from frequenz.sdk import microgrid
from frequenz.sdk.microgrid.component import ComponentCategory
from frequenz.sdk.actor import ResamplerConfig
from frequenz.sdk.actor.power_distributing import (
PowerDistributingActor,
Request,
Expand All @@ -103,29 +102,45 @@ class PowerDistributingActor:
PartialFailure,
Ignored,
)
from frequenz.channels import Bidirectional, Broadcast, Receiver, Sender
from datetime import timedelta
from frequenz.sdk import actor
HOST = "localhost"
PORT = 50051
target = f"{host}:{port}"
grpc_channel = grpcaio.insecure_channel(target)
api = MicrogridGrpcClient(grpc_channel, target)
await microgrid.initialize(
HOST,
PORT,
ResamplerConfig(resampling_period=timedelta(seconds=1))
)
graph = _MicrogridComponentGraph()
await graph.refresh_from_api(api)
graph = microgrid.connection_manager.get().component_graph
batteries = graph.components(component_category={ComponentCategory.BATTERY})
batteries_ids = {c.component_id for c in batteries}
battery_status_channel = Broadcast[BatteryStatus]("battery-status")
channel = Bidirectional[Request, Result]("user1", "power_distributor")
power_distributor = PowerDistributingActor(
mock_api, component_graph, {"user1": channel.service_handle}
users_channels={"user1": channel.service_handle},
battery_status_sender=battery_status_channel.new_sender(),
)
# Start the actor
await actor.run(power_distributor)
client_handle = channel.client_handle
# Set power 1200W to given batteries.
request = Request(power=1200.0, batteries=batteries_ids, request_timeout_sec=10.0)
await client_handle.send(request)
# Set power 1200W to given batteries.
request = Request(power=1200, batteries=batteries_ids, request_timeout_sec=10.0)
await client_handle.send(request)
# It is recommended to use timeout when waiting for the response!
result: Result = await asyncio.wait_for(client_handle.receive(), timeout=10)
Expand All @@ -134,9 +149,10 @@ class PowerDistributingActor:
elif isinstance(result, PartialFailure):
print(
f"Batteries {result.failed_batteries} failed, total failed power" \
f"{result.failed_power}")
f"{result.failed_power}"
)
elif isinstance(result, Ignored):
print(f"Request was ignored, because of newer request")
print("Request was ignored, because of newer request")
elif isinstance(result, Error):
print(f"Request failed with error: {result.msg}")
```
Expand Down
92 changes: 51 additions & 41 deletions src/frequenz/sdk/power/_distribution_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,26 +71,26 @@ class DistributionAlgorithm:
We would like our distribution to meet the equation:
``` python
```
distribution[i] = power_w * capacity_ratio[i] * x[i]
```
where:
``` python
```
sum(capacity_ratio[i] * x[i] for i in range(N)) == 1
```
Let `y` be our unknown, the proportion to discharge each battery would be
(1):
``` python
```
x[i] = available_soc[i]*y
```
We can compute `y` from equation above (2):
``` python
```
sum(capacity_ratio[i] * x[i] for i in range(N)) == 1
# =>
sum(capacity_ratio[i] * available_soc[i] * y for i in range(N)) == 1
Expand All @@ -100,7 +100,7 @@ class DistributionAlgorithm:
Now we know everything and we can compute distribution:
``` python
```
distribution[i] = power_w * capacity_ratio[i] * x[i] # from (1)
distribution[i] = \
power_w * capacity_ratio[i] * available_soc[i] * y # from (2)
Expand All @@ -110,13 +110,13 @@ class DistributionAlgorithm:
Let:
``` python
```
battery_availability_ratio[i] = capacity_ratio[i] * available_soc[i]
total_battery_availability_ratio = sum(battery_availability_ratio)
```
Then:
``` python
```
distribution[i] = power_w * battery_availability_ratio[i] \
/ total_battery_availability_ratio
```
Expand Down Expand Up @@ -151,29 +151,33 @@ def __init__(self, distributor_exponent: float = 1) -> None:
If `distribution_exponent` is:
* `0`: distribution for each battery will be the equal.
``` python
Bat1.distribution = 4000; Bat2.distribution = 4000
```python
BAT1_DISTRIBUTION = 4000
BAT2_DISTRIBUTION = 4000
```
* `1`: then `Bat2` will have 3x more power assigned then `Bat1`.
``` python
10 * x + 30 * x = 8000
x = 200
Bat1.distribution = 2000; Bat2.distribution = 6000
```python
# 10 * x + 30 * x = 8000
X = 200
BAT1_DISTRIBUTION = 2000
BAT2_DISTRIBUTION = 6000
```
* `2`: then `Bat2` will have 9x more power assigned then `Bat1`.
``` python
10^2 * x + 30^2 * x = 8000
x = 80
Bat1.distribution = 800; Bat2.distribution = 7200
```python
# 10^2 * x + 30^2 * x = 8000
X = 80
BAT1_DISTRIBUTION = 800
BAT2_DISTRIBUTION = 7200
```
* `3`: then `Bat2` will have 27x more power assigned then `Bat1`.
``` python
10^3 * x + 30^3 * x = 8000
x = 0,285714286
Bat1.distribution = 285; Bat2.distribution = 7715
```python
# 10^3 * x + 30^3 * x = 8000
X = 0.285714286
BAT1_DISTRIBUTION = 285
BAT2_DISTRIBUTION = 7715
```
# Example 2
Expand All @@ -189,29 +193,33 @@ def __init__(self, distributor_exponent: float = 1) -> None:
If `distribution_exponent` is:
* `0`: distribution for each battery will be the same.
``` python
Bat1.distribution = 4500; Bat2.distribution = 450
```python
BAT1_DISTRIBUTION = 4500
BAT2_DISTRIBUTION = 450
```
* `1`: then `Bat2` will have 2x more power assigned then `Bat1`.
``` python
30 * x + 60 * x = 900
x = 100
Bat1.distribution = 300; Bat2.distribution = 600
```python
# 30 * x + 60 * x = 900
X = 100
BAT1_DISTRIBUTION = 300
BAT2_DISTRIBUTION = 600
```
* `2`: then `Bat2` will have 4x more power assigned then `Bat1`.
``` python
30^2 * x + 60^2 * x = 900
x = 0.2
Bat1.distribution = 180; Bat2.distribution = 720
```python
# 30^2 * x + 60^2 * x = 900
X = 0.2
BAT1_DISTRIBUTION = 180
BAT2_DISTRIBUTION = 720
```
* `3`: then `Bat2` will have 8x more power assigned then `Bat1`.
``` python
30^3 * x + 60^3 * x = 900
x = 0,003703704
Bat1.distribution = 100; Bat2.distribution = 800
```python
# 30^3 * x + 60^3 * x = 900
X = 0.003703704
BAT1_DISTRIBUTION = 100
BAT2_DISTRIBUTION = 800
```
# Example 3
Expand All @@ -226,15 +234,17 @@ def __init__(self, distributor_exponent: float = 1) -> None:
If `distribution_exponent` is:
* `0`: distribution for each battery will be the equal.
``` python
Bat1.distribution = 450; Bat2.distribution = 450
```python
BAT1_DISTRIBUTION = 450
BAT2_DISTRIBUTION = 450
```
* `0.5`: then `Bat2` will have 6/4x more power assigned then `Bat1`.
``` python
sqrt(36) * x + sqrt(16) * x = 900
x = 100
Bat1.distribution = 600; Bat2.distribution = 400
```python
# sqrt(36) * x + sqrt(16) * x = 900
X = 100
BAT1_DISTRIBUTION = 600
BAT2_DISTRIBUTION = 400
```
Raises:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,13 @@ class FormulaBuilder:
following calls need to be made:
```python
builder = FormulaBuilder()
builder.push_metric("metric_1", receiver_1)
channel = Broadcast[Sample]("channel")
receiver_1 = channel.new_receiver("receiver_1")
receiver_2 = channel.new_receiver("receiver_2")
builder = FormulaBuilder("addition")
builder.push_metric("metric_1", receiver_1, nones_are_zeros=True)
builder.push_oper("+")
builder.push_metric("metric_2", receiver_2)
builder.push_metric("metric_2", receiver_2, nones_are_zeros=True)
engine = builder.build()
```
Expand Down
Loading

0 comments on commit 6c94354

Please sign in to comment.