From caee3411fc572700aaa368f5248d1dc3a8e216fa Mon Sep 17 00:00:00 2001 From: vargastat Date: Tue, 2 Apr 2024 18:05:32 +0200 Subject: [PATCH] resolve_components.py completed --- src/andromede/model/parsing.py | 22 ------- src/andromede/model/resolve_library.py | 47 +------------- src/andromede/{model => study}/components.py | 6 +- src/andromede/study/parsing.py | 45 +++++++++++++ src/andromede/study/resolve_components.py | 64 +++++++++++++++++++ tests/unittests/data/components.yml | 6 ++ .../model/test_components_parsing.py | 15 ----- tests/unittests/study/__init__.py | 0 .../study/test_components_parsing.py | 19 ++++++ 9 files changed, 140 insertions(+), 84 deletions(-) rename src/andromede/{model => study}/components.py (85%) create mode 100644 src/andromede/study/parsing.py create mode 100644 src/andromede/study/resolve_components.py delete mode 100644 tests/unittests/model/test_components_parsing.py create mode 100644 tests/unittests/study/__init__.py create mode 100644 tests/unittests/study/test_components_parsing.py diff --git a/src/andromede/model/parsing.py b/src/andromede/model/parsing.py index 112ab3c9..13bb713f 100644 --- a/src/andromede/model/parsing.py +++ b/src/andromede/model/parsing.py @@ -21,11 +21,6 @@ def parse_yaml_library(input: typing.TextIO) -> "InputLibrary": return InputLibrary.model_validate(tree["library"]) -def parse_yaml_components(input_components: typing.TextIO) -> "InputComponent": - tree = safe_load(input_components) - return InputComponent.model_validate(tree["study"]) - - # Design note: actual parsing and validation is delegated to pydantic models def _to_kebab(snake: str) -> str: return snake.replace("_", "-") @@ -96,23 +91,6 @@ class Config: alias_generator = _to_kebab -class InputPortConnections(BaseModel): - id: str - component1: str - port_1: str - component2: str - port_2: str - - -class InputComponent(BaseModel): - nodes: List[InputModel] = Field(default_factory=list) - components: List[InputModel] = Field(default_factory=list) - connections: List[InputPortConnections] = Field(default_factory=list) - - class Config: - alias_generator = _to_kebab - - class InputLibrary(BaseModel): id: str port_types: List[InputPortType] = Field(default_factory=list) diff --git a/src/andromede/model/resolve_library.py b/src/andromede/model/resolve_library.py index fd96ef09..a17c1789 100644 --- a/src/andromede/model/resolve_library.py +++ b/src/andromede/model/resolve_library.py @@ -9,8 +9,8 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. -from pydoc import text -from typing import Dict, List, Optional + +from typing import Dict, Optional from andromede.expression import ExpressionNode from andromede.expression.indexing_structure import IndexingStructure @@ -30,23 +30,19 @@ Variable, model, ) -from andromede.model.components import Components, components from andromede.model.library import Library, library from andromede.model.model import PortFieldDefinition, port_field_def from andromede.model.parsing import ( - InputComponent, InputConstraint, InputField, InputLibrary, InputModel, InputModelPort, InputParameter, - InputPortConnections, InputPortFieldDefinition, InputPortType, InputVariable, ) -from andromede.study import Component, PortRef, PortsConnection def resolve_library(input_lib: InputLibrary) -> Library: @@ -59,18 +55,8 @@ def resolve_library(input_lib: InputLibrary) -> Library: port_types = [_convert_port_type(p) for p in input_lib.port_types] port_types_dict = dict((p.id, p) for p in port_types) models = [_resolve_model(m, port_types_dict) for m in input_lib.models] - return library(port_types, models) - - -def resolve_components_and_cnx(input_comp: InputComponent) -> Components: - components_list = [_resolve_component(m, m.id) for m in input_comp.components] - connections = [] - for c in input_comp.connections: - resolved_cnx = _resolve_connections(c, components_list) - connections.extend(resolved_cnx) - - return components(components_list, connections) + return library(port_types, models) def _convert_field(field: InputField) -> PortField: @@ -165,30 +151,3 @@ def _to_constraint( lower_bound=_to_expression_if_present(constraint.lower_bound, identifiers), upper_bound=_to_expression_if_present(constraint.upper_bound, identifiers), ) - - -def _resolve_component(model_for_component: InputModel, component_id: str) -> Component: - return Component( - model=_resolve_model_identifier(model_for_component), id=component_id - ) - - -def _resolve_connections( - connection: InputPortConnections, - components_list: List[Component], -) -> List[PortRef]: - component1 = connection.component1 - component2 = connection.component2 - port1 = connection.port_1 - port2 = connection.port_2 - - port_ref_1 = PortRef(_get_component_by_id(components_list, component1), port1) - port_ref_2 = PortRef(_get_component_by_id(components_list, component2), port2) - return [port_ref_1, port_ref_2] - - -def _get_component_by_id( - components_list: List[Component], component_id: str -) -> Optional[Component]: - components_dict = {component.id: component for component in components_list} - return components_dict.get(component_id) diff --git a/src/andromede/model/components.py b/src/andromede/study/components.py similarity index 85% rename from src/andromede/model/components.py rename to src/andromede/study/components.py index f357bd31..af377b95 100644 --- a/src/andromede/model/components.py +++ b/src/andromede/study/components.py @@ -19,14 +19,14 @@ @dataclass(frozen=True) class Components: components: Dict[str, Component] - ports_to_connect: List[PortRef] + connections: List[PortRef] def components( components_list: Iterable[Component], - ports_to_connect: Iterable[PortRef], + connections: Iterable[PortRef], ) -> Components: return Components( components=dict((m.id, m) for m in components_list), - ports_to_connect=list(p for p in ports_to_connect), + connections=list(p for p in connections), ) diff --git a/src/andromede/study/parsing.py b/src/andromede/study/parsing.py new file mode 100644 index 00000000..dc5bbe35 --- /dev/null +++ b/src/andromede/study/parsing.py @@ -0,0 +1,45 @@ +# Copyright (c) 2024, RTE (https://www.rte-france.com) +# +# See AUTHORS.txt +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 +# +# This file is part of the Antares project. +import typing +from typing import List, Optional + +from pydantic import BaseModel, Field +from yaml import safe_load + +from andromede.model.parsing import InputModel + + +def parse_yaml_components(input_components: typing.TextIO) -> "InputComponent": + tree = safe_load(input_components) + return InputComponent.model_validate(tree["study"]) + + +# Design note: actual parsing and validation is delegated to pydantic models +def _to_kebab(snake: str) -> str: + return snake.replace("_", "-") + + +class InputPortConnections(BaseModel): + id: str + component1: str + port_1: str + component2: str + port_2: str + + +class InputComponent(BaseModel): + nodes: List[InputModel] = Field(default_factory=list) + components: List[InputModel] = Field(default_factory=list) + connections: List[InputPortConnections] = Field(default_factory=list) + + class Config: + alias_generator = _to_kebab diff --git a/src/andromede/study/resolve_components.py b/src/andromede/study/resolve_components.py new file mode 100644 index 00000000..a019cf53 --- /dev/null +++ b/src/andromede/study/resolve_components.py @@ -0,0 +1,64 @@ +# Copyright (c) 2024, RTE (https://www.rte-france.com) +# +# See AUTHORS.txt +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 +# +# This file is part of the Antares project. +from typing import List, Optional + +from andromede.model.parsing import InputModel +from andromede.model.resolve_library import _resolve_model_identifier +from andromede.study import Component, PortRef +from andromede.study.components import Components, components +from andromede.study.parsing import InputComponent, InputPortConnections + + +def resolve_components_and_cnx(input_comp: InputComponent) -> Components: + """ + Resolves: + - components to be used for study + - connections between components""" + components_list = [_resolve_component(m, m.id) for m in input_comp.components] + components_list.extend(_resolve_component(n, n.id) for n in input_comp.nodes) + connections = [] + + for cnx in input_comp.connections: + resolved_cnx = _resolve_connections(cnx, components_list) + connections.extend(resolved_cnx) + + return components(components_list, connections) + + +def _resolve_component(model_for_component: InputModel, component_id: str) -> Component: + return Component( + model=_resolve_model_identifier(model_for_component), id=component_id + ) + + +def _resolve_connections( + connection: InputPortConnections, + components_list: List[Component], +) -> List[PortRef]: + cnx_component1 = connection.component1 + cnx_component2 = connection.component2 + port1 = connection.port_1 + port2 = connection.port_2 + + component_1 = _get_component_by_id(components_list, cnx_component1) + component_2 = _get_component_by_id(components_list, cnx_component2) + assert component_1 is not None and component_2 is not None + port_ref_1 = PortRef(component_1, port1) + port_ref_2 = PortRef(component_2, port2) + return [port_ref_1, port_ref_2] + + +def _get_component_by_id( + components_list: List[Component], component_id: str +) -> Optional[Component]: + components_dict = {component.id: component for component in components_list} + return components_dict.get(component_id) diff --git a/tests/unittests/data/components.yml b/tests/unittests/data/components.yml index ee4b6afd..830d7577 100644 --- a/tests/unittests/data/components.yml +++ b/tests/unittests/data/components.yml @@ -38,5 +38,11 @@ study: component2: D port_2: injection_port + - id: cnx2 + component1: N + port_1: injection_port + component2: G + port_2: injection_port + diff --git a/tests/unittests/model/test_components_parsing.py b/tests/unittests/model/test_components_parsing.py deleted file mode 100644 index 2f2148ad..00000000 --- a/tests/unittests/model/test_components_parsing.py +++ /dev/null @@ -1,15 +0,0 @@ -from pathlib import Path - -from andromede.model.parsing import parse_yaml_components -from andromede.model.resolve_library import resolve_components_and_cnx - - -def test_parsing_components_ok(data_dir: Path): - compo_file = data_dir / "components.yml" - lib = data_dir / "lib.yml" - - with compo_file.open() as c: - input_compo = parse_yaml_components(c) - assert len(input_compo.components) == 2 - - components = resolve_components_and_cnx(input_compo) diff --git a/tests/unittests/study/__init__.py b/tests/unittests/study/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unittests/study/test_components_parsing.py b/tests/unittests/study/test_components_parsing.py new file mode 100644 index 00000000..dc1d6f06 --- /dev/null +++ b/tests/unittests/study/test_components_parsing.py @@ -0,0 +1,19 @@ +from pathlib import Path + +from andromede.study.parsing import parse_yaml_components +from andromede.study.resolve_components import resolve_components_and_cnx + + +def test_parsing_components_ok(data_dir: Path): + compo_file = data_dir / "components.yml" + + with compo_file.open() as c: + input_compo = parse_yaml_components(c) + assert len(input_compo.components) == 2 + assert len(input_compo.nodes) == 1 + assert len(input_compo.connections) == 2 + + result = resolve_components_and_cnx(input_compo) + + assert len(result.components) == 3 + assert len(result.connections) == 4