pythonfmu3 0.2.4__tar.gz → 0.3.0__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.
- {pythonfmu3-0.2.4/pythonfmu3.egg-info → pythonfmu3-0.3.0}/PKG-INFO +61 -8
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/README.md +60 -7
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/__init__.py +3 -1
- pythonfmu3-0.3.0/pythonfmu3/_version.py +1 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/builder.py +14 -7
- pythonfmu3-0.3.0/pythonfmu3/cosimulation.py +8 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/fmi3slave.py +63 -4
- pythonfmu3-0.3.0/pythonfmu3/modelexchange.py +23 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp +17 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp +215 -82
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp +105 -2
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp +17 -0
- pythonfmu3-0.3.0/pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
- pythonfmu3-0.3.0/pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
- pythonfmu3-0.3.0/pythonfmu3/tests/slaves/pythonslaveMX.py +27 -0
- pythonfmu3-0.3.0/pythonfmu3/tests/slaves/pythonslaveMXCS.py +32 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/test_fmi3slave.py +16 -1
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/test_integration.py +61 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0/pythonfmu3.egg-info}/PKG-INFO +61 -8
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3.egg-info/SOURCES.txt +4 -0
- pythonfmu3-0.2.4/pythonfmu3/_version.py +0 -1
- pythonfmu3-0.2.4/pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
- pythonfmu3-0.2.4/pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/LICENSE +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/MANIFEST.in +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/__main__.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/default_experiment.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/deploy.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/enums.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/logmsg.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/osutil.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/.clang-format +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/CMakeLists.txt +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/build_unix.sh +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/build_win.sh +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/CMakeLists.txt +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.cpp +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/fmi/fmi3FunctionTypes.h +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/fmi/fmi3Functions.h +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/fmi/fmi3PlatformTypes.h +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/pythonfmu/IPyState.hpp +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/__init__.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/conftest.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/data/csvdemo.csv +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/data/hello.txt +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/slaves/PythonSlaveWithException.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/slaves/localmodule.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/slaves/pythonslave.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/slaves/pythonslave_read_file.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/slaves/slavewithdep.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/test_builder.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/test_deploy.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/test_logger.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/test_multiple_fmus.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/test_variables.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/tests/utils.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/unit.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/variable_types.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3/variables.py +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3.egg-info/dependency_links.txt +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3.egg-info/entry_points.txt +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3.egg-info/not-zip-safe +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3.egg-info/requires.txt +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/pythonfmu3.egg-info/top_level.txt +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/setup.cfg +0 -0
- {pythonfmu3-0.2.4 → pythonfmu3-0.3.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonfmu3
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A lightweight framework that enables the packaging of Python3.x code as co-simulation FMUs.
|
|
5
5
|
Home-page: https://github.com/stephensmith25/PythonFMU3
|
|
6
6
|
Author: Stephen Smith
|
|
@@ -27,7 +27,7 @@ Dynamic: license-file
|
|
|
27
27
|
|
|
28
28
|
# PythonFMU3
|
|
29
29
|
|
|
30
|
-
> A lightweight framework that enables the packaging of Python 3 code as
|
|
30
|
+
> A lightweight framework that enables the packaging of Python 3 code as FMUs (following FMI version 3.0).
|
|
31
31
|
|
|
32
32
|
[](https://opensource.org/licenses/MIT)
|
|
33
33
|
[](https://github.com/StephenSmith25/PythonFMU3/issues)
|
|
@@ -42,14 +42,16 @@ This project is a fork of the original PythonFMU repository available at https:/
|
|
|
42
42
|
### Support:
|
|
43
43
|
|
|
44
44
|
Please take a look at the examples to see the supported features.
|
|
45
|
+
We currently support both the Model exchange and Cosimulation Interfaces.
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
For both interfaces event mode is not supported.
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
We currently do not support the following methods (beyond a default, trivial implementation)
|
|
49
50
|
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
51
|
+
- fmi3UpdateDiscreteStates
|
|
52
|
+
- fmi3GetNumberOfEventindicators
|
|
53
|
+
- fmi3GetNominalsOfContinuousStates
|
|
54
|
+
- fmi3EvaluateDiscreteStates
|
|
53
55
|
|
|
54
56
|
### How do I build an FMU from python code?
|
|
55
57
|
|
|
@@ -123,7 +125,7 @@ optional arguments:
|
|
|
123
125
|
Requirements or environment file.
|
|
124
126
|
```
|
|
125
127
|
|
|
126
|
-
### Example:
|
|
128
|
+
### Cosimulation Example:
|
|
127
129
|
|
|
128
130
|
#### Write the script
|
|
129
131
|
|
|
@@ -169,3 +171,54 @@ pythonfmu3 build -f pythonslave.py myproject
|
|
|
169
171
|
In this example a python class named `PythonSlave` that extends `Fmi3Slave` is declared in a file named `pythonslave.py`,
|
|
170
172
|
where `myproject` is an optional folder containing additional project files required by the python script.
|
|
171
173
|
Project folders such as this will be recursively copied into the FMU. Multiple project files/folders may be added.
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
### Model Exchange Example
|
|
177
|
+
|
|
178
|
+
To create a model exchange FMU you must inherit from `Fmi3SlaveBase` and one of (or both) mixin classes `ModelExchange` or `CoSimulation`.
|
|
179
|
+
|
|
180
|
+
#### Write the script
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
|
|
184
|
+
from pythonfmu3 import Fmi3Causality, Fmi3SlaveBase, Float64, ModelExchange
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class PythonSlaveMX(Fmi3SlaveBase, ModelExchange):
|
|
188
|
+
|
|
189
|
+
def __init__(self, **kwargs):
|
|
190
|
+
super().__init__(**kwargs)
|
|
191
|
+
|
|
192
|
+
self.author = "John Doe"
|
|
193
|
+
self.description = "A simple description"
|
|
194
|
+
|
|
195
|
+
self.time = 0.0
|
|
196
|
+
self.mu = 1.0
|
|
197
|
+
self.x0 = 2
|
|
198
|
+
self.x1 = 0
|
|
199
|
+
self.derx0 = 0.0
|
|
200
|
+
self.derx1 = 0.0
|
|
201
|
+
|
|
202
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
203
|
+
self.register_variable(Float64("x0", causality=Fmi3Causality.output, start=2, variability=Fmi3Variability.continuous, initial=Fmi3Initial.exact))
|
|
204
|
+
self.register_variable(Float64("x1", causality=Fmi3Causality.output, start=0, variability=Fmi3Variability.continuous, initial=Fmi3Initial.exact))
|
|
205
|
+
self.register_variable(Float64("derx0", causality=Fmi3Causality.local, variability=Fmi3Variability.continuous, derivative=1))
|
|
206
|
+
self.register_variable(Float64("derx1", causality=Fmi3Causality.local, variability=Fmi3Variability.continuous, derivative=2))
|
|
207
|
+
self.register_variable(Float64("mu", causality=Fmi3Causality.parameter, variability=Fmi3Variability.fixed))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def get_continuous_state_derivatives(self) -> List[float]:
|
|
211
|
+
self.derx0 = self.x1
|
|
212
|
+
self.derx1 = self.mu * ((1 - self.x0**2) * self.x1) - self.x0
|
|
213
|
+
return [self.derx0, self.derx1]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Note: There is a hard requirement that we have a `self.time` member in the derived class.
|
|
219
|
+
|
|
220
|
+
#### Create the FMU
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
pythonfmu3 build -f pythonslaveMX.py
|
|
224
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# PythonFMU3
|
|
2
2
|
|
|
3
|
-
> A lightweight framework that enables the packaging of Python 3 code as
|
|
3
|
+
> A lightweight framework that enables the packaging of Python 3 code as FMUs (following FMI version 3.0).
|
|
4
4
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://github.com/StephenSmith25/PythonFMU3/issues)
|
|
@@ -15,14 +15,16 @@ This project is a fork of the original PythonFMU repository available at https:/
|
|
|
15
15
|
### Support:
|
|
16
16
|
|
|
17
17
|
Please take a look at the examples to see the supported features.
|
|
18
|
+
We currently support both the Model exchange and Cosimulation Interfaces.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
For both interfaces event mode is not supported.
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
We currently do not support the following methods (beyond a default, trivial implementation)
|
|
22
23
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
24
|
+
- fmi3UpdateDiscreteStates
|
|
25
|
+
- fmi3GetNumberOfEventindicators
|
|
26
|
+
- fmi3GetNominalsOfContinuousStates
|
|
27
|
+
- fmi3EvaluateDiscreteStates
|
|
26
28
|
|
|
27
29
|
### How do I build an FMU from python code?
|
|
28
30
|
|
|
@@ -96,7 +98,7 @@ optional arguments:
|
|
|
96
98
|
Requirements or environment file.
|
|
97
99
|
```
|
|
98
100
|
|
|
99
|
-
### Example:
|
|
101
|
+
### Cosimulation Example:
|
|
100
102
|
|
|
101
103
|
#### Write the script
|
|
102
104
|
|
|
@@ -142,3 +144,54 @@ pythonfmu3 build -f pythonslave.py myproject
|
|
|
142
144
|
In this example a python class named `PythonSlave` that extends `Fmi3Slave` is declared in a file named `pythonslave.py`,
|
|
143
145
|
where `myproject` is an optional folder containing additional project files required by the python script.
|
|
144
146
|
Project folders such as this will be recursively copied into the FMU. Multiple project files/folders may be added.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
### Model Exchange Example
|
|
150
|
+
|
|
151
|
+
To create a model exchange FMU you must inherit from `Fmi3SlaveBase` and one of (or both) mixin classes `ModelExchange` or `CoSimulation`.
|
|
152
|
+
|
|
153
|
+
#### Write the script
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
|
|
157
|
+
from pythonfmu3 import Fmi3Causality, Fmi3SlaveBase, Float64, ModelExchange
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class PythonSlaveMX(Fmi3SlaveBase, ModelExchange):
|
|
161
|
+
|
|
162
|
+
def __init__(self, **kwargs):
|
|
163
|
+
super().__init__(**kwargs)
|
|
164
|
+
|
|
165
|
+
self.author = "John Doe"
|
|
166
|
+
self.description = "A simple description"
|
|
167
|
+
|
|
168
|
+
self.time = 0.0
|
|
169
|
+
self.mu = 1.0
|
|
170
|
+
self.x0 = 2
|
|
171
|
+
self.x1 = 0
|
|
172
|
+
self.derx0 = 0.0
|
|
173
|
+
self.derx1 = 0.0
|
|
174
|
+
|
|
175
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
176
|
+
self.register_variable(Float64("x0", causality=Fmi3Causality.output, start=2, variability=Fmi3Variability.continuous, initial=Fmi3Initial.exact))
|
|
177
|
+
self.register_variable(Float64("x1", causality=Fmi3Causality.output, start=0, variability=Fmi3Variability.continuous, initial=Fmi3Initial.exact))
|
|
178
|
+
self.register_variable(Float64("derx0", causality=Fmi3Causality.local, variability=Fmi3Variability.continuous, derivative=1))
|
|
179
|
+
self.register_variable(Float64("derx1", causality=Fmi3Causality.local, variability=Fmi3Variability.continuous, derivative=2))
|
|
180
|
+
self.register_variable(Float64("mu", causality=Fmi3Causality.parameter, variability=Fmi3Variability.fixed))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def get_continuous_state_derivatives(self) -> List[float]:
|
|
184
|
+
self.derx0 = self.x1
|
|
185
|
+
self.derx1 = self.mu * ((1 - self.x0**2) * self.x1) - self.x0
|
|
186
|
+
return [self.derx0, self.derx1]
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Note: There is a hard requirement that we have a `self.time` member in the derived class.
|
|
192
|
+
|
|
193
|
+
#### Create the FMU
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
pythonfmu3 build -f pythonslaveMX.py
|
|
197
|
+
```
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from ._version import __version__
|
|
2
2
|
from .builder import FmuBuilder
|
|
3
|
+
from .cosimulation import CoSimulation
|
|
4
|
+
from .modelexchange import ModelExchange
|
|
3
5
|
from .enums import Fmi3Causality, Fmi3Initial, Fmi3Status, Fmi3Variability
|
|
4
|
-
from .fmi3slave import Fmi3Slave, Fmi3StepResult
|
|
6
|
+
from .fmi3slave import Fmi3Slave, Fmi3SlaveBase, Fmi3StepResult
|
|
5
7
|
from .variables import Boolean, Enumeration, Int32, Int64, UInt64, Float64, String, Dimension
|
|
6
8
|
from .default_experiment import DefaultExperiment
|
|
7
9
|
from .variable_types import Float64Type, EnumerationType
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -13,7 +13,7 @@ from typing import Iterable, Optional, Tuple, Union
|
|
|
13
13
|
from xml.dom.minidom import parseString
|
|
14
14
|
from xml.etree.ElementTree import Element, SubElement, tostring
|
|
15
15
|
from .osutil import get_lib_extension, get_platform
|
|
16
|
-
from .fmi3slave import FMI3_MODEL_OPTIONS, Fmi3Slave
|
|
16
|
+
from .fmi3slave import FMI3_MODEL_OPTIONS, Fmi3Slave, Fmi3SlaveBase
|
|
17
17
|
|
|
18
18
|
FilePath = Union[str, Path]
|
|
19
19
|
HERE = Path(__file__).parent
|
|
@@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
|
|
|
24
24
|
def get_class_name(file_name: Path) -> str:
|
|
25
25
|
with open(str(file_name), 'r') as file:
|
|
26
26
|
data = file.read()
|
|
27
|
-
return re.search(r'class (\w+)\(
|
|
27
|
+
return re.search(r'class (\w+)\(([^)]*\bFmi3Slave(?:Base)?\b[^)]*)\)\s*:', data).group(1)
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def get_model_description(filepath: Path, module_name: str) -> Tuple[str, Element]:
|
|
@@ -50,9 +50,9 @@ def get_model_description(filepath: Path, module_name: str) -> Tuple[str, Elemen
|
|
|
50
50
|
finally:
|
|
51
51
|
sys.path.remove(str(filepath.parent)) # remove inserted temporary path
|
|
52
52
|
|
|
53
|
-
if not isinstance(instance,
|
|
53
|
+
if not isinstance(instance, Fmi3SlaveBase):
|
|
54
54
|
raise TypeError(
|
|
55
|
-
f"The provided class '{class_name}' does not inherit from {
|
|
55
|
+
f"The provided class '{class_name}' does not inherit from {Fmi3SlaveBase.__qualname__}"
|
|
56
56
|
)
|
|
57
57
|
# Produce the xml
|
|
58
58
|
return instance.modelName, instance.to_xml()
|
|
@@ -138,9 +138,16 @@ class FmuBuilder:
|
|
|
138
138
|
|
|
139
139
|
type_node = xml.find("CoSimulation")
|
|
140
140
|
option_names = [opt.name for opt in FMI3_MODEL_OPTIONS]
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
if type_node:
|
|
142
|
+
for option, value in options.items():
|
|
143
|
+
if option in option_names:
|
|
144
|
+
type_node.set(option, str(value).lower())
|
|
145
|
+
|
|
146
|
+
type_node = xml.find("ModelExchange")
|
|
147
|
+
if type_node:
|
|
148
|
+
for option, value in options.items():
|
|
149
|
+
if option in option_names:
|
|
150
|
+
type_node.set(option, str(value).lower())
|
|
144
151
|
|
|
145
152
|
with zipfile.ZipFile(dest_file, "w") as zip_fmu:
|
|
146
153
|
|
|
@@ -11,6 +11,8 @@ from xml.etree.ElementTree import Element, SubElement
|
|
|
11
11
|
|
|
12
12
|
from .logmsg import LogMsg
|
|
13
13
|
from .default_experiment import DefaultExperiment
|
|
14
|
+
from .cosimulation import CoSimulation
|
|
15
|
+
from .modelexchange import ModelExchange
|
|
14
16
|
from ._version import __version__ as VERSION
|
|
15
17
|
from .enums import Fmi3Type, Fmi3Status, Fmi3Causality, Fmi3Initial, Fmi3Variability
|
|
16
18
|
from .variables import Boolean, Enumeration, Int32, Int64, UInt64, Float64, ModelVariable, String
|
|
@@ -33,7 +35,8 @@ class Fmi3StepResult(NamedTuple):
|
|
|
33
35
|
terminateSimulation: bool = False
|
|
34
36
|
earlyReturn: bool = False
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
|
|
39
|
+
class Fmi3SlaveBase(object):
|
|
37
40
|
"""Abstract facade class to execute Python through FMI standard."""
|
|
38
41
|
|
|
39
42
|
# Dictionary of (category, description) entries
|
|
@@ -104,8 +107,18 @@ class Fmi3Slave(ABC):
|
|
|
104
107
|
options[option.name] = str(value).lower()
|
|
105
108
|
options["modelIdentifier"] = self.modelName
|
|
106
109
|
options["canNotUseMemoryManagementFunctions"] = "true"
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
|
|
111
|
+
options_me = dict()
|
|
112
|
+
options_me["canGetAndSetFMUState"] = "true"
|
|
113
|
+
options_me["modelIdentifier"] = self.modelName
|
|
114
|
+
options_me["needsCompletedIntegratorStep"] = "false"
|
|
115
|
+
|
|
116
|
+
# check if we have cosim mixin or model exchange mixin
|
|
117
|
+
if isinstance(self, ModelExchange):
|
|
118
|
+
SubElement(root, "ModelExchange", attrib=options_me)
|
|
119
|
+
|
|
120
|
+
if isinstance(self, CoSimulation):
|
|
121
|
+
SubElement(root, "CoSimulation", attrib=options)
|
|
109
122
|
|
|
110
123
|
if self.units:
|
|
111
124
|
unit_defs = SubElement(root, "UnitDefinitions")
|
|
@@ -239,7 +252,6 @@ class Fmi3Slave(ABC):
|
|
|
239
252
|
def exit_initialization_mode(self):
|
|
240
253
|
pass
|
|
241
254
|
|
|
242
|
-
@abstractmethod
|
|
243
255
|
def do_step(self, current_time: float, step_size: float) -> Fmi3StepResult:
|
|
244
256
|
pass
|
|
245
257
|
|
|
@@ -404,6 +416,50 @@ class Fmi3Slave(ABC):
|
|
|
404
416
|
if v.setter is not None:
|
|
405
417
|
v.setter(value)
|
|
406
418
|
|
|
419
|
+
def set_continuous_states(self, values: List[float]):
|
|
420
|
+
offset = 0
|
|
421
|
+
continuous_state_derivatives = list(
|
|
422
|
+
filter(lambda v: v.variability == Fmi3Variability.continuous and (isinstance(v, Float64) and v.derivative is not None), self.vars.values())
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
vrs = [v.derivative for v in continuous_state_derivatives]
|
|
426
|
+
|
|
427
|
+
for vr in vrs:
|
|
428
|
+
var = self.vars[vr]
|
|
429
|
+
size = var.size(self.vars)
|
|
430
|
+
if size > 1:
|
|
431
|
+
var.setter(values[offset:offset+size])
|
|
432
|
+
else:
|
|
433
|
+
var.setter(values[offset])
|
|
434
|
+
offset += size
|
|
435
|
+
|
|
436
|
+
def get_continuous_states(self) -> List[float]:
|
|
437
|
+
offset = 0
|
|
438
|
+
continuous_state_derivatives = list(
|
|
439
|
+
filter(lambda v: v.variability == Fmi3Variability.continuous and (isinstance(v, Float64) and v.derivative is not None), self.vars.values())
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
vrs = [v.derivative for v in continuous_state_derivatives]
|
|
443
|
+
|
|
444
|
+
refs = list()
|
|
445
|
+
for vr in vrs:
|
|
446
|
+
var = self.vars[vr]
|
|
447
|
+
if len(var.dimensions) == 0:
|
|
448
|
+
refs.append(float(var.getter()))
|
|
449
|
+
else:
|
|
450
|
+
refs.extend(var.getter())
|
|
451
|
+
|
|
452
|
+
return refs
|
|
453
|
+
|
|
454
|
+
def get_number_of_continuous_states(self) -> int:
|
|
455
|
+
continuous_state_derivatives = list(
|
|
456
|
+
filter(lambda v: v.variability == Fmi3Variability.continuous and (isinstance(v, Float64) and v.derivative is not None), self.vars.values())
|
|
457
|
+
)
|
|
458
|
+
return len(continuous_state_derivatives)
|
|
459
|
+
|
|
460
|
+
def set_time(self, time: float):
|
|
461
|
+
self.time = time
|
|
462
|
+
|
|
407
463
|
@staticmethod
|
|
408
464
|
def _fmu_state_to_bytes(state: Dict[str, Any]) -> bytes:
|
|
409
465
|
return json.dumps(state).encode("utf-8")
|
|
@@ -436,3 +492,6 @@ class Fmi3Slave(ABC):
|
|
|
436
492
|
category = "logAll"
|
|
437
493
|
log_msg = LogMsg(status, category, msg, debug)
|
|
438
494
|
self.log_queue.append(log_msg)
|
|
495
|
+
|
|
496
|
+
class Fmi3Slave(Fmi3SlaveBase, CoSimulation):
|
|
497
|
+
pass
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from abc import ABC, ABCMeta, abstractmethod
|
|
2
|
+
|
|
3
|
+
class RequireTimeMeta(ABCMeta):
|
|
4
|
+
def __init__(cls, name, bases, namespace):
|
|
5
|
+
super().__init__(name, bases, namespace)
|
|
6
|
+
orig_init = cls.__init__
|
|
7
|
+
def new_init(self, *args, **kwargs):
|
|
8
|
+
orig_init(self, *args, **kwargs)
|
|
9
|
+
if not hasattr(self, 'time'):
|
|
10
|
+
raise AttributeError(f"{cls.__name__} must define 'self.time' in __init__")
|
|
11
|
+
cls.__init__ = new_init
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# model exchange mixin
|
|
15
|
+
class ModelExchange(ABC, metaclass=RequireTimeMeta):
|
|
16
|
+
"""
|
|
17
|
+
Classes derived from ModelExchange must define a 'self.time' member variable.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def get_continuous_state_derivatives(self):
|
|
22
|
+
"""Return the continuous state derivatives of the model."""
|
|
23
|
+
pass
|
|
@@ -133,6 +133,23 @@ public:
|
|
|
133
133
|
FMIBoolean* earlyReturn,
|
|
134
134
|
FMIFloat64& endOfStep) = 0;
|
|
135
135
|
|
|
136
|
+
// Model Exchange methods
|
|
137
|
+
virtual void GetContinuousStates(cppfmu::FMIFloat64* continuousStates, std::size_t nStates) const = 0;
|
|
138
|
+
virtual void GetContinuousStateDerivatives(cppfmu::FMIFloat64* derivatives, std::size_t nStates) const = 0;
|
|
139
|
+
virtual void GetNominalsOfContinuousStates(cppfmu::FMIFloat64* nominalContinuousStates, std::size_t nStates) const = 0;
|
|
140
|
+
virtual void SetContinuousStates(const cppfmu::FMIFloat64* continuousStates, std::size_t nStates) = 0;
|
|
141
|
+
virtual void SetTime(cppfmu::FMIFloat64 time) = 0;
|
|
142
|
+
virtual void GetNumberOfContinuousStates(std::size_t& nStates) const = 0;
|
|
143
|
+
virtual void GetNumberOfEventIndicators(std::size_t& nEventIndicators) const = 0;
|
|
144
|
+
virtual void UpdateDiscreteStates(
|
|
145
|
+
cppfmu::FMIBoolean* discreteStatesNeedUpdate,
|
|
146
|
+
cppfmu::FMIBoolean* terminateSimulation,
|
|
147
|
+
cppfmu::FMIBoolean* nominalContinuousStatesChanged,
|
|
148
|
+
cppfmu::FMIBoolean* valuesOfContinuousStatesChanged,
|
|
149
|
+
cppfmu::FMIBoolean* nextEventTimeDefined,
|
|
150
|
+
cppfmu::FMIFloat64* nextEventTime) = 0;
|
|
151
|
+
|
|
152
|
+
|
|
136
153
|
virtual void GetFMUstate(fmi3FMUState& state) = 0;
|
|
137
154
|
virtual void SetFMUstate(const fmi3FMUState& state) = 0;
|
|
138
155
|
virtual void FreeFMUstate(fmi3FMUState& state) = 0;
|