Skip to content

Commit

Permalink
Fetch PV power from PV meters, rather than PV inverters
Browse files Browse the repository at this point in the history
This is because the microgrid API currently doesn't have drivers for
reading data from PV inverters.  We have plans to update our generated
formulas to have fallback components, when a certain selection of
components is unable to stream data, tracked here: frequenz-floss#184.

Reading PV power from a meter is also going to be more accurate,
because it measures the actual power coming out of the PV inverter,
unaffected by the inverter's self consumption.

Signed-off-by: Sahas Subramanian <[email protected]>
  • Loading branch information
shsms committed May 22, 2023
1 parent c28afa1 commit bf1cef1
Showing 1 changed file with 50 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@

"""Formula generator for PV Power, from the component graph."""

from __future__ import annotations

import logging
from collections import abc

from ....microgrid import connection_manager
from ....microgrid.component import ComponentCategory, ComponentMetricId, InverterType
from .._formula_engine import FormulaEngine
from ._formula_generator import NON_EXISTING_COMPONENT_ID, FormulaGenerator, FormulaType
from ._formula_generator import (
NON_EXISTING_COMPONENT_ID,
FormulaGenerationError,
FormulaGenerator,
FormulaType,
)

_logger = logging.getLogger(__name__)

Expand All @@ -27,15 +35,8 @@ def generate(self) -> FormulaEngine:
"""
builder = self._get_builder("pv-power", ComponentMetricId.ACTIVE_POWER)

component_graph = connection_manager.get().component_graph
pv_inverters = list(
comp
for comp in component_graph.components()
if comp.category == ComponentCategory.INVERTER
and comp.type == InverterType.SOLAR
)

if not pv_inverters:
pv_meters = self._get_pv_meters()
if not pv_meters:
_logger.warning(
"Unable to find any PV inverters in the component graph. "
"Subscribing to the resampling actor with a non-existing "
Expand All @@ -51,11 +52,11 @@ def generate(self) -> FormulaEngine:

builder.push_oper("(")
builder.push_oper("(")
for idx, comp in enumerate(pv_inverters):
for idx, comp_id in enumerate(pv_meters):
if idx > 0:
builder.push_oper("+")

builder.push_component_metric(comp.component_id, nones_are_zeros=True)
builder.push_component_metric(comp_id, nones_are_zeros=True)
builder.push_oper(")")
if self._config.formula_type == FormulaType.PRODUCTION:
builder.push_oper("*")
Expand All @@ -66,3 +67,40 @@ def generate(self) -> FormulaEngine:
builder.push_clipper(0.0, None)

return builder.build()

def _get_pv_meters(self) -> abc.Set[int]:
component_graph = connection_manager.get().component_graph

pv_inverters = list(
comp
for comp in component_graph.components()
if comp.category == ComponentCategory.INVERTER
and comp.type == InverterType.SOLAR
)
pv_meters: set[int] = set()

if not pv_inverters:
return pv_meters

for pv_inverter in pv_inverters:
predecessors = component_graph.predecessors(pv_inverter.component_id)
if len(predecessors) != 1:
raise FormulaGenerationError(
"Expected exactly one predecessor for PV inverter "
f"{pv_inverter.component_id}, but found {len(predecessors)}."
)
meter = next(iter(predecessors))
if meter.category != ComponentCategory.METER:
raise FormulaGenerationError(
f"Expected predecessor of PV inverter {pv_inverter.component_id} "
f"to be a meter, but found {meter.category}."
)
meter_successors = component_graph.successors(meter.component_id)
if len(meter_successors) != 1:
raise FormulaGenerationError(
f"Expected exactly one successor for meter {meter.component_id}"
f", connected to PV inverter {pv_inverter.component_id}"
f", but found {len(meter_successors)}."
)
pv_meters.add(meter.component_id)
return pv_meters

0 comments on commit bf1cef1

Please sign in to comment.