diff --git a/pyproject.toml b/pyproject.toml index 4d8bdb3..720fb7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,9 @@ dependencies = [ "PySide6>=6.7,<7", ] +[project.scripts] +bedit-graphviz = "bedit_util.graphviz:main" + [project.optional-dependencies] dev = [ "pytest>=8", diff --git a/tests/manual_test.py b/tests/manual_test.py new file mode 100644 index 0000000..df982fa --- /dev/null +++ b/tests/manual_test.py @@ -0,0 +1,12 @@ +import sys +from bedit_core.serialization import load, save +from bedit_core.bondgraph import causality_inference + +def main(path: str): + doc = load(path) + for id, root in doc.root.items(): + doc.root[id] = causality_inference(doc.root[id]) + save(doc, path) + +if __name__ == "__main__": + main(sys.argv[1]) \ No newline at end of file diff --git a/tests/unit/test_bondgraph_flatten.py b/tests/unit/test_bondgraph_flatten.py new file mode 100644 index 0000000..4f56b0b --- /dev/null +++ b/tests/unit/test_bondgraph_flatten.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +import pytest + +from bedit_core.bondgraph import flatten_bondgraph +from bedit_core.models import ( + BondConnection, + BondPort, + Component, + ComponentID, + ConnectionID, + EquationImplementation, + Graph, + GraphImplementation, + Interface, + PortID, + SignalDirection, +) + + +def _leaf(name: str, port_id: PortID) -> Component: + return Component( + name=name, + interface=Interface( + {port_id: BondPort(name="p", direction=SignalDirection.INPUT)} + ), + parameters={}, + implementation=EquationImplementation(), + ) + + +@pytest.mark.unit +def test_flattens_bonds_across_a_component_boundary() -> None: + boundary_id = PortID("boundary") + inner_id = PortID("inner") + outer_id = PortID("outer") + inner = _leaf("Inner", inner_id) + subsystem = Component( + name="Subsystem", + interface=Interface( + { + boundary_id: BondPort( + name="boundary", + direction=SignalDirection.INPUT, + ) + } + ), + parameters={}, + implementation=GraphImplementation( + Graph( + components={ComponentID("inner"): inner}, + connections={ + ConnectionID("inside"): BondConnection( + boundary_id, + inner_id, + ) + }, + ) + ), + ) + outer = _leaf("Outer", outer_id) + root = Component( + name="Root", + interface=Interface(), + parameters={}, + implementation=GraphImplementation( + Graph( + components={ + ComponentID("subsystem"): subsystem, + ComponentID("outer"): outer, + }, + connections={ + ConnectionID("outside"): BondConnection( + outer_id, + boundary_id, + ) + }, + ) + ), + ) + + network = flatten_bondgraph(root) + boundary = next(port for port in network.ports if port.port_id == boundary_id) + + assert len(network.components) == 4 + assert len(network.ports) == 3 + assert len(network.bonds) == 2 + assert len(network.bonds_for(boundary)) == 2 diff --git a/tests/unit/test_graphviz_util.py b/tests/unit/test_graphviz_util.py new file mode 100644 index 0000000..5e3f11c --- /dev/null +++ b/tests/unit/test_graphviz_util.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +import pytest + +from bedit_core.models import Document +from bedit_util.graphviz import document_to_dot + + +@pytest.mark.unit +def test_dot_contains_component_name_path(minimal_document: Document) -> None: + dot = document_to_dot(minimal_document, "Root") + + assert 'label="Root"' in dot + assert 'xlabel="Root"' in dot diff --git a/untitled.bedit.json b/untitled.bedit.json index 331db9f..e8d9957 100644 --- a/untitled.bedit.json +++ b/untitled.bedit.json @@ -23,7 +23,10 @@ "name": "p", "direction": "input", "multiplicity": false, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "domain": "power", "causality_preference": "preferred_effort_out" @@ -33,7 +36,10 @@ "name": "state", "direction": "output", "multiplicity": false, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "value_type": "real", "quantity": "", @@ -54,8 +60,13 @@ "implementation": { "implementation_type": "equation", "declarations": [], - "initial_equations": ["state = 0;"], - "equations": ["der(state) = p.f;", "p.e = state/c;"] + "initial_equations": [ + "state = 0;" + ], + "equations": [ + "der(state) = p.f;", + "p.e = state/c;" + ] } }, "9ee2f42b-5ed1-446d-8790-c2a1df6b61d3": { @@ -67,10 +78,13 @@ "name": "p", "direction": "input", "multiplicity": true, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "domain": "power", - "causality_preference": "indifferent" + "causality_preference": "single_flow_in" } } }, @@ -100,7 +114,10 @@ "name": "p", "direction": "input", "multiplicity": false, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "domain": "power", "causality_preference": "indifferent" @@ -121,7 +138,9 @@ "implementation_type": "equation", "declarations": [], "initial_equations": [], - "equations": ["p.e = r*p.f;"] + "equations": [ + "p.e = r*p.f;" + ] } }, "2804f2f1-6123-4a53-aff1-87a3a8202711": { @@ -133,7 +152,10 @@ "name": "p", "direction": "output", "multiplicity": false, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "domain": "power", "causality_preference": "fixed_effort_out" @@ -152,9 +174,14 @@ }, "implementation": { "implementation_type": "equation", - "declarations": ["Real f;"], + "declarations": [ + "Real f;" + ], "initial_equations": [], - "equations": ["p.e = e;", "f = p.f;"] + "equations": [ + "p.e = e;", + "f = p.f;" + ] } }, "9f392c28-6bef-4b10-9305-93e250747007": { @@ -166,17 +193,22 @@ "name": "p", "direction": "input", "multiplicity": true, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "domain": "power", - "causality_preference": "indifferent" + "causality_preference": "single_effort_in" } } }, "parameters": {}, "implementation": { "implementation_type": "equation", - "declarations": ["Real e;"], + "declarations": [ + "Real e;" + ], "initial_equations": [], "equations": [ "e = p[1].e;", @@ -196,7 +228,10 @@ "name": "p", "direction": "input", "multiplicity": false, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "domain": "power", "causality_preference": "indifferent" @@ -217,7 +252,9 @@ "implementation_type": "equation", "declarations": [], "initial_equations": [], - "equations": ["p.e = r*p.f;"] + "equations": [ + "p.e = r*p.f;" + ] } }, "033b930e-bf79-403a-8b0b-a159f3c81ce9": { @@ -229,7 +266,10 @@ "name": "p", "direction": "input", "multiplicity": false, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "domain": "power", "causality_preference": "preferred_flow_out" @@ -239,7 +279,10 @@ "name": "state", "direction": "output", "multiplicity": false, - "matrix_size": [1, 1], + "matrix_size": [ + 1, + 1 + ], "description": "", "value_type": "real", "quantity": "", @@ -260,8 +303,13 @@ "implementation": { "implementation_type": "equation", "declarations": [], - "initial_equations": ["state = 0;"], - "equations": ["der(state) = p.e;", "p.f = state/i;"] + "initial_equations": [ + "state = 0;" + ], + "equations": [ + "der(state) = p.e;", + "p.f = state/i;" + ] } } }, @@ -270,42 +318,42 @@ "connection_type": "bond", "source": "5b6a8a0c-0875-402c-bd74-11d086aac372", "target": "4306701a-6b1b-4d19-b8ff-45dbfa04f2d3", - "causality": "flow_out", + "causality": "effort_out", "undesired": false }, "66414b62-ca76-4be6-8da4-72551f8ffc0e": { "connection_type": "bond", "source": "497b1f74-1186-471f-976a-36b07a451caf", "target": "4a3f69f2-b305-4305-92e3-3998634ff226", - "causality": "effort_out", + "causality": "flow_out", "undesired": false }, "8aaa96b3-45ed-4674-8f7a-06b28178a5c8": { "connection_type": "bond", "source": "4306701a-6b1b-4d19-b8ff-45dbfa04f2d3", "target": "c87881f1-23b4-4a69-b586-8e3c7bf6e21e", - "causality": "effort_out", + "causality": "flow_out", "undesired": false }, "ff606583-d31f-4638-93f7-926163db2665": { "connection_type": "bond", "source": "4306701a-6b1b-4d19-b8ff-45dbfa04f2d3", "target": "0ac7d4ea-77f7-4c0d-8e37-406ef66e4740", - "causality": "flow_out", + "causality": "effort_out", "undesired": false }, "1f52dc5a-1847-4982-aabc-2538cf99a86f": { "connection_type": "bond", "source": "497b1f74-1186-471f-976a-36b07a451caf", "target": "0b9036b1-e4e4-437e-8c35-f5bb391b6cbd", - "causality": "flow_out", + "causality": "effort_out", "undesired": false }, "5af42b61-2317-48a8-b32b-0e2dfd88368e": { "connection_type": "bond", "source": "4306701a-6b1b-4d19-b8ff-45dbfa04f2d3", "target": "497b1f74-1186-471f-976a-36b07a451caf", - "causality": "effort_out", + "causality": "flow_out", "undesired": false } }