Skip to content

Conversation

@shsms
Copy link
Contributor

@shsms shsms commented Mar 21, 2024

This PR adds new propose_power and power_status methods to the EVChargerPool similar to the BatteryPool. These method interface with the PowerManager and PowerDistributor, which currently uses a first-come-first-serve algorithm to distribute power to EVs.

The following changes were made:

  • An EVChargerStatusTracker implementation which tracks the health of an EV charger and whether an EV is connected.
  • PowerDistributor and PowerManager are now modular to support multiple component types.
  • Add an EVChargerManager that plugs into the PowerDistributor
  • Connect the PowerDistributor and PowerManager to the EVChargerPool.

Fixes #872.

@shsms shsms requested a review from a team as a code owner March 21, 2024 16:41
@shsms shsms requested a review from llucax March 21, 2024 16:41
@github-actions github-actions bot added part:docs Affects the documentation part:tests Affects the unit, integration and performance (benchmarks) tests part:data-pipeline Affects the data pipeline part:actor Affects an actor ot the actors utilities (decorator, etc.) part:microgrid Affects the interactions with the microgrid labels Mar 21, 2024

# Send initial status
await self._status_sender.send(
ComponentStatus(self._component_id, self._last_status)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this means we will always send the default NOT_WORKING initially before correcting it no? Why not make default None and don't send any initial status, just let the loop update it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worst case, the timer will fire and send the update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but that would require some amount of refactoring, because the timer trigger would send something out only if the status has changed. But I'm not sure that it should be changed.

This is an internal feature, and its values are used by the power distributor and the system bounds tracker. So having it like this in practice would mean that PowerManager can report zero bounds right away on start up, until data is available.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but that would require some amount of refactoring, because the timer trigger would send something out only if the status has changed

But something has changed if _last_status is None as that is different from the new_status which is set to not working

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used as a hint to the power manager and *pool metrics whether a battery is usable or not. We need to send something out right at the beginning, else they wait for a status to arrive, before streaming values out.

So changing this would mean changing all the users of this data, which is more than a few.

Sending the NOT_WORKING flag even though we don't know if it is working or not is I'm guessing the objection? Maybe renaming it to NOT_USABLE would make it more general, and can be sent before we receive any data, or when missing data, or when the component is known to be not working?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the battery pool too, it doesn't seem to say "NOT_WORKING" at startup, just:

INFO:frequenz.sdk.actor.power_distributing._component_status._battery_status_tracker:battery 1001 changed status ComponentStatusEnum.WORKING
INFO:frequenz.sdk.actor.power_distributing._component_status._battery_status_tracker:battery 1004 changed status ComponentStatusEnum.WORKING

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without having seen the changes yet, and not being very familiar with how this is used (so high risk of saying nonsense), how about INITIALIZING instead of UNKNOWN, if we can use a special state for this initialization step?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess related to this, reading the commit message:

It reports an EV charger as WORKING when an EV is connected to it,
and power can be allocated to it, and NOT_WORKING otherwise.

I guess this status comes from batteries, right? Maybe it would be better to rename WORKING/NOT_WORKING to USABLE/UNUSABLE or AVAILABLE/UNAVAILABLE, it looks a bit misleading to say a EV charger is "not working" just because an EV is not connected to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could also be because of EV charger in error state. Then someone might have to go and reset.

This used for telling the Power Distributor whether power can be allocated to a component or not. And we need alternatives for WORKING, NOT_WORKING, UNCERTAIN that would be suitable for batteries, EV chargers, PV inverters, CHP, etc.

Maybe USABLE/NOT_USABLE is the way to go. UNUSABLE is not distinct enough for my eyes, I might mix things up, especially when reading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But would keep for a separate PR: #903


@override
async def stop(self) -> None:
"""Stop the ev charger data manager."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no stopping the data manager?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, this is supposed to stop the component pool status tracker. I've added a fixup commit.

)
await self._results_sender.send(result)

async def _set_api_power(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No method documentation?

@shsms
Copy link
Contributor Author

shsms commented Mar 25, 2024

I've added some fixup commits with the missing docstrings, so they're easy to review. Will squash them before merging.

!!! note
When specifying priority, bigger values indicate higher priority. The default
priority is the lowest possible value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the priority is equal? Which one will then be selected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are multiple proposals with the same priority, they will be treated as having different priorities based on their randomly generated source_id. So the priority between actors will be stable and they won't get into a race with each other when they have the same priority values.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a short form or hint of htat to the docu here :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do as part of this issue: #902

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small side note, in docstrings you can use normal sections for admonitions:

Note:
    When specifying priority, bigger values indicate higher priority. The default ...

You only need the "special" syntax if you need some customization (like if you don't want it to be collapsible or to show it on the side for example).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in #904

@Marenz
Copy link
Contributor

Marenz commented Mar 25, 2024

(more for myself) I have currently reviewed up to Add a propose_power method to EVChargerPool

@llucax
Copy link
Contributor

llucax commented Mar 27, 2024

FYI, starting a review.

Copy link
Contributor

@llucax llucax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the comments (most minor), really nice job!


@property
def bounds(self) -> Bounds[Power] | None:
"""The usable bounds for the batteries.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""The usable bounds for the batteries.
"""The usable bounds for the EV chargers.

priorities.
There might be exclusion zones within these bounds. If necessary, the
[`adjust_to_bounds`][frequenz.sdk.timeseries.battery_pool.BatteryPoolReport.adjust_to_bounds]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this adjust_to_bounds should be present in the EVChargerPoolReport and this should be fixed to reference that instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if we need it for EV chargers, because the bounds are likely to be simple, and there won't be exclusion bounds for EV chargers.

Also @cwasicki was looking for ways to make sure we don't send out zero power repeatedly, and I was wondering if adjust_to_bounds is a good way to achieve that. We'll have to do some brainstorming probably next week, and maybe change the adjust_to_bounds interface in the BatteryPool too.

For now, I will remove these comments.

@shsms shsms requested review from Marenz and llucax April 2, 2024 08:15
@shsms
Copy link
Contributor Author

shsms commented Apr 2, 2024

@llucax @Marenz can I squash the fixup commits?

Copy link
Contributor

@llucax llucax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! @Marenz is supposed to be back tomorrow, maybe we can wait for his OK too.

@Marenz
Copy link
Contributor

Marenz commented Apr 3, 2024

LGTM as well

shsms and others added 17 commits April 3, 2024 10:41
It reports an EV charger as `WORKING` when an EV is connected to it,
and power can be allocated to it, and `NOT_WORKING` otherwise.

If it receives a power assignment failure from the PowerDistributor,
when the component is expected to be working, it is marked as
`UNCERTAIN` for a specific interval, before being marked `WORKING`
again.

This status would be used in the PowerDistributor and in the
EVChargerPool.

Signed-off-by: Sahas Subramanian <[email protected]>
Until now, the PowerDistributingActor has supported only distributing
power to batteries.  Batteries are (supposed to be) always available,
so the distribution takes place once when the power distributor
receives a request.

This changes when distributing power to EV chargers, because power
needs to be:

- distributed when an EV is connected
- redistributed to other EVs when an EV is disconnected
- redistributed to other EVs if an EV appears to be not charging, etc.

So a power distribution manager for EVs would be best implemented with
its own `select` loop, where distribution happens.

Because of this, a call to the `distribute_power` method on the EV
charger manager can't immediately return a single distribution result,
but that call is not the only thing that triggers the distribution,
but other events too.

So the manager needs to be what sends out the distribution results.

Signed-off-by: Sahas Subramanian <[email protected]>
This is exposed through the private `_system_power_bounds` method in
the EVChargerPool.

Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
This allows the efficient creation of multiple `EVChargerPool`
instances for the same set of EV chargers, but with different
priorities, while reusing the formulas and bounds by having only a
single instance of the `EVChargerPoolReferenceStore` class for any
given set of EV chargers.

Signed-off-by: Sahas Subramanian <[email protected]>
Additional tests for EV charger pool control methods can go in that
directory as well.

Signed-off-by: Sahas Subramanian <[email protected]>
Also remove the default value from the PowerDistributor constructor,
which allows the value to be mocked/overridden before a
PowerDistributor instance is created.

Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
Co-authored-by: Leandro Lucarella <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
@shsms shsms force-pushed the ev-charger-pool branch from 72e0575 to 08c514e Compare April 3, 2024 08:44
@shsms
Copy link
Contributor Author

shsms commented Apr 3, 2024

I've applied the fixups.

@shsms shsms added this pull request to the merge queue Apr 3, 2024
Merged via the queue into frequenz-floss:v1.x.x with commit df875ee Apr 3, 2024
@shsms shsms deleted the ev-charger-pool branch April 3, 2024 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

part:actor Affects an actor ot the actors utilities (decorator, etc.) part:data-pipeline Affects the data pipeline part:docs Affects the documentation part:microgrid Affects the interactions with the microgrid part:tests Affects the unit, integration and performance (benchmarks) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EV Charger Pool

3 participants