modelica 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ site/
6
+ public/
7
+ .moon/cache/
8
+ .ruff_cache/
9
+ .pytest_cache/
10
+ .coverage
11
+ logs/
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. Versions follow
4
+ [semantic versioning](https://semver.org/); the version in `pyproject.toml` is the single
5
+ source of truth and is bumped automatically when shipped source changes.
6
+
7
+ ## 0.0.1 — unreleased
8
+
9
+ Project restart. The 2010 Python 2 / pyparsing prototype is retired (it remains in git
10
+ history); nothing from it is carried forward.
11
+
12
+ - Claim the `modelica` distribution name on PyPI.
13
+ - Repository moved from GitHub to GitLab (`gitlab.com/jorgeecardona/pymodelica`).
14
+ - Toolchain: uv + moon pinned via proto; ruff (`select = ["ALL"]`) and pyright strict.
15
+ - Single-job GitLab CI driven by `moon run :ci`; PyPI releases via OIDC Trusted
16
+ Publishing; docs on GitLab Pages.
17
+ - Python 3.11+ only.
modelica-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jorge Cardona
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: modelica
3
+ Version: 0.0.1
4
+ Summary: Pure-Python Modelica toolchain: parse, flatten, simulate, and exchange via FMI 3.0 and SSP 2.0
5
+ Project-URL: Homepage, https://gitlab.com/jorgeecardona/pymodelica
6
+ Project-URL: Repository, https://gitlab.com/jorgeecardona/pymodelica
7
+ Project-URL: Changelog, https://gitlab.com/jorgeecardona/pymodelica/-/blob/main/CHANGELOG.md
8
+ Project-URL: Documentation, https://jorgeecardona.gitlab.io/pymodelica/
9
+ Author-email: Jorge Cardona <jorgeecardona@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: co-simulation,dae,digital-twin,fmi,fmu,modelica,simulation,ssp,system-modeling
13
+ Classifier: Development Status :: 2 - Pre-Alpha
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Provides-Extra: docs
25
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
26
+ Requires-Dist: mkdocs>=1.6; extra == 'docs'
27
+ Provides-Extra: sim
28
+ Requires-Dist: numpy>=2.0; extra == 'sim'
29
+ Requires-Dist: scipy>=1.13; extra == 'sim'
30
+ Provides-Extra: sundials
31
+ Requires-Dist: scikit-sundae>=1.0; extra == 'sundials'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # modelica
35
+
36
+ A pure-Python Modelica toolchain: **parse** Modelica, **flatten** it to a DAE, **simulate**
37
+ it, and interoperate with the two Modelica Association exchange standards — **FMI 3.0**
38
+ for single components and **SSP 2.0** for systems of them.
39
+
40
+ > Distribution name is `modelica`; the git repo keeps its historical name `pymodelica`.
41
+ > (PyPI's `pymodelica` is held by an abandoned Modelon/JModelica.org upload.)
42
+
43
+ ```bash
44
+ pip install modelica # core: parse + model structure, zero dependencies
45
+ pip install "modelica[sim]" # + numpy/scipy for simulation
46
+ ```
47
+
48
+ ## Status
49
+
50
+ Pre-alpha. This is a **full rewrite** of a 2010 Python 2 experiment — none of that code
51
+ survives, only the goal. Python 3.11+.
52
+
53
+ ## Why
54
+
55
+ The Python side of Modelica is split: [FMPy](https://github.com/CATIA-Systems/FMPy) and
56
+ [PyFMI](https://github.com/modelon-community/PyFMI) *consume* FMUs that some other tool
57
+ compiled, and the tools that actually compile Modelica (Dymola, OpenModelica, JModelica)
58
+ are large non-Python programs. Nothing in Python takes Modelica source all the way to a
59
+ running simulation, and nothing treats the model — flat equations, the incidence structure,
60
+ the index — as an ordinary Python data structure you can inspect and rewrite.
61
+
62
+ That gap is the project: a readable, dependency-light implementation where every stage is a
63
+ value you can hold.
64
+
65
+ ## Layers
66
+
67
+ Each layer is usable without the ones above it.
68
+
69
+ | Module | Does |
70
+ | --- | --- |
71
+ | `modelica.lang` | Modelica source → tokens → AST (hand-written lexer/parser, positions carried for diagnostics) |
72
+ | `modelica.ir` | AST → flat model (variables, equations, expression IR), then matching / BLT sorting / index reduction |
73
+ | `modelica.sim` | flat model → ODE/DAE problem, solver backends, event (zero-crossing) handling |
74
+ | `modelica.fmi` | FMI 3.0 — read and simulate an FMU; export a flat model as one |
75
+ | `modelica.ssp` | SSP 2.0 — `.ssd`/`.ssv`/`.ssb` inside a `.ssp`, and a co-simulation master over the components |
76
+
77
+ See [the architecture notes](docs/architecture.md) for the data model and the order things
78
+ get built in.
79
+
80
+ ## Development
81
+
82
+ ```bash
83
+ make install-hooks # once after cloning: pre-commit (auto version bump + ruff), pre-push (full check)
84
+ make check # lint + typecheck + test, exactly what CI gates on
85
+ make docs # build the mkdocs site strictly
86
+ ```
87
+
88
+ The toolchain is pinned in `.prototools` (moon + uv) — `proto install` brings up
89
+ everything. All task ordering lives in `moon.yml`; CI is a single `moon run :ci`.
90
+
91
+ ## Releasing
92
+
93
+ There is no manual release step. `pyproject.toml`'s `version` is the single source of
94
+ truth:
95
+
96
+ * the pre-commit hook auto-bumps the patch version whenever `src/**/*.py` changes;
97
+ * `scripts/ci-version-guard` fails the pipeline if shipped source moved without a bump
98
+ (the non-bypassable counterpart);
99
+ * on `main`, `moon run :release` publishes to PyPI **only if that version isn't there
100
+ yet**, via GitLab OIDC Trusted Publishing — no API token is stored anywhere.
101
+
102
+ Docs are published to GitLab Pages from the same pipeline.
103
+
104
+ ## License
105
+
106
+ MIT © Jorge Cardona
@@ -0,0 +1,73 @@
1
+ # modelica
2
+
3
+ A pure-Python Modelica toolchain: **parse** Modelica, **flatten** it to a DAE, **simulate**
4
+ it, and interoperate with the two Modelica Association exchange standards — **FMI 3.0**
5
+ for single components and **SSP 2.0** for systems of them.
6
+
7
+ > Distribution name is `modelica`; the git repo keeps its historical name `pymodelica`.
8
+ > (PyPI's `pymodelica` is held by an abandoned Modelon/JModelica.org upload.)
9
+
10
+ ```bash
11
+ pip install modelica # core: parse + model structure, zero dependencies
12
+ pip install "modelica[sim]" # + numpy/scipy for simulation
13
+ ```
14
+
15
+ ## Status
16
+
17
+ Pre-alpha. This is a **full rewrite** of a 2010 Python 2 experiment — none of that code
18
+ survives, only the goal. Python 3.11+.
19
+
20
+ ## Why
21
+
22
+ The Python side of Modelica is split: [FMPy](https://github.com/CATIA-Systems/FMPy) and
23
+ [PyFMI](https://github.com/modelon-community/PyFMI) *consume* FMUs that some other tool
24
+ compiled, and the tools that actually compile Modelica (Dymola, OpenModelica, JModelica)
25
+ are large non-Python programs. Nothing in Python takes Modelica source all the way to a
26
+ running simulation, and nothing treats the model — flat equations, the incidence structure,
27
+ the index — as an ordinary Python data structure you can inspect and rewrite.
28
+
29
+ That gap is the project: a readable, dependency-light implementation where every stage is a
30
+ value you can hold.
31
+
32
+ ## Layers
33
+
34
+ Each layer is usable without the ones above it.
35
+
36
+ | Module | Does |
37
+ | --- | --- |
38
+ | `modelica.lang` | Modelica source → tokens → AST (hand-written lexer/parser, positions carried for diagnostics) |
39
+ | `modelica.ir` | AST → flat model (variables, equations, expression IR), then matching / BLT sorting / index reduction |
40
+ | `modelica.sim` | flat model → ODE/DAE problem, solver backends, event (zero-crossing) handling |
41
+ | `modelica.fmi` | FMI 3.0 — read and simulate an FMU; export a flat model as one |
42
+ | `modelica.ssp` | SSP 2.0 — `.ssd`/`.ssv`/`.ssb` inside a `.ssp`, and a co-simulation master over the components |
43
+
44
+ See [the architecture notes](docs/architecture.md) for the data model and the order things
45
+ get built in.
46
+
47
+ ## Development
48
+
49
+ ```bash
50
+ make install-hooks # once after cloning: pre-commit (auto version bump + ruff), pre-push (full check)
51
+ make check # lint + typecheck + test, exactly what CI gates on
52
+ make docs # build the mkdocs site strictly
53
+ ```
54
+
55
+ The toolchain is pinned in `.prototools` (moon + uv) — `proto install` brings up
56
+ everything. All task ordering lives in `moon.yml`; CI is a single `moon run :ci`.
57
+
58
+ ## Releasing
59
+
60
+ There is no manual release step. `pyproject.toml`'s `version` is the single source of
61
+ truth:
62
+
63
+ * the pre-commit hook auto-bumps the patch version whenever `src/**/*.py` changes;
64
+ * `scripts/ci-version-guard` fails the pipeline if shipped source moved without a bump
65
+ (the non-bypassable counterpart);
66
+ * on `main`, `moon run :release` publishes to PyPI **only if that version isn't there
67
+ yet**, via GitLab OIDC Trusted Publishing — no API token is stored anywhere.
68
+
69
+ Docs are published to GitLab Pages from the same pipeline.
70
+
71
+ ## License
72
+
73
+ MIT © Jorge Cardona
@@ -0,0 +1,110 @@
1
+ # Architecture
2
+
3
+ The design rule for this rewrite: **every stage is a value you can hold.** Parsing returns
4
+ an AST, flattening returns a flat model, causalization returns a sorted block structure,
5
+ and simulation returns trajectories — each one an ordinary, inspectable Python object
6
+ rather than state hidden inside a compiler. The 2010 prototype failed the other way: the
7
+ grammar (pyparsing) *was* the program, so there was nothing to look at between "source" and
8
+ "result".
9
+
10
+ Nothing here is implemented yet. This is the target, and the order it gets built in.
11
+
12
+ ## The pipeline
13
+
14
+ ```
15
+ Modelica source
16
+ │ modelica.lang hand-written lexer + recursive-descent parser
17
+
18
+ AST positions carried on every node (diagnostics)
19
+ │ modelica.ir instantiation / flattening
20
+
21
+ FlatModel variables + equations + expression IR
22
+ │ modelica.ir matching, BLT sorting, index reduction
23
+
24
+ CausalizedModel sorted blocks, known index, states chosen
25
+ │ modelica.sim residual/RHS construction
26
+
27
+ Problem ──► Solver ──► Result
28
+ ```
29
+
30
+ Two exchange formats cut across it:
31
+
32
+ * **FMI 3.0** (`modelica.fmi`) attaches at both ends — read an FMU someone else compiled
33
+ and simulate it through the same `Problem`/`Solver` interface, or export a
34
+ `CausalizedModel` as an FMU.
35
+ * **SSP 2.0** (`modelica.ssp`) sits above — a `.ssp` names components (usually FMUs) and
36
+ wires them, so it drives a co-simulation master over several `Problem`s at once.
37
+
38
+ ## Layer notes
39
+
40
+ ### `modelica.lang` — source → AST
41
+
42
+ Hand-written lexer and recursive-descent parser against the Modelica Language
43
+ Specification 3.x grammar. No parser-generator and no `pyparsing`: the grammar is stable
44
+ and small enough to write out, and only a hand-written parser gives usable errors
45
+ ("expected `end <name>;` at 12:5, to close `model Foo` opened at 3:1"). AST nodes are
46
+ frozen dataclasses carrying source spans.
47
+
48
+ ### `modelica.ir` — AST → solvable system
49
+
50
+ The interesting layer, and the one the whole project stands on.
51
+
52
+ * **Flattening** — resolve `extends`, expand components and `for`-equations, apply
53
+ modifications, connect `connect()` equations into their potential/flow forms. Output:
54
+ a `FlatModel` of typed variables and scalar equations over an expression IR.
55
+ * **Expression IR** — separate from the AST on purpose. The AST mirrors what was written;
56
+ the IR is what gets differentiated and evaluated, so it is small, typed, and closed under
57
+ symbolic differentiation.
58
+ * **Matching + BLT** — bipartite matching of equations to unknowns, then Tarjan SCC to get
59
+ block-lower-triangular form: which equations must be solved simultaneously and which are
60
+ a straight assignment sequence.
61
+ * **Index reduction** — Pantelides to find the structurally singular subsets, symbolic
62
+ differentiation of those equations, and the dummy-derivative method to select states.
63
+ A high-index DAE is the normal case for a physical model written with `connect()`; a
64
+ toolchain that cannot reduce the index cannot simulate a mechanical model at all.
65
+
66
+ ### `modelica.sim` — running it
67
+
68
+ `Problem` is the interface every backend consumes: an explicit ODE where causalization
69
+ achieved one, a residual DAE otherwise. Backends:
70
+
71
+ * **scipy** (`sim` extra) — `solve_ivp` for ODE form, plus root-finding for the algebraic
72
+ blocks. The default, because it installs everywhere.
73
+ * **SUNDIALS IDA** via [scikit-SUNDAE](https://github.com/NREL/scikit-sundae) (`sundials`
74
+ extra) — real index-1 DAE integration with consistent initialization. Optional because it
75
+ needs a compiled SUNDIALS.
76
+
77
+ Events (zero crossings from `if`/`when`, and state events generally) are the part that
78
+ makes this harder than "call an integrator": the solver must stop at the crossing, the
79
+ model is re-initialized, and integration restarts. That is designed in from the start, not
80
+ bolted on.
81
+
82
+ ### `modelica.fmi` — FMI 3.0
83
+
84
+ `modelDescription.xml` parsed into dataclasses (variables, causality/variability, model
85
+ structure, unit definitions), the FMU's shared library loaded through `ctypes`, and both
86
+ interface types wrapped: Model Exchange (the FMU supplies the derivatives, we integrate)
87
+ and Co-Simulation (the FMU integrates itself between communication points). Export goes the
88
+ other way: a `CausalizedModel` plus a generated `modelDescription.xml`, with the FMU's C
89
+ entry points backed by the Python evaluator.
90
+
91
+ ### `modelica.ssp` — SSP 2.0
92
+
93
+ A `.ssp` is a zip: `SystemStructure.ssd` (components, connectors, connections, possibly
94
+ nested systems), `.ssv` parameter sets, `.ssb` bindings. Parsed into a composition graph,
95
+ resolved against the FMUs it references, and executed by a co-simulation master that steps
96
+ the components and exchanges signals at communication points. SSP 2.0 specifics that matter
97
+ to the data model: FMI 3.0 component support, the `structuralParameter` connector kind,
98
+ clocked connectors, and arrays in connectors/connections.
99
+
100
+ ## Build order
101
+
102
+ 1. `lang` + `ir` far enough to flatten a trivial `model` into scalar equations.
103
+ 2. `sim` on the explicit-ODE case, scipy backend — first end-to-end simulation.
104
+ 3. Matching/BLT, then Pantelides + dummy derivatives — first physical model with
105
+ `connect()`.
106
+ 4. `fmi` import (read + simulate an existing FMU) — validates the variable/model-structure
107
+ data model against real files.
108
+ 5. Events.
109
+ 6. `fmi` export.
110
+ 7. `ssp` composition + co-simulation master.
@@ -0,0 +1,3 @@
1
+ # Changelog
2
+
3
+ --8<-- "CHANGELOG.md"
@@ -0,0 +1,31 @@
1
+ # modelica
2
+
3
+ Pure-Python Modelica toolchain: parse Modelica, flatten it to a DAE, simulate it, and
4
+ exchange models via **FMI 3.0** and **SSP 2.0**.
5
+
6
+ ```bash
7
+ pip install modelica # core: parse + model structure, zero dependencies
8
+ pip install "modelica[sim]" # + numpy/scipy for simulation
9
+ ```
10
+
11
+ !!! warning "Pre-alpha"
12
+ A full rewrite of a 2010 prototype, in progress. The API is not stable and the layers
13
+ below are being built bottom-up. Python 3.11+.
14
+
15
+ ## The layers
16
+
17
+ | Module | Does |
18
+ | --- | --- |
19
+ | `modelica.lang` | Modelica source → tokens → AST |
20
+ | `modelica.ir` | AST → flat model; matching, BLT sorting, index reduction |
21
+ | `modelica.sim` | flat model → ODE/DAE problem; solvers and events |
22
+ | `modelica.fmi` | FMI 3.0 — import, simulate, and export FMUs |
23
+ | `modelica.ssp` | SSP 2.0 — system composition and a co-simulation master |
24
+
25
+ Start with [Architecture](architecture.md) for the data model and the build order.
26
+
27
+ ## Standards targeted
28
+
29
+ * **Modelica Language Specification 3.x**
30
+ * **FMI 3.0** — [fmi-standard.org](https://fmi-standard.org/)
31
+ * **SSP 2.0** — [ssp-standard.org](https://ssp-standard.org/)
@@ -0,0 +1,123 @@
1
+ [project]
2
+ # modelica — a pure-Python Modelica/FMI/SSP toolchain: parse Modelica, flatten it to a
3
+ # DAE, simulate it, and interoperate with the two Modelica Association exchange standards
4
+ # (FMI 3.0 for single components, SSP 2.0 for systems of them).
5
+ #
6
+ # The distribution name is `modelica`, not `pymodelica`: PyPI's `pymodelica` is held by an
7
+ # abandoned Modelon/JModelica.org upload (version "trunk"). The git repo keeps the
8
+ # historical name; the import package matches the distribution.
9
+ name = "modelica"
10
+ version = "0.0.1"
11
+ description = "Pure-Python Modelica toolchain: parse, flatten, simulate, and exchange via FMI 3.0 and SSP 2.0"
12
+ readme = "README.md"
13
+ # 3.11+, no compatibility shims: this is a full rewrite of a 2010 Python 2 codebase, and
14
+ # the modelling layer leans on 3.11 features (Self, StrEnum, exception groups for
15
+ # multi-error diagnostics, and the tomllib/typing niceties) rather than back-porting them.
16
+ requires-python = ">=3.11"
17
+ license = "MIT"
18
+ license-files = ["LICENSE"]
19
+ authors = [{ name = "Jorge Cardona", email = "jorgeecardona@gmail.com" }]
20
+ keywords = [
21
+ "modelica",
22
+ "fmi",
23
+ "fmu",
24
+ "ssp",
25
+ "simulation",
26
+ "dae",
27
+ "co-simulation",
28
+ "digital-twin",
29
+ "system-modeling",
30
+ ]
31
+ classifiers = [
32
+ "Development Status :: 2 - Pre-Alpha",
33
+ "Intended Audience :: Science/Research",
34
+ "Operating System :: POSIX :: Linux",
35
+ "Operating System :: MacOS",
36
+ "Programming Language :: Python :: 3",
37
+ "Programming Language :: Python :: 3.11",
38
+ "Programming Language :: Python :: 3.12",
39
+ "Programming Language :: Python :: 3.13",
40
+ "Topic :: Scientific/Engineering",
41
+ "Typing :: Typed",
42
+ ]
43
+ # Deliberately dependency-free at the core. The parser is hand-written (no pyparsing —
44
+ # that was the 2010 design and it could not carry error positions), the FMI/SSP layers
45
+ # read XML from the stdlib, and numeric backends arrive as extras so `import modelica`
46
+ # stays cheap for tooling that only reads model structure.
47
+ dependencies = []
48
+
49
+ [project.optional-dependencies]
50
+ # Numerics for the simulation layer. scipy covers ODE + the algebraic solves; the SUNDIALS
51
+ # IDA binding for real (index-1) DAEs is a separate extra because it needs a compiled
52
+ # SUNDIALS and must not be a hard requirement of `pip install modelica`.
53
+ sim = ["numpy>=2.0", "scipy>=1.13"]
54
+ sundials = ["scikit-sundae>=1.0"]
55
+ # docs are an *extra* (not a uv dependency-group) so external services can install them via pip
56
+ docs = ["mkdocs>=1.6", "mkdocs-material>=9.5"]
57
+
58
+ [project.urls]
59
+ Homepage = "https://gitlab.com/jorgeecardona/pymodelica"
60
+ Repository = "https://gitlab.com/jorgeecardona/pymodelica"
61
+ Changelog = "https://gitlab.com/jorgeecardona/pymodelica/-/blob/main/CHANGELOG.md"
62
+ Documentation = "https://jorgeecardona.gitlab.io/pymodelica/"
63
+
64
+ [build-system]
65
+ requires = ["hatchling"]
66
+ build-backend = "hatchling.build"
67
+
68
+ [tool.hatch.build.targets.wheel]
69
+ packages = ["src/modelica"]
70
+
71
+ [tool.hatch.build.targets.sdist]
72
+ include = ["src", "tests", "docs", "README.md", "CHANGELOG.md", "LICENSE"]
73
+
74
+ [dependency-groups]
75
+ dev = [
76
+ "ruff>=0.6",
77
+ "pyright>=1.1.390",
78
+ "pytest>=8",
79
+ "pytest-cov>=6",
80
+ "numpy>=2.0",
81
+ "scipy>=1.13",
82
+ ]
83
+
84
+ [tool.pytest.ini_options]
85
+ testpaths = ["tests"]
86
+
87
+ [tool.ruff]
88
+ line-length = 100
89
+ target-version = "py311"
90
+ src = ["src", "tests"]
91
+
92
+ [tool.ruff.lint]
93
+ # Strict: everything on, each opt-out justified.
94
+ select = ["ALL"]
95
+ ignore = [
96
+ "E501", # line length is the formatter's job (line-length = 100)
97
+ "COM812", # trailing commas conflict with `ruff format` (ruff's own recommendation)
98
+ "D1", # not every helper owes a docstring; the ones that exist explain *why*
99
+ "D401", # docstrings here are narrative rationale, not imperative summaries
100
+ "TRY003", "EM101", "EM102", # inline exception messages; msg-variable style adds noise
101
+ "CPY001", # per-file copyright headers; LICENSE covers the repo
102
+ ]
103
+
104
+ [tool.ruff.lint.per-file-ignores]
105
+ "tests/*" = [
106
+ "S101", # asserts are the point of tests
107
+ "PLR2004", # literal expectations in asserts are clearer than named constants
108
+ "ARG", # pytest fixture signatures are fixed; params can go unused
109
+ ]
110
+
111
+ [tool.ruff.lint.pydocstyle]
112
+ convention = "pep257"
113
+
114
+ [tool.ruff.lint.isort]
115
+ known-first-party = ["modelica"]
116
+
117
+ [tool.pyright]
118
+ include = ["src", "tests"]
119
+ extraPaths = ["src"]
120
+ pythonVersion = "3.11"
121
+ typeCheckingMode = "strict"
122
+ venvPath = "."
123
+ venv = ".venv"
@@ -0,0 +1,21 @@
1
+ """Pure-Python Modelica toolchain: parse, flatten, simulate, exchange.
2
+
3
+ The package is layered, and each layer is usable without the ones above it:
4
+
5
+ * ``modelica.lang`` -- Modelica source -> tokens -> AST (hand-written lexer/parser).
6
+ * ``modelica.ir`` -- AST -> flat model: variables, equations, an expression IR;
7
+ then matching/BLT sorting and index reduction down to a solvable system.
8
+ * ``modelica.sim`` -- the flat model as an ODE/DAE problem plus solver backends and
9
+ event (zero-crossing) handling.
10
+ * ``modelica.fmi`` -- FMI 3.0: read/simulate an existing FMU, and export a flat model
11
+ as one.
12
+ * ``modelica.ssp`` -- SSP 2.0: system composition (.ssd/.ssv/.ssb inside a .ssp) and a
13
+ co-simulation master over the components it names.
14
+
15
+ ``import modelica`` pulls in no third-party dependency; numerics live behind the ``sim``
16
+ extra so that tooling which only reads model *structure* stays cheap to install.
17
+ """
18
+
19
+ __version__ = "0.0.1"
20
+
21
+ __all__ = ["__version__"]
File without changes
@@ -0,0 +1,22 @@
1
+ """The version in ``modelica.__version__`` mirrors pyproject's.
2
+
3
+ scripts/bump-version rewrites both, and this is what catches it when only one of them moves.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import re
9
+ import tomllib
10
+ from pathlib import Path
11
+
12
+ import modelica
13
+
14
+
15
+ def test_version_matches_pyproject() -> None:
16
+ pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
17
+ data = tomllib.loads(pyproject.read_text(encoding="utf-8"))
18
+ assert modelica.__version__ == data["project"]["version"]
19
+
20
+
21
+ def test_version_is_semver() -> None:
22
+ assert re.fullmatch(r"\d+\.\d+\.\d+", modelica.__version__)