commit 7e99669b310bf502b572e67105a45d805e0a38eb Author: Joppe Blondel Date: Thu Jul 23 14:24:53 2026 +0200 Dataclasses for the data representation diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d03479 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.venv/ +__pycache__/ +*.py[cod] +*.egg-info/ +build/ +dist/ +.pytest_cache/ +.vscode/* +!.vscode/tasks.json +.ruff_cache/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4d8bdb3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[build-system] +requires = ["setuptools>=69"] +build-backend = "setuptools.build_meta" + +[project] +name = "bedit" +version = "0.2.0" +description = "A Bondgraph and block scheme simulator" +readme = "README.md" +requires-python = ">=3.10" +dependencies = [ + "msgpack>=1.0,<2", + "PySide6>=6.7,<7", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8", + "ruff>=0.5", +] + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +addopts = "--strict-config --strict-markers -ra" +testpaths = ["tests"] +markers = [ + "unit: fast tests without GUI event-loop interaction", + "gui: tests that create or interact with Qt objects", +] diff --git a/src/bedit_core/models.py b/src/bedit_core/models.py new file mode 100644 index 0000000..aa169d2 --- /dev/null +++ b/src/bedit_core/models.py @@ -0,0 +1,124 @@ +from __future__ import annotations +from dataclasses import dataclass, field +from enum import Enum +from typing import Any, Annotated +from uuid import uuid4 + +@dataclass(frozen=True, slots=True) +class ID: + value: str = field(default_factory=lambda: str(uuid4())) + + def __post_init__(self) -> None: + if not isinstance(self.value, str): + raise TypeError("ID value must be a string") + if not self.value: + raise ValueError("ID value cannot be empty") + + def __str__(self) -> str: + return self.value + +class ComponentID(ID): + pass + +class ConnectionID(ID): + pass + +class ParameterID(ID): + pass + +class PortID(ID): + pass + +class SignalDirection(Enum): + INPUT = "input" + OUTPUT = "output" + +class BondCausality(Enum): + NONE = "none" + EFFORT_OUT = "effort_out" + FLOW_OUT = "flow_out" + +class PortCausality(Enum): + INDIFFERENT = "indifferent" + FIXED_EFFORT_OUT = "fixed_effort_out" + FIXED_FLOW_OUT = "fixed_flow_out" + PREFERRED_EFFORT_OUT = "preferred_effort_out" + PREFERRED_FLOW_OUT = "preferred_flow_out" + LIKES_EFFORT_OUT = "likes_effort_out" + LIKES_FLOW_OUT = "likes_flow_out" + +@dataclass +class Document: + format_version: int + id: ID + name: str + root: dict[ComponentID, Component] + metadata: dict[str, Any] | None = None + +@dataclass +class Component: + name: str + interface: Interface + parameters: dict[ParameterID, Parameter] + implementation: GraphImplementation | EquationImplementation + +@dataclass +class Interface: + ports: dict[PortID, Port] = field(default_factory=dict) + +@dataclass +class Port: + name: str + direction: SignalDirection + multiplicity: bool = False + matrix_size: Annotated[list[int], 2] = field(default_factory=lambda: [1, 1]) + description: str | None = None + +@dataclass +class SignalPort(Port): + value_type: str = "Real" + quantity: str | None = None + unit: str | None = None + +@dataclass +class BondPort(Port): + domain: str = "" + causality_preference: PortCausality = PortCausality.INDIFFERENT + +@dataclass +class Parameter: + name: str + value: Any = 1.0 + value_type: str = "Real" + quantity: str | None = None + unit: str | None = None + description: str | None = None + +@dataclass +class GraphImplementation: + graph: Graph + +@dataclass +class EquationImplementation: + declarations: list[str] = field(default_factory=list) + initial_equations: list[str] = field(default_factory=list) + equations: list[str] = field(default_factory=list) + +@dataclass +class Graph: + components: dict[ComponentID, Component] = field(default_factory=dict) + connections: dict[ConnectionID, Connection] = field(default_factory=dict) + +@dataclass +class Connection: + source: PortID + target: PortID + +@dataclass +class SignalConnection(Connection): + pass + +@dataclass +class BondConnection(Connection): + causality: BondCausality = BondCausality.NONE + undesired: bool = False diff --git a/untitled.bedit.json b/untitled.bedit.json new file mode 100644 index 0000000..331db9f --- /dev/null +++ b/untitled.bedit.json @@ -0,0 +1,317 @@ +{ + "file_format_version": 1, + "format_version": 1, + "id": "3b6780c7-488b-471e-a784-392db7632090", + "name": "Current Document", + "root": { + "50e6ef97-f686-4400-bc01-e5a352e8cc22": { + "name": "New Graph Block 1", + "interface": { + "ports": {} + }, + "parameters": {}, + "implementation": { + "implementation_type": "graph", + "graph": { + "components": { + "43b3aee6-0b38-429b-9c6d-d38fc097297f": { + "name": "C", + "interface": { + "ports": { + "c87881f1-23b4-4a69-b586-8e3c7bf6e21e": { + "port_type": "bond", + "name": "p", + "direction": "input", + "multiplicity": false, + "matrix_size": [1, 1], + "description": "", + "domain": "power", + "causality_preference": "preferred_effort_out" + }, + "c62de8eb-e13b-4849-bea5-c8cc5332269e": { + "port_type": "signal", + "name": "state", + "direction": "output", + "multiplicity": false, + "matrix_size": [1, 1], + "description": "", + "value_type": "real", + "quantity": "", + "unit": "" + } + } + }, + "parameters": { + "parameter-d056bc27": { + "name": "c", + "value": "1", + "value_type": "Real", + "quantity": null, + "unit": null, + "description": null + } + }, + "implementation": { + "implementation_type": "equation", + "declarations": [], + "initial_equations": ["state = 0;"], + "equations": ["der(state) = p.f;", "p.e = state/c;"] + } + }, + "9ee2f42b-5ed1-446d-8790-c2a1df6b61d3": { + "name": "j1", + "interface": { + "ports": { + "4306701a-6b1b-4d19-b8ff-45dbfa04f2d3": { + "port_type": "bond", + "name": "p", + "direction": "input", + "multiplicity": true, + "matrix_size": [1, 1], + "description": "", + "domain": "power", + "causality_preference": "indifferent" + } + } + }, + "parameters": {}, + "implementation": { + "implementation_type": "equation", + "declarations": [ + "parameter Real s[$p_N$] = $p_S$;", + "Real f;" + ], + "initial_equations": [], + "equations": [ + "f = s[1] * p[1].f;", + "sum(s[i] * p[i].e for i in 1:$p_N$) = 0;", + "for i in 2:$p_N$ loop", + " s[i] * p[i].f = s[i-1] * p[i-1].f;", + "end for;" + ] + } + }, + "5a8b2e8d-489f-467b-8282-7e344dfad576": { + "name": "R", + "interface": { + "ports": { + "0ac7d4ea-77f7-4c0d-8e37-406ef66e4740": { + "port_type": "bond", + "name": "p", + "direction": "input", + "multiplicity": false, + "matrix_size": [1, 1], + "description": "", + "domain": "power", + "causality_preference": "indifferent" + } + } + }, + "parameters": { + "parameter-d056bc27": { + "name": "r", + "value": "1", + "value_type": "Real", + "quantity": null, + "unit": null, + "description": null + } + }, + "implementation": { + "implementation_type": "equation", + "declarations": [], + "initial_equations": [], + "equations": ["p.e = r*p.f;"] + } + }, + "2804f2f1-6123-4a53-aff1-87a3a8202711": { + "name": "Se", + "interface": { + "ports": { + "5b6a8a0c-0875-402c-bd74-11d086aac372": { + "port_type": "bond", + "name": "p", + "direction": "output", + "multiplicity": false, + "matrix_size": [1, 1], + "description": "", + "domain": "power", + "causality_preference": "fixed_effort_out" + } + } + }, + "parameters": { + "parameter-d056bc27": { + "name": "e", + "value": "1", + "value_type": "Real", + "quantity": null, + "unit": null, + "description": null + } + }, + "implementation": { + "implementation_type": "equation", + "declarations": ["Real f;"], + "initial_equations": [], + "equations": ["p.e = e;", "f = p.f;"] + } + }, + "9f392c28-6bef-4b10-9305-93e250747007": { + "name": "j0", + "interface": { + "ports": { + "497b1f74-1186-471f-976a-36b07a451caf": { + "port_type": "bond", + "name": "p", + "direction": "input", + "multiplicity": true, + "matrix_size": [1, 1], + "description": "", + "domain": "power", + "causality_preference": "indifferent" + } + } + }, + "parameters": {}, + "implementation": { + "implementation_type": "equation", + "declarations": ["Real e;"], + "initial_equations": [], + "equations": [ + "e = p[1].e;", + "sum(p[i].f for i in 1:$p_N$) = 0;", + "for i in 2:$p_N$ loop", + " p[i].e = p[i-1].e;", + "end for;" + ] + } + }, + "77701f68-b14c-4b98-8929-c5fba0261962": { + "name": "R0", + "interface": { + "ports": { + "4a3f69f2-b305-4305-92e3-3998634ff226": { + "port_type": "bond", + "name": "p", + "direction": "input", + "multiplicity": false, + "matrix_size": [1, 1], + "description": "", + "domain": "power", + "causality_preference": "indifferent" + } + } + }, + "parameters": { + "parameter-d056bc27": { + "name": "r", + "value": "10", + "value_type": "Real", + "quantity": null, + "unit": null, + "description": null + } + }, + "implementation": { + "implementation_type": "equation", + "declarations": [], + "initial_equations": [], + "equations": ["p.e = r*p.f;"] + } + }, + "033b930e-bf79-403a-8b0b-a159f3c81ce9": { + "name": "I", + "interface": { + "ports": { + "0b9036b1-e4e4-437e-8c35-f5bb391b6cbd": { + "port_type": "bond", + "name": "p", + "direction": "input", + "multiplicity": false, + "matrix_size": [1, 1], + "description": "", + "domain": "power", + "causality_preference": "preferred_flow_out" + }, + "fb25f35d-4c0c-4cfa-92d4-1a18a71e2c11": { + "port_type": "signal", + "name": "state", + "direction": "output", + "multiplicity": false, + "matrix_size": [1, 1], + "description": "", + "value_type": "real", + "quantity": "", + "unit": "" + } + } + }, + "parameters": { + "parameter-d056bc27": { + "name": "i", + "value": "1", + "value_type": "Real", + "quantity": null, + "unit": null, + "description": null + } + }, + "implementation": { + "implementation_type": "equation", + "declarations": [], + "initial_equations": ["state = 0;"], + "equations": ["der(state) = p.e;", "p.f = state/i;"] + } + } + }, + "connections": { + "7e293c73-1e07-4d4c-b750-6f43870c71c4": { + "connection_type": "bond", + "source": "5b6a8a0c-0875-402c-bd74-11d086aac372", + "target": "4306701a-6b1b-4d19-b8ff-45dbfa04f2d3", + "causality": "flow_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", + "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", + "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", + "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", + "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", + "undesired": false + } + } + } + } + } + }, + "metadata": {} +}