pythonfmu3 0.2.4__tar.gz → 0.3.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.
Files changed (68) hide show
  1. {pythonfmu3-0.2.4/pythonfmu3.egg-info → pythonfmu3-0.3.1}/PKG-INFO +61 -8
  2. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/README.md +60 -7
  3. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/__init__.py +3 -1
  4. pythonfmu3-0.3.1/pythonfmu3/_version.py +1 -0
  5. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/builder.py +14 -7
  6. pythonfmu3-0.3.1/pythonfmu3/cosimulation.py +8 -0
  7. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/fmi3slave.py +63 -4
  8. pythonfmu3-0.3.1/pythonfmu3/modelexchange.py +23 -0
  9. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp +17 -0
  10. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp +215 -82
  11. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp +105 -2
  12. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp +17 -0
  13. pythonfmu3-0.3.1/pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
  14. pythonfmu3-0.3.1/pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
  15. pythonfmu3-0.3.1/pythonfmu3/tests/slaves/pythonslaveMX.py +27 -0
  16. pythonfmu3-0.3.1/pythonfmu3/tests/slaves/pythonslaveMXCS.py +32 -0
  17. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/test_fmi3slave.py +20 -1
  18. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/test_integration.py +61 -0
  19. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1/pythonfmu3.egg-info}/PKG-INFO +61 -8
  20. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3.egg-info/SOURCES.txt +4 -0
  21. pythonfmu3-0.2.4/pythonfmu3/_version.py +0 -1
  22. pythonfmu3-0.2.4/pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
  23. pythonfmu3-0.2.4/pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
  24. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/LICENSE +0 -0
  25. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/MANIFEST.in +0 -0
  26. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/__main__.py +0 -0
  27. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/default_experiment.py +0 -0
  28. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/deploy.py +0 -0
  29. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/enums.py +0 -0
  30. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/logmsg.py +0 -0
  31. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/osutil.py +0 -0
  32. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/.clang-format +0 -0
  33. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/CMakeLists.txt +0 -0
  34. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/build_unix.sh +0 -0
  35. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/build_win.sh +0 -0
  36. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/CMakeLists.txt +0 -0
  37. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp +0 -0
  38. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.cpp +0 -0
  39. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/fmi/fmi3FunctionTypes.h +0 -0
  40. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/fmi/fmi3Functions.h +0 -0
  41. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/fmi/fmi3PlatformTypes.h +0 -0
  42. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/pythonfmu/IPyState.hpp +0 -0
  43. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp +0 -0
  44. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/__init__.py +0 -0
  45. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/conftest.py +0 -0
  46. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/data/csvdemo.csv +0 -0
  47. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/data/hello.txt +0 -0
  48. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/slaves/PythonSlaveWithException.py +0 -0
  49. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/slaves/localmodule.py +0 -0
  50. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/slaves/pythonslave.py +0 -0
  51. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/slaves/pythonslave_read_file.py +0 -0
  52. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/slaves/slavewithdep.py +0 -0
  53. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/test_builder.py +0 -0
  54. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/test_deploy.py +0 -0
  55. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/test_logger.py +0 -0
  56. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/test_multiple_fmus.py +0 -0
  57. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/test_variables.py +0 -0
  58. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/tests/utils.py +0 -0
  59. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/unit.py +0 -0
  60. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/variable_types.py +0 -0
  61. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3/variables.py +0 -0
  62. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3.egg-info/dependency_links.txt +0 -0
  63. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3.egg-info/entry_points.txt +0 -0
  64. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3.egg-info/not-zip-safe +0 -0
  65. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3.egg-info/requires.txt +0 -0
  66. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/pythonfmu3.egg-info/top_level.txt +0 -0
  67. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/setup.cfg +0 -0
  68. {pythonfmu3-0.2.4 → pythonfmu3-0.3.1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonfmu3
3
- Version: 0.2.4
3
+ Version: 0.3.1
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 co-simulation FMUs (following FMI version 3.0).
30
+ > A lightweight framework that enables the packaging of Python 3 code as FMUs (following FMI version 3.0).
31
31
 
32
32
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
33
33
  [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](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
- ### Future
47
+ For both interfaces event mode is not supported.
47
48
 
48
- In no particular order, we plan to add support for:
49
+ We currently do not support the following methods (beyond a default, trivial implementation)
49
50
 
50
- - Support more variable types from FMI3
51
- - Improve array support
52
- - Add event mode
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 co-simulation FMUs (following FMI version 3.0).
3
+ > A lightweight framework that enables the packaging of Python 3 code as FMUs (following FMI version 3.0).
4
4
 
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](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
- ### Future
20
+ For both interfaces event mode is not supported.
20
21
 
21
- In no particular order, we plan to add support for:
22
+ We currently do not support the following methods (beyond a default, trivial implementation)
22
23
 
23
- - Support more variable types from FMI3
24
- - Improve array support
25
- - Add event mode
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.1"
@@ -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+)\(\s*Fmi3Slave\s*\)\s*:', data).group(1)
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, Fmi3Slave):
53
+ if not isinstance(instance, Fmi3SlaveBase):
54
54
  raise TypeError(
55
- f"The provided class '{class_name}' does not inherit from {Fmi3Slave.__qualname__}"
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
- for option, value in options.items():
142
- if option in option_names:
143
- type_node.set(option, str(value).lower())
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
 
@@ -0,0 +1,8 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ # co-simulation mixin
4
+ class CoSimulation(ABC):
5
+
6
+ @abstractmethod
7
+ def do_step(self, current_time: float, step_size: float):
8
+ pass
@@ -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
- class Fmi3Slave(ABC):
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
- SubElement(root, "CoSimulation", attrib=options)
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;