Skip to content

Commit

Permalink
fix: flow diagram generation
Browse files Browse the repository at this point in the history
The fix simplifies the diagram. Details below fuel and electricity
consumers will no longer available. It also assumes no changes over
time.
  • Loading branch information
jsolaas committed Nov 22, 2024
1 parent c463ba8 commit ff06d8e
Show file tree
Hide file tree
Showing 12 changed files with 322 additions and 560 deletions.
3 changes: 1 addition & 2 deletions src/ecalc_cli/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ def run(

if flow_diagram:
write_flow_diagram(
model_dto=model.dto,
result_options=model.result_options,
energy_model=model,
output_folder=output_folder,
name_prefix=name_prefix,
)
Expand Down
16 changes: 7 additions & 9 deletions src/ecalc_cli/io/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
from libecalc.application.graph_result import GraphResult
from libecalc.common.run_info import RunInfo
from libecalc.common.time_utils import resample_periods
from libecalc.dto import Asset, ResultOptions
from libecalc.infrastructure.file_utils import OutputFormat, get_result_output
from libecalc.presentation.exporter.configs.configs import LTPConfig, STPConfig
from libecalc.presentation.exporter.configs.formatter_config import PeriodFormatterConfig
from libecalc.presentation.exporter.exporter import Exporter
from libecalc.presentation.exporter.formatters.formatter import CSVFormatter
from libecalc.presentation.exporter.handlers.handler import MultiFileHandler
from libecalc.presentation.exporter.infrastructure import ExportableGraphResult
from libecalc.presentation.flow_diagram.EcalcModelMapper import EcalcModelMapper
from libecalc.presentation.flow_diagram.EcalcModelMapper import EnergyModelFlowDiagram
from libecalc.presentation.json_result.result import EcalcModelResult as EcalcModelResultDTO
from libecalc.presentation.yaml.model import YamlModel


def write_output(output: str, output_file: Path = None):
Expand Down Expand Up @@ -168,12 +168,11 @@ def export_tsv(
exporter.export(row_based_data)


def write_flow_diagram(model_dto: Asset, result_options: ResultOptions, output_folder: Path, name_prefix: str):
def write_flow_diagram(energy_model: YamlModel, output_folder: Path, name_prefix: str):
"""Write FDE diagram to file.
Args:
model_dto: eCalc model
result_options: Result options specifying start, end and frequency
energy_model: The yaml energy model
output_folder: Desired output location of FDE diagram
name_prefix: Name of FDE diagram file
Expand All @@ -183,10 +182,9 @@ def write_flow_diagram(model_dto: Asset, result_options: ResultOptions, output_f
EcalcCLIError: If a OSError occurs during the writing of diagram to file.
"""
flow_diagram = EcalcModelMapper.from_dto_to_fde(
ecalc_model=model_dto,
result_options=result_options,
)
flow_diagram = EnergyModelFlowDiagram(
energy_model=energy_model, model_period=energy_model.variables.period
).get_energy_flow_diagram()
flow_diagram_filename = f"{name_prefix}.flow-diagram.json" if name_prefix != "" else "flow-diagram.json"
flow_diagram_path = output_folder / flow_diagram_filename
try:
Expand Down
31 changes: 31 additions & 0 deletions src/libecalc/application/energy/energy_component.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import abc

from libecalc.application.energy.component_energy_context import ComponentEnergyContext
from libecalc.application.energy.model_change_event import ModelChangeEvent
from libecalc.common.component_type import ComponentType
from libecalc.core.result import EcalcModelResult


Expand All @@ -15,5 +17,34 @@ class EnergyComponent(abc.ABC):
@abc.abstractmethod
def id(self) -> str: ...

@abc.abstractmethod
def get_component_process_type(self) -> ComponentType: ...

@abc.abstractmethod
def get_name(self) -> str: ...

@abc.abstractmethod
def get_change_events(self) -> list[ModelChangeEvent]:
"""
Get all the change events for this component
"""
...

@abc.abstractmethod
def is_provider(self) -> bool:
"""
Whether the energy component provides energy to other energy components.
"""
...

@abc.abstractmethod
def is_container(self) -> bool:
"""
Whether the energy component is a container for other energy components.
"""
...


class EvaluatableEnergyComponent(EnergyComponent, abc.ABC):
@abc.abstractmethod
def evaluate_energy_usage(self, energy_context: ComponentEnergyContext) -> EcalcModelResult: ...
4 changes: 2 additions & 2 deletions src/libecalc/application/energy/energy_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ def get_regularity(self, component_id: str) -> dict[datetime, Expression]:
...

@abc.abstractmethod
def get_consumers(self, provider_id: str) -> list[EnergyComponent]:
def get_consumers(self, provider_id: str = None) -> list[EnergyComponent]:
"""
Get consumers of the given provider
Get consumers of the given provider. If no provider is given, assume top-level.
"""
...

Expand Down
31 changes: 31 additions & 0 deletions src/libecalc/application/energy/model_change_event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Self

from libecalc.common.time_utils import Period


@dataclass(eq=True, frozen=True)
class ModelChangeEvent:
"""
An event that (might) change the structure of the diagram. Since dates in the model might be used to set
expressions only, the structure might not change even though there is a change event.
"""

name: str
period: Period

@property
def start(self) -> datetime:
return self.period.start

@property
def end(self) -> datetime:
return self.period.end

@classmethod
def from_period(cls, period) -> Self:
return cls(
name=str(period.start),
period=period,
)
4 changes: 3 additions & 1 deletion src/libecalc/dto/component_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
# TODO: Rename to energy graph, use composition instead of inheritance. Alternatively make YamlModel the EnergyGraph/EnergyModel and use Graph directly in YamlModel
# Currently it is practical to have the EnergyModel graph related functions here to deal with dto tests.
class ComponentGraph(Graph):
def get_consumers(self, provider_id: str) -> list[EnergyComponent]:
def get_consumers(self, provider_id: str = None) -> list[EnergyComponent]:
if provider_id is None:
provider_id = self.root
consumer_ids = self.get_successors(provider_id)
return [self.get_node(consumer_id) for consumer_id in consumer_ids]

Expand Down
Loading

0 comments on commit ff06d8e

Please sign in to comment.