fmuloader 0.1.1rc0__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,168 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: fmuloader
|
|
3
|
+
Version: 0.1.1rc0
|
|
4
|
+
Summary: a lightweight fmu loader for python
|
|
5
|
+
Author: coder
|
|
6
|
+
Author-email: coder <coder@timeintegral.ai>
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Project-URL: Repository, https://github.com/time-integral/fmuloader
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# fmuloader ⚙️
|
|
12
|
+
|
|
13
|
+
A lightweight, **zero-dependency** Python library for loading and calling FMI 2.0
|
|
14
|
+
and 3.0 shared-library binaries via ctypes.
|
|
15
|
+
|
|
16
|
+
[](https://badge.fury.io/py/fmuloader)
|
|
17
|
+
|
|
18
|
+
## Installation 📦
|
|
19
|
+
|
|
20
|
+
Add `fmuloader` to your project with `uv`:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv add fmuloader
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
> To install `uv`, see <https://docs.astral.sh/uv/getting-started/installation/>
|
|
27
|
+
|
|
28
|
+
## How to use 🚀
|
|
29
|
+
|
|
30
|
+
### FMI 2.0 Co-Simulation
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from fmuloader.fmi2 import Fmi2Slave, Fmi2Type
|
|
34
|
+
|
|
35
|
+
slave = Fmi2Slave("model.fmu", model_identifier="MyModel")
|
|
36
|
+
|
|
37
|
+
slave.instantiate("instance1", Fmi2Type.CO_SIMULATION, guid="{...}")
|
|
38
|
+
slave.setup_experiment(start_time=0.0, stop_time=10.0)
|
|
39
|
+
slave.enter_initialization_mode()
|
|
40
|
+
slave.exit_initialization_mode()
|
|
41
|
+
|
|
42
|
+
t, dt = 0.0, 0.01
|
|
43
|
+
while t < 10.0:
|
|
44
|
+
slave.do_step(t, dt)
|
|
45
|
+
t += dt
|
|
46
|
+
|
|
47
|
+
values = slave.get_real([1, 2])
|
|
48
|
+
slave.terminate()
|
|
49
|
+
slave.free_instance()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### FMI 3.0 Co-Simulation
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from fmuloader.fmi3 import Fmi3Slave
|
|
56
|
+
|
|
57
|
+
slave = Fmi3Slave("model.fmu", model_identifier="MyModel")
|
|
58
|
+
|
|
59
|
+
slave.instantiate_co_simulation("instance1", instantiation_token="{...}")
|
|
60
|
+
slave.enter_initialization_mode(start_time=0.0, stop_time=10.0)
|
|
61
|
+
slave.exit_initialization_mode()
|
|
62
|
+
|
|
63
|
+
t, dt = 0.0, 0.01
|
|
64
|
+
while t < 10.0:
|
|
65
|
+
result = slave.do_step(t, dt)
|
|
66
|
+
t += dt
|
|
67
|
+
|
|
68
|
+
values = slave.get_float64([1, 2])
|
|
69
|
+
slave.terminate()
|
|
70
|
+
slave.free_instance()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### FMI 3.0 Model Exchange
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from fmuloader.fmi3 import Fmi3Slave
|
|
77
|
+
|
|
78
|
+
slave = Fmi3Slave("model.fmu", model_identifier="MyModel")
|
|
79
|
+
|
|
80
|
+
slave.instantiate_model_exchange("instance1", instantiation_token="{...}")
|
|
81
|
+
slave.enter_initialization_mode(start_time=0.0)
|
|
82
|
+
slave.exit_initialization_mode()
|
|
83
|
+
|
|
84
|
+
result = slave.update_discrete_states()
|
|
85
|
+
while result.discrete_states_need_update:
|
|
86
|
+
result = slave.update_discrete_states()
|
|
87
|
+
slave.enter_continuous_time_mode()
|
|
88
|
+
|
|
89
|
+
nx = slave.get_number_of_continuous_states()
|
|
90
|
+
slave.set_time(0.0)
|
|
91
|
+
derivs = slave.get_continuous_state_derivatives(nx)
|
|
92
|
+
|
|
93
|
+
slave.terminate()
|
|
94
|
+
slave.free_instance()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### FMI 3.0 Scheduled Execution
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from fmuloader.fmi3 import Fmi3Slave
|
|
101
|
+
|
|
102
|
+
slave = Fmi3Slave("model.fmu", model_identifier="MyModel")
|
|
103
|
+
|
|
104
|
+
slave.instantiate_scheduled_execution("instance1", instantiation_token="{...}")
|
|
105
|
+
slave.enter_initialization_mode(start_time=0.0)
|
|
106
|
+
slave.exit_initialization_mode()
|
|
107
|
+
|
|
108
|
+
slave.activate_model_partition(clock_reference=1001, activation_time=0.0)
|
|
109
|
+
|
|
110
|
+
slave.terminate()
|
|
111
|
+
slave.free_instance()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Features ✨
|
|
115
|
+
|
|
116
|
+
- Load and call FMI **2.0** and **3.0** shared-library binaries via ctypes
|
|
117
|
+
- Full Co-Simulation, Model Exchange, and Scheduled Execution support
|
|
118
|
+
- All FMI 3.0 data types: `Float32`, `Float64`, `Int8`–`Int64`, `UInt8`–`UInt64`, `Boolean`, `String`, `Binary`, `Clock`
|
|
119
|
+
- FMU state management: get, set, serialize, and deserialize
|
|
120
|
+
- Directional and adjoint derivatives
|
|
121
|
+
- Clock interval and shift functions
|
|
122
|
+
- Context manager support for automatic cleanup
|
|
123
|
+
- Automatic platform detection (macOS, Linux, Windows; x86, x86_64, aarch64)
|
|
124
|
+
|
|
125
|
+
## Design philosophy 💡
|
|
126
|
+
|
|
127
|
+
**fmuloader intentionally separates binary loading from modelDescription.xml parsing.**
|
|
128
|
+
|
|
129
|
+
Most FMI libraries tightly couple XML parsing with binary invocation, pulling in
|
|
130
|
+
heavy dependencies and making it hard to use one without the other. fmuloader
|
|
131
|
+
takes a different approach:
|
|
132
|
+
|
|
133
|
+
- **fmuloader** handles only the **binary loading** — extracting the shared
|
|
134
|
+
library from an `.fmu` archive, binding every C function via ctypes, and
|
|
135
|
+
exposing thin Python wrappers.
|
|
136
|
+
- **modelDescription.xml parsing** is left to the user or to a dedicated library
|
|
137
|
+
like [fmureader](https://github.com/time-integral/fmureader).
|
|
138
|
+
|
|
139
|
+
This means:
|
|
140
|
+
|
|
141
|
+
- **Zero runtime dependencies** — fmuloader uses only the Python standard library.
|
|
142
|
+
- **Bring your own parser** — use fmureader, FMPy, lxml, or anything else to
|
|
143
|
+
read GUIDs, value references, and variable metadata. Then pass them straight
|
|
144
|
+
to fmuloader.
|
|
145
|
+
- **Minimal surface area** — each module (`fmi2`, `fmi3`) is a single file you
|
|
146
|
+
can vendor into your own project if needed.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
# Example: combine fmureader (parsing) with fmuloader (execution)
|
|
150
|
+
import fmureader.fmi3 as reader
|
|
151
|
+
from fmuloader.fmi3 import Fmi3Slave
|
|
152
|
+
|
|
153
|
+
md = reader.read_model_description("model.fmu")
|
|
154
|
+
|
|
155
|
+
slave = Fmi3Slave("model.fmu", model_identifier=md.co_simulation.model_identifier)
|
|
156
|
+
slave.instantiate_co_simulation("inst", instantiation_token=md.instantiation_token)
|
|
157
|
+
# ... use md.model_variables to look up value references, then call slave.get_float64() etc.
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Related projects 🔗
|
|
161
|
+
|
|
162
|
+
- [fmureader](https://github.com/time-integral/fmureader) — Lightweight Pydantic-based modelDescription.xml parser for FMI 2.0 and 3.0
|
|
163
|
+
- [FMPy](https://github.com/CATIA-Systems/FMPy) — Full-featured FMI library with simulation, GUI, and more
|
|
164
|
+
|
|
165
|
+
## Licensing 📄
|
|
166
|
+
|
|
167
|
+
The code in this project is licensed under MIT license.
|
|
168
|
+
See the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# fmuloader ⚙️
|
|
2
|
+
|
|
3
|
+
A lightweight, **zero-dependency** Python library for loading and calling FMI 2.0
|
|
4
|
+
and 3.0 shared-library binaries via ctypes.
|
|
5
|
+
|
|
6
|
+
[](https://badge.fury.io/py/fmuloader)
|
|
7
|
+
|
|
8
|
+
## Installation 📦
|
|
9
|
+
|
|
10
|
+
Add `fmuloader` to your project with `uv`:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uv add fmuloader
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
> To install `uv`, see <https://docs.astral.sh/uv/getting-started/installation/>
|
|
17
|
+
|
|
18
|
+
## How to use 🚀
|
|
19
|
+
|
|
20
|
+
### FMI 2.0 Co-Simulation
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from fmuloader.fmi2 import Fmi2Slave, Fmi2Type
|
|
24
|
+
|
|
25
|
+
slave = Fmi2Slave("model.fmu", model_identifier="MyModel")
|
|
26
|
+
|
|
27
|
+
slave.instantiate("instance1", Fmi2Type.CO_SIMULATION, guid="{...}")
|
|
28
|
+
slave.setup_experiment(start_time=0.0, stop_time=10.0)
|
|
29
|
+
slave.enter_initialization_mode()
|
|
30
|
+
slave.exit_initialization_mode()
|
|
31
|
+
|
|
32
|
+
t, dt = 0.0, 0.01
|
|
33
|
+
while t < 10.0:
|
|
34
|
+
slave.do_step(t, dt)
|
|
35
|
+
t += dt
|
|
36
|
+
|
|
37
|
+
values = slave.get_real([1, 2])
|
|
38
|
+
slave.terminate()
|
|
39
|
+
slave.free_instance()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### FMI 3.0 Co-Simulation
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from fmuloader.fmi3 import Fmi3Slave
|
|
46
|
+
|
|
47
|
+
slave = Fmi3Slave("model.fmu", model_identifier="MyModel")
|
|
48
|
+
|
|
49
|
+
slave.instantiate_co_simulation("instance1", instantiation_token="{...}")
|
|
50
|
+
slave.enter_initialization_mode(start_time=0.0, stop_time=10.0)
|
|
51
|
+
slave.exit_initialization_mode()
|
|
52
|
+
|
|
53
|
+
t, dt = 0.0, 0.01
|
|
54
|
+
while t < 10.0:
|
|
55
|
+
result = slave.do_step(t, dt)
|
|
56
|
+
t += dt
|
|
57
|
+
|
|
58
|
+
values = slave.get_float64([1, 2])
|
|
59
|
+
slave.terminate()
|
|
60
|
+
slave.free_instance()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### FMI 3.0 Model Exchange
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from fmuloader.fmi3 import Fmi3Slave
|
|
67
|
+
|
|
68
|
+
slave = Fmi3Slave("model.fmu", model_identifier="MyModel")
|
|
69
|
+
|
|
70
|
+
slave.instantiate_model_exchange("instance1", instantiation_token="{...}")
|
|
71
|
+
slave.enter_initialization_mode(start_time=0.0)
|
|
72
|
+
slave.exit_initialization_mode()
|
|
73
|
+
|
|
74
|
+
result = slave.update_discrete_states()
|
|
75
|
+
while result.discrete_states_need_update:
|
|
76
|
+
result = slave.update_discrete_states()
|
|
77
|
+
slave.enter_continuous_time_mode()
|
|
78
|
+
|
|
79
|
+
nx = slave.get_number_of_continuous_states()
|
|
80
|
+
slave.set_time(0.0)
|
|
81
|
+
derivs = slave.get_continuous_state_derivatives(nx)
|
|
82
|
+
|
|
83
|
+
slave.terminate()
|
|
84
|
+
slave.free_instance()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### FMI 3.0 Scheduled Execution
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from fmuloader.fmi3 import Fmi3Slave
|
|
91
|
+
|
|
92
|
+
slave = Fmi3Slave("model.fmu", model_identifier="MyModel")
|
|
93
|
+
|
|
94
|
+
slave.instantiate_scheduled_execution("instance1", instantiation_token="{...}")
|
|
95
|
+
slave.enter_initialization_mode(start_time=0.0)
|
|
96
|
+
slave.exit_initialization_mode()
|
|
97
|
+
|
|
98
|
+
slave.activate_model_partition(clock_reference=1001, activation_time=0.0)
|
|
99
|
+
|
|
100
|
+
slave.terminate()
|
|
101
|
+
slave.free_instance()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Features ✨
|
|
105
|
+
|
|
106
|
+
- Load and call FMI **2.0** and **3.0** shared-library binaries via ctypes
|
|
107
|
+
- Full Co-Simulation, Model Exchange, and Scheduled Execution support
|
|
108
|
+
- All FMI 3.0 data types: `Float32`, `Float64`, `Int8`–`Int64`, `UInt8`–`UInt64`, `Boolean`, `String`, `Binary`, `Clock`
|
|
109
|
+
- FMU state management: get, set, serialize, and deserialize
|
|
110
|
+
- Directional and adjoint derivatives
|
|
111
|
+
- Clock interval and shift functions
|
|
112
|
+
- Context manager support for automatic cleanup
|
|
113
|
+
- Automatic platform detection (macOS, Linux, Windows; x86, x86_64, aarch64)
|
|
114
|
+
|
|
115
|
+
## Design philosophy 💡
|
|
116
|
+
|
|
117
|
+
**fmuloader intentionally separates binary loading from modelDescription.xml parsing.**
|
|
118
|
+
|
|
119
|
+
Most FMI libraries tightly couple XML parsing with binary invocation, pulling in
|
|
120
|
+
heavy dependencies and making it hard to use one without the other. fmuloader
|
|
121
|
+
takes a different approach:
|
|
122
|
+
|
|
123
|
+
- **fmuloader** handles only the **binary loading** — extracting the shared
|
|
124
|
+
library from an `.fmu` archive, binding every C function via ctypes, and
|
|
125
|
+
exposing thin Python wrappers.
|
|
126
|
+
- **modelDescription.xml parsing** is left to the user or to a dedicated library
|
|
127
|
+
like [fmureader](https://github.com/time-integral/fmureader).
|
|
128
|
+
|
|
129
|
+
This means:
|
|
130
|
+
|
|
131
|
+
- **Zero runtime dependencies** — fmuloader uses only the Python standard library.
|
|
132
|
+
- **Bring your own parser** — use fmureader, FMPy, lxml, or anything else to
|
|
133
|
+
read GUIDs, value references, and variable metadata. Then pass them straight
|
|
134
|
+
to fmuloader.
|
|
135
|
+
- **Minimal surface area** — each module (`fmi2`, `fmi3`) is a single file you
|
|
136
|
+
can vendor into your own project if needed.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# Example: combine fmureader (parsing) with fmuloader (execution)
|
|
140
|
+
import fmureader.fmi3 as reader
|
|
141
|
+
from fmuloader.fmi3 import Fmi3Slave
|
|
142
|
+
|
|
143
|
+
md = reader.read_model_description("model.fmu")
|
|
144
|
+
|
|
145
|
+
slave = Fmi3Slave("model.fmu", model_identifier=md.co_simulation.model_identifier)
|
|
146
|
+
slave.instantiate_co_simulation("inst", instantiation_token=md.instantiation_token)
|
|
147
|
+
# ... use md.model_variables to look up value references, then call slave.get_float64() etc.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Related projects 🔗
|
|
151
|
+
|
|
152
|
+
- [fmureader](https://github.com/time-integral/fmureader) — Lightweight Pydantic-based modelDescription.xml parser for FMI 2.0 and 3.0
|
|
153
|
+
- [FMPy](https://github.com/CATIA-Systems/FMPy) — Full-featured FMI library with simulation, GUI, and more
|
|
154
|
+
|
|
155
|
+
## Licensing 📄
|
|
156
|
+
|
|
157
|
+
The code in this project is licensed under MIT license.
|
|
158
|
+
See the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fmuloader"
|
|
3
|
+
version = "0.1.1rc0"
|
|
4
|
+
description = "a lightweight fmu loader for python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "coder", email = "coder@timeintegral.ai" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = []
|
|
11
|
+
|
|
12
|
+
[project.urls]
|
|
13
|
+
Repository = "https://github.com/time-integral/fmuloader"
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["uv_build>=0.10.0,<0.11.0"]
|
|
17
|
+
build-backend = "uv_build"
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"httpx>=0.28.1",
|
|
22
|
+
"pyright>=1.1.407",
|
|
23
|
+
"pytest>=9.0.2",
|
|
24
|
+
"pytest-cov>=7.0.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
[tool.pyright]
|
|
29
|
+
include = [
|
|
30
|
+
"src",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[tool.uv]
|
|
34
|
+
default-groups = ["dev"]
|
|
35
|
+
package = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.1rc0"
|