pythonfmu3 0.2.2.2__py3-none-any.whl → 0.2.3__py3-none-any.whl
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/__init__.py +8 -8
- pythonfmu3/__main__.py +52 -52
- pythonfmu3/_version.py +1 -1
- pythonfmu3/builder.py +260 -260
- pythonfmu3/default_experiment.py +7 -7
- pythonfmu3/deploy.py +118 -118
- pythonfmu3/enums.py +48 -48
- pythonfmu3/fmi3slave.py +438 -438
- pythonfmu3/logmsg.py +14 -14
- pythonfmu3/osutil.py +22 -22
- pythonfmu3/pythonfmu-export/.clang-format +35 -35
- pythonfmu3/pythonfmu-export/CMakeLists.txt +59 -59
- pythonfmu3/pythonfmu-export/build_unix.sh +4 -4
- pythonfmu3/pythonfmu-export/build_win.sh +4 -4
- pythonfmu3/pythonfmu-export/src/CMakeLists.txt +45 -45
- pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp +161 -161
- pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.cpp +192 -192
- pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp +185 -185
- pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp +937 -937
- pythonfmu3/pythonfmu-export/src/fmi/fmi3FunctionTypes.h +638 -638
- pythonfmu3/pythonfmu-export/src/fmi/fmi3Functions.h +343 -343
- pythonfmu3/pythonfmu-export/src/fmi/fmi3PlatformTypes.h +94 -94
- pythonfmu3/pythonfmu-export/src/pythonfmu/IPyState.hpp +17 -17
- pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp +744 -744
- pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp +111 -111
- pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp +84 -84
- pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
- pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
- pythonfmu3/tests/conftest.py +5 -5
- pythonfmu3/tests/data/csvdemo.csv +7 -7
- pythonfmu3/tests/slaves/PythonSlaveWithException.py +18 -18
- pythonfmu3/tests/slaves/localmodule.py +8 -8
- pythonfmu3/tests/slaves/pythonslave.py +52 -52
- pythonfmu3/tests/slaves/pythonslave_read_file.py +23 -23
- pythonfmu3/tests/slaves/slavewithdep.py +21 -21
- pythonfmu3/tests/test_builder.py +195 -195
- pythonfmu3/tests/test_deploy.py +48 -48
- pythonfmu3/tests/test_fmi3slave.py +152 -152
- pythonfmu3/tests/test_integration.py +350 -350
- pythonfmu3/tests/test_logger.py +180 -180
- pythonfmu3/tests/test_multiple_fmus.py +141 -141
- pythonfmu3/tests/test_variables.py +334 -334
- pythonfmu3/tests/utils.py +13 -13
- pythonfmu3/unit.py +62 -62
- pythonfmu3/variable_types.py +101 -101
- pythonfmu3/variables.py +400 -400
- {pythonfmu3-0.2.2.2.dist-info → pythonfmu3-0.2.3.dist-info}/METADATA +7 -9
- pythonfmu3-0.2.3.dist-info/RECORD +54 -0
- {pythonfmu3-0.2.2.2.dist-info → pythonfmu3-0.2.3.dist-info}/WHEEL +1 -1
- {pythonfmu3-0.2.2.2.dist-info → pythonfmu3-0.2.3.dist-info}/entry_points.txt +0 -1
- pythonfmu3-0.2.2.2.dist-info/LICENSE.txt → pythonfmu3-0.2.3.dist-info/licenses/LICENSE +23 -23
- pythonfmu3/pythonfmu-export/build/ALL_BUILD.vcxproj +0 -184
- pythonfmu3/pythonfmu-export/build/ALL_BUILD.vcxproj.filters +0 -8
- pythonfmu3/pythonfmu-export/build/CMakeCache.txt +0 -369
- pythonfmu3/pythonfmu-export/build/CMakeFiles/0ee4db0675778dfba98c7160896d5a45/generate.stamp.rule +0 -1
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeCCompiler.cmake +0 -72
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeCXXCompiler.cmake +0 -83
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeDetermineCompilerABI_C.bin +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeDetermineCompilerABI_CXX.bin +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeRCCompiler.cmake +0 -6
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeSystem.cmake +0 -15
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CMakeCCompilerId.c +0 -838
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CompilerIdC.exe +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CompilerIdC.vcxproj +0 -71
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CMakeCCompilerId.obj +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.exe.recipe +0 -11
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CompilerIdC.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -826
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CompilerIdCXX.exe +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CompilerIdCXX.vcxproj +0 -71
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +0 -11
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/VCTargetsPath.txt +0 -1
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/VCTargetsPath.vcxproj +0 -31
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/x64/Debug/VCTargetsPath.recipe +0 -11
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeCCompiler.cmake +0 -81
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeCXXCompiler.cmake +0 -101
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeDetermineCompilerABI_C.bin +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeRCCompiler.cmake +0 -6
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeSystem.cmake +0 -15
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CMakeCCompilerId.c +0 -904
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CompilerIdC.exe +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CompilerIdC.vcxproj +0 -72
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CMakeCCompilerId.obj +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.exe.recipe +0 -11
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CompilerIdC.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -919
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CompilerIdCXX.exe +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CompilerIdCXX.vcxproj +0 -72
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +0 -11
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/VCTargetsPath.txt +0 -1
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/VCTargetsPath.vcxproj +0 -31
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/x64/Debug/VCTargetsPath.recipe +0 -11
- pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/CMakeFiles/CMakeConfigureLog.yaml +0 -568
- pythonfmu3/pythonfmu-export/build/CMakeFiles/CMakeOutput.log +0 -357
- pythonfmu3/pythonfmu-export/build/CMakeFiles/TargetDirectories.txt +0 -3
- pythonfmu3/pythonfmu-export/build/CMakeFiles/cmake.check_cache +0 -1
- pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp +0 -1
- pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp.depend +0 -28
- pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp.list +0 -2
- pythonfmu3/pythonfmu-export/build/ZERO_CHECK.vcxproj +0 -174
- pythonfmu3/pythonfmu-export/build/ZERO_CHECK.vcxproj.filters +0 -13
- pythonfmu3/pythonfmu-export/build/cmake_install.cmake +0 -50
- pythonfmu3/pythonfmu-export/build/install_manifest.txt +0 -0
- pythonfmu3/pythonfmu-export/build/pythonfmu-export.sln +0 -53
- pythonfmu3/pythonfmu-export/build/src/CMakeFiles/generate.stamp +0 -1
- pythonfmu3/pythonfmu-export/build/src/CMakeFiles/generate.stamp.depend +0 -2
- pythonfmu3/pythonfmu-export/build/src/Debug/pythonfmu-export.pdb +0 -0
- pythonfmu3/pythonfmu-export/build/src/Release/pythonfmu-export.exp +0 -0
- pythonfmu3/pythonfmu-export/build/src/Release/pythonfmu-export.lib +0 -0
- pythonfmu3/pythonfmu-export/build/src/cmake_install.cmake +0 -34
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/PySlaveInstance.obj +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/cppfmu_cs.obj +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/exports.def +0 -1885
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/fmi_functions.obj +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/objects.txt +0 -3
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.command.1.tlog +0 -10
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.read.1.tlog +0 -1
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.write.1.tlog +0 -2
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.command.1.tlog +0 -1
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.read.1.tlog +0 -1
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.read.3.tlog +0 -1
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.write.1.tlog +0 -1
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/pythonfmu-export.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/unsuccessfulbuild +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/vc142.pdb +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/MinSizeRel/objects.txt +0 -3
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/RelWithDebInfo/objects.txt +0 -3
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/PySlaveInstance.obj +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/cppfmu_cs.obj +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/exports.def +0 -538
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/fmi_functions.obj +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/objects.txt +0 -3
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.dll.recipe +0 -14
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.command.1.tlog +0 -10
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.read.1.tlog +0 -1
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.write.1.tlog +0 -2
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.command.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.delete.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.read.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.write.1.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/pythonfmu-export.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/pythonfmu-export.write.1u.tlog +0 -0
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.vcxproj +0 -414
- pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.vcxproj.filters +0 -48
- pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.recipe +0 -11
- pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +0 -10
- pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +0 -29
- pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +0 -3
- pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.recipe +0 -17
- pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate +0 -2
- pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog +0 -10
- pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog +0 -27
- pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog +0 -2
- pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe +0 -11
- pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +0 -10
- pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +0 -29
- pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +0 -3
- pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +0 -2
- pythonfmu3/resources/binaries/x86_64-windows1/pythonfmu-export.dll +0 -0
- pythonfmu3-0.2.2.2.dist-info/RECORD +0 -200
- {pythonfmu3-0.2.2.2.dist-info → pythonfmu3-0.2.3.dist-info}/top_level.txt +0 -0
pythonfmu3/fmi3slave.py
CHANGED
|
@@ -1,438 +1,438 @@
|
|
|
1
|
-
"""Define the abstract facade class."""
|
|
2
|
-
import json
|
|
3
|
-
import ctypes
|
|
4
|
-
import datetime
|
|
5
|
-
from abc import ABC, abstractmethod
|
|
6
|
-
from collections import OrderedDict, namedtuple
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Any, ClassVar, Dict, List, NamedTuple, Optional
|
|
9
|
-
from uuid import uuid1
|
|
10
|
-
from xml.etree.ElementTree import Element, SubElement
|
|
11
|
-
|
|
12
|
-
from .logmsg import LogMsg
|
|
13
|
-
from .default_experiment import DefaultExperiment
|
|
14
|
-
from ._version import __version__ as VERSION
|
|
15
|
-
from .enums import Fmi3Type, Fmi3Status, Fmi3Causality, Fmi3Initial, Fmi3Variability
|
|
16
|
-
from .variables import Boolean, Enumeration, Int32, Int64, UInt64, Float64, ModelVariable, String
|
|
17
|
-
from .variable_types import VariableType
|
|
18
|
-
from .unit import Unit
|
|
19
|
-
|
|
20
|
-
ModelOptions = namedtuple("ModelOptions", ["name", "value", "cli"])
|
|
21
|
-
|
|
22
|
-
FMI3_MODEL_OPTIONS: List[ModelOptions] = [
|
|
23
|
-
ModelOptions("needsExecutionTool", True, "no-external-tool"),
|
|
24
|
-
ModelOptions("canHandleVariableCommunicationStepSize", True, "no-variable-step"),
|
|
25
|
-
ModelOptions("canBeInstantiatedOnlyOncePerProcess", False, "only-one-per-process"),
|
|
26
|
-
ModelOptions("canGetAndSetFMUState", False, "handle-state"),
|
|
27
|
-
ModelOptions("canSerializeFMUState", False, "serialize-state")
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
class Fmi3StepResult(NamedTuple):
|
|
31
|
-
status: Fmi3Status = Fmi3Status.ok
|
|
32
|
-
eventHandlingNeeded: bool = False
|
|
33
|
-
terminateSimulation: bool = False
|
|
34
|
-
earlyReturn: bool = False
|
|
35
|
-
|
|
36
|
-
class Fmi3Slave(ABC):
|
|
37
|
-
"""Abstract facade class to execute Python through FMI standard."""
|
|
38
|
-
|
|
39
|
-
# Dictionary of (category, description) entries
|
|
40
|
-
log_categories: Dict[str, str] = {
|
|
41
|
-
"logStatusWarning": "Log messages with fmi3Warning status.",
|
|
42
|
-
"logStatusDiscard": "Log messages with fmi3Discard status.",
|
|
43
|
-
"logStatusError": "Log messages with fmi3Error status.",
|
|
44
|
-
"logStatusFatal": "Log messages with fmi3Fatal status.",
|
|
45
|
-
"logAll": "Log all messages."
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
def __init__(self, **kwargs):
|
|
49
|
-
self.vars = OrderedDict()
|
|
50
|
-
self.instance_name = kwargs["instance_name"]
|
|
51
|
-
self.resources = kwargs.get("resources", None)
|
|
52
|
-
self.visible = kwargs.get("visible", False)
|
|
53
|
-
self.log_queue = []
|
|
54
|
-
|
|
55
|
-
self.guid = uuid1()
|
|
56
|
-
self.author: Optional[str] = None
|
|
57
|
-
self.license: Optional[str] = None
|
|
58
|
-
self.version: Optional[str] = None
|
|
59
|
-
self.copyright: Optional[str] = None
|
|
60
|
-
self.modelName: Optional[str] = self.__class__.__name__
|
|
61
|
-
self.description: Optional[str] = None
|
|
62
|
-
self.default_experiment: Optional[DefaultExperiment] = None
|
|
63
|
-
|
|
64
|
-
self.type_definitions: Dict[str, VariableType] = {}
|
|
65
|
-
self.units: Dict[str, Unit] = {}
|
|
66
|
-
|
|
67
|
-
def to_xml(self, model_options: Dict[str, str] = dict()) -> Element:
|
|
68
|
-
"""Build the XML representation of the model.
|
|
69
|
-
|
|
70
|
-
Args:
|
|
71
|
-
model_options (Dict[str, str]) : FMU model options
|
|
72
|
-
|
|
73
|
-
Returns:
|
|
74
|
-
(xml.etree.TreeElement.Element) XML description of the FMU
|
|
75
|
-
"""
|
|
76
|
-
|
|
77
|
-
t = datetime.datetime.now(datetime.timezone.utc)
|
|
78
|
-
date_str = t.isoformat(timespec="seconds")
|
|
79
|
-
|
|
80
|
-
attrib = dict(
|
|
81
|
-
fmiVersion="3.0",
|
|
82
|
-
modelName=self.modelName,
|
|
83
|
-
instantiationToken=f"{self.guid!s}",
|
|
84
|
-
generationTool=f"PythonFMU3 {VERSION}",
|
|
85
|
-
generationDateAndTime=date_str,
|
|
86
|
-
variableNamingConvention="structured"
|
|
87
|
-
)
|
|
88
|
-
if self.description is not None:
|
|
89
|
-
attrib["description"] = self.description
|
|
90
|
-
if self.author is not None:
|
|
91
|
-
attrib["author"] = self.author
|
|
92
|
-
if self.license is not None:
|
|
93
|
-
attrib["license"] = self.license
|
|
94
|
-
if self.version is not None:
|
|
95
|
-
attrib["version"] = self.version
|
|
96
|
-
if self.copyright is not None:
|
|
97
|
-
attrib["copyright"] = self.copyright
|
|
98
|
-
|
|
99
|
-
root = Element("fmiModelDescription", attrib)
|
|
100
|
-
|
|
101
|
-
options = dict()
|
|
102
|
-
for option in FMI3_MODEL_OPTIONS:
|
|
103
|
-
value = model_options.get(option.name, option.value)
|
|
104
|
-
options[option.name] = str(value).lower()
|
|
105
|
-
options["modelIdentifier"] = self.modelName
|
|
106
|
-
options["canNotUseMemoryManagementFunctions"] = "true"
|
|
107
|
-
|
|
108
|
-
SubElement(root, "CoSimulation", attrib=options)
|
|
109
|
-
|
|
110
|
-
if self.units:
|
|
111
|
-
unit_defs = SubElement(root, "UnitDefinitions")
|
|
112
|
-
for _, unit in self.units.items():
|
|
113
|
-
unit_defs.append(unit.to_xml())
|
|
114
|
-
|
|
115
|
-
if self.type_definitions:
|
|
116
|
-
type_defs = SubElement(root, "TypeDefinitions")
|
|
117
|
-
for _, val in self.type_definitions.items():
|
|
118
|
-
type_defs.append(val.to_xml())
|
|
119
|
-
|
|
120
|
-
if len(self.log_categories) > 0:
|
|
121
|
-
categories = SubElement(root, "LogCategories")
|
|
122
|
-
for category, description in self.log_categories.items():
|
|
123
|
-
categories.append(
|
|
124
|
-
Element(
|
|
125
|
-
"Category",
|
|
126
|
-
attrib={"name": category, "description": description},
|
|
127
|
-
)
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
if self.default_experiment is not None:
|
|
131
|
-
attrib = dict()
|
|
132
|
-
if self.default_experiment.start_time is not None:
|
|
133
|
-
attrib["startTime"] = str(self.default_experiment.start_time)
|
|
134
|
-
if self.default_experiment.stop_time is not None:
|
|
135
|
-
attrib["stopTime"] = str(self.default_experiment.stop_time)
|
|
136
|
-
if self.default_experiment.step_size is not None:
|
|
137
|
-
attrib["stepSize"] = str(self.default_experiment.step_size)
|
|
138
|
-
if self.default_experiment.tolerance is not None:
|
|
139
|
-
attrib["tolerance"] = str(self.default_experiment.tolerance)
|
|
140
|
-
SubElement(root, "DefaultExperiment", attrib)
|
|
141
|
-
|
|
142
|
-
variables = SubElement(root, "ModelVariables")
|
|
143
|
-
for v in self.vars.values():
|
|
144
|
-
if ModelVariable.requires_start(v):
|
|
145
|
-
self.__apply_start_value(v)
|
|
146
|
-
variables.append(v.to_xml())
|
|
147
|
-
|
|
148
|
-
structure = SubElement(root, "ModelStructure")
|
|
149
|
-
outputs = list(
|
|
150
|
-
filter(lambda v: v.causality == Fmi3Causality.output, self.vars.values())
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
continuous_state_derivatives = list(
|
|
154
|
-
filter(lambda v: v.variability == Fmi3Variability.continuous and (isinstance(v, Float64) and v.derivative is not None), self.vars.values())
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
allowed_variability = [None, Fmi3Initial.approx, Fmi3Initial.calculated]
|
|
158
|
-
initial_unknown = list(
|
|
159
|
-
filter(lambda v: (v.causality == Fmi3Causality.output and (v.initial in allowed_variability))
|
|
160
|
-
or v.causality == Fmi3Causality.calculatedParameter
|
|
161
|
-
or v in continuous_state_derivatives and v.initial in allowed_variability
|
|
162
|
-
or v.variability == Fmi3Variability.continuous and v.initial in allowed_variability and v.causality != Fmi3Causality.independent, self.vars.values())
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
for v in outputs:
|
|
166
|
-
SubElement(structure, "Output", attrib=dict(valueReference=str(v.value_reference)))
|
|
167
|
-
|
|
168
|
-
for v in continuous_state_derivatives:
|
|
169
|
-
SubElement(structure, "ContinuousStateDerivative", attrib=dict(valueReference=str(v.value_reference)))
|
|
170
|
-
|
|
171
|
-
for v in initial_unknown:
|
|
172
|
-
SubElement(structure, "InitialUnknown", attrib=dict(valueReference=str(v.value_reference)))
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return root
|
|
177
|
-
|
|
178
|
-
def __apply_start_value(self, var: ModelVariable):
|
|
179
|
-
vrs = [var.value_reference]
|
|
180
|
-
if isinstance(var, Int32):
|
|
181
|
-
refs = self.get_int32(vrs)
|
|
182
|
-
elif isinstance(var, (Enumeration, Int64)):
|
|
183
|
-
refs = self.get_int64(vrs)
|
|
184
|
-
elif isinstance(var, UInt64):
|
|
185
|
-
refs = [val.value for val in self.get_uint64(vrs)]
|
|
186
|
-
elif isinstance(var, Float64):
|
|
187
|
-
refs = self.get_float64(vrs)
|
|
188
|
-
elif isinstance(var, Boolean):
|
|
189
|
-
refs = self.get_boolean(vrs)
|
|
190
|
-
elif isinstance(var, String):
|
|
191
|
-
refs = self.get_string(vrs)
|
|
192
|
-
else:
|
|
193
|
-
raise Exception(f"Unsupported type {type(var)}!")
|
|
194
|
-
var.start = refs if len(getattr(var, "dimensions", [])) > 0 else refs[0]
|
|
195
|
-
|
|
196
|
-
def register_variable(self, var: ModelVariable, nested: bool = True, var_type: Any = None):
|
|
197
|
-
"""Register a variable as FMU interface.
|
|
198
|
-
|
|
199
|
-
Args:
|
|
200
|
-
var (ModelVariable): The variable to be registered
|
|
201
|
-
nested (bool): Optional, does the "." in the variable name reflect an object hierarchy to access it? Default True
|
|
202
|
-
"""
|
|
203
|
-
variable_reference = len(self.vars)
|
|
204
|
-
self.vars[variable_reference] = var
|
|
205
|
-
# Set the unique value reference
|
|
206
|
-
var.value_reference = variable_reference
|
|
207
|
-
owner = self
|
|
208
|
-
if var.getter is None and nested and "." in var.name:
|
|
209
|
-
split = var.name.split(".")
|
|
210
|
-
split.pop(-1)
|
|
211
|
-
for s in split:
|
|
212
|
-
owner = getattr(owner, s)
|
|
213
|
-
if var.getter is None:
|
|
214
|
-
if hasattr(var, "dimensions") and len(var.dimensions) > 0:
|
|
215
|
-
var.getter = lambda: getattr(owner, var.local_name).flatten().tolist()
|
|
216
|
-
else:
|
|
217
|
-
var.getter = lambda: getattr(owner, var.local_name)
|
|
218
|
-
if var.setter is None and hasattr(owner, var.local_name) and var.variability != Fmi3Variability.constant:
|
|
219
|
-
if hasattr(var, "dimensions") and len(var.dimensions) > 0:
|
|
220
|
-
import numpy as np
|
|
221
|
-
var.setter = lambda v: setattr(owner, var.local_name, np.reshape(v, newshape=getattr(owner, var.local_name).shape))
|
|
222
|
-
else:
|
|
223
|
-
var.setter = lambda v: setattr(owner, var.local_name, v)
|
|
224
|
-
|
|
225
|
-
if var_type:
|
|
226
|
-
self.type_definitions[var_type.name] = var_type
|
|
227
|
-
var.declared_type = var_type.name
|
|
228
|
-
|
|
229
|
-
def setup_experiment(self, start_time: float):
|
|
230
|
-
pass
|
|
231
|
-
|
|
232
|
-
def register_units(self, units: List[Unit]):
|
|
233
|
-
for unit in units:
|
|
234
|
-
self.units[unit.name] = unit
|
|
235
|
-
|
|
236
|
-
def enter_initialization_mode(self):
|
|
237
|
-
pass
|
|
238
|
-
|
|
239
|
-
def exit_initialization_mode(self):
|
|
240
|
-
pass
|
|
241
|
-
|
|
242
|
-
@abstractmethod
|
|
243
|
-
def do_step(self, current_time: float, step_size: float) -> Fmi3StepResult:
|
|
244
|
-
pass
|
|
245
|
-
|
|
246
|
-
def terminate(self):
|
|
247
|
-
pass
|
|
248
|
-
|
|
249
|
-
def get_int32(self, vrs: List[int]) -> List[int]:
|
|
250
|
-
refs = list()
|
|
251
|
-
for vr in vrs:
|
|
252
|
-
var = self.vars[vr]
|
|
253
|
-
if isinstance(var, Int32):
|
|
254
|
-
refs.append(int(var.getter()))
|
|
255
|
-
else:
|
|
256
|
-
raise TypeError(
|
|
257
|
-
f"Variable with valueReference={vr} is not of type Integer!"
|
|
258
|
-
)
|
|
259
|
-
return refs
|
|
260
|
-
|
|
261
|
-
def get_int64(self, vrs: List[int]) -> List[int]:
|
|
262
|
-
refs = list()
|
|
263
|
-
for vr in vrs:
|
|
264
|
-
var = self.vars[vr]
|
|
265
|
-
if isinstance(var, (Enumeration, Int64)):
|
|
266
|
-
refs.append(int(var.getter()))
|
|
267
|
-
else:
|
|
268
|
-
raise TypeError(
|
|
269
|
-
f"Variable with valueReference={vr} is not of type Int64!"
|
|
270
|
-
)
|
|
271
|
-
return refs
|
|
272
|
-
|
|
273
|
-
def get_uint64(self, vrs: List[int]) -> List[ctypes.c_uint64]:
|
|
274
|
-
refs = list()
|
|
275
|
-
for vr in vrs:
|
|
276
|
-
var = self.vars[vr]
|
|
277
|
-
if isinstance(var, UInt64):
|
|
278
|
-
val = var.getter()
|
|
279
|
-
refs.append(val if isinstance(val, ctypes.c_uint64) else ctypes.c_uint64(val))
|
|
280
|
-
else:
|
|
281
|
-
raise TypeError(
|
|
282
|
-
f"Variable with valueReference={vr} is not of type UInt64!"
|
|
283
|
-
)
|
|
284
|
-
return refs
|
|
285
|
-
|
|
286
|
-
def get_float64(self, vrs: List[int]) -> List[float]:
|
|
287
|
-
refs = list()
|
|
288
|
-
for vr in vrs:
|
|
289
|
-
var = self.vars[vr]
|
|
290
|
-
if isinstance(var, Float64):
|
|
291
|
-
if len(var.dimensions) == 0:
|
|
292
|
-
refs.append(float(var.getter()))
|
|
293
|
-
else:
|
|
294
|
-
refs.extend(var.getter())
|
|
295
|
-
else:
|
|
296
|
-
raise TypeError(
|
|
297
|
-
f"Variable with valueReference={vr} is not of type Real!"
|
|
298
|
-
)
|
|
299
|
-
return refs
|
|
300
|
-
|
|
301
|
-
def get_boolean(self, vrs: List[int]) -> List[bool]:
|
|
302
|
-
refs = list()
|
|
303
|
-
for vr in vrs:
|
|
304
|
-
var = self.vars[vr]
|
|
305
|
-
if isinstance(var, Boolean):
|
|
306
|
-
refs.append(bool(var.getter()))
|
|
307
|
-
else:
|
|
308
|
-
raise TypeError(
|
|
309
|
-
f"Variable with valueReference={vr} is not of type Boolean!"
|
|
310
|
-
)
|
|
311
|
-
return refs
|
|
312
|
-
|
|
313
|
-
def get_string(self, vrs: List[int]) -> List[str]:
|
|
314
|
-
refs = list()
|
|
315
|
-
for vr in vrs:
|
|
316
|
-
var = self.vars[vr]
|
|
317
|
-
if isinstance(var, String):
|
|
318
|
-
refs.append(str(var.getter()))
|
|
319
|
-
else:
|
|
320
|
-
raise TypeError(
|
|
321
|
-
f"Variable with valueReference={vr} is not of type String!"
|
|
322
|
-
)
|
|
323
|
-
return refs
|
|
324
|
-
|
|
325
|
-
def set_int32(self, vrs: List[int], values: List[int]):
|
|
326
|
-
for vr, value in zip(vrs, values):
|
|
327
|
-
var = self.vars[vr]
|
|
328
|
-
if isinstance(var, Int32):
|
|
329
|
-
var.setter(value)
|
|
330
|
-
else:
|
|
331
|
-
raise TypeError(
|
|
332
|
-
f"Variable with valueReference={vr} is not of type Integer!"
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
def set_int64(self, vrs: List[int], values: List[int]):
|
|
336
|
-
for vr, value in zip(vrs, values):
|
|
337
|
-
var = self.vars[vr]
|
|
338
|
-
if isinstance(var, (Enumeration, Int64)):
|
|
339
|
-
var.setter(value)
|
|
340
|
-
else:
|
|
341
|
-
raise TypeError(
|
|
342
|
-
f"Variable with valueReference={vr} is not of type Integer!"
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
def set_uint64(self, vrs: List[int], values: List[int]):
|
|
346
|
-
for vr, value in zip(vrs, values):
|
|
347
|
-
var = self.vars[vr]
|
|
348
|
-
if isinstance(var, UInt64):
|
|
349
|
-
var.setter(value)
|
|
350
|
-
else:
|
|
351
|
-
raise TypeError(
|
|
352
|
-
f"Variable with valueReference={vr} is not of type UInt64!"
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
def set_float64(self, vrs: List[int], values: List[float]):
|
|
356
|
-
offset = 0
|
|
357
|
-
for vr in vrs:
|
|
358
|
-
var = self.vars[vr]
|
|
359
|
-
if isinstance(var, Float64):
|
|
360
|
-
size = var.size(self.vars)
|
|
361
|
-
if size > 1:
|
|
362
|
-
var.setter(values[offset:offset+size])
|
|
363
|
-
else:
|
|
364
|
-
var.setter(values[offset])
|
|
365
|
-
offset += size
|
|
366
|
-
else:
|
|
367
|
-
raise TypeError(
|
|
368
|
-
f"Variable with valueReference={vr} is not of type Real!"
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
def set_boolean(self, vrs: List[int], values: List[bool]):
|
|
372
|
-
for vr, value in zip(vrs, values):
|
|
373
|
-
var = self.vars[vr]
|
|
374
|
-
if isinstance(var, Boolean):
|
|
375
|
-
var.setter(value)
|
|
376
|
-
else:
|
|
377
|
-
raise TypeError(
|
|
378
|
-
f"Variable with valueReference={vr} is not of type Boolean!"
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
def set_string(self, vrs: List[int], values: List[str]):
|
|
382
|
-
for vr, value in zip(vrs, values):
|
|
383
|
-
var = self.vars[vr]
|
|
384
|
-
if isinstance(var, String):
|
|
385
|
-
var.setter(value)
|
|
386
|
-
else:
|
|
387
|
-
raise TypeError(
|
|
388
|
-
f"Variable with valueReference={vr} is not of type String!"
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
def _get_fmu_state(self) -> Dict[str, Any]:
|
|
392
|
-
state = dict()
|
|
393
|
-
for var in self.vars.values():
|
|
394
|
-
state[var.name] = var.getter()
|
|
395
|
-
return state
|
|
396
|
-
|
|
397
|
-
def _set_fmu_state(self, state: Dict[str, Any]):
|
|
398
|
-
vars_by_name = dict([(v.name, v) for v in self.vars.values()])
|
|
399
|
-
for name, value in state.items():
|
|
400
|
-
if name not in vars_by_name:
|
|
401
|
-
setattr(self, name, value)
|
|
402
|
-
else:
|
|
403
|
-
v = vars_by_name[name]
|
|
404
|
-
if v.setter is not None:
|
|
405
|
-
v.setter(value)
|
|
406
|
-
|
|
407
|
-
@staticmethod
|
|
408
|
-
def _fmu_state_to_bytes(state: Dict[str, Any]) -> bytes:
|
|
409
|
-
return json.dumps(state).encode("utf-8")
|
|
410
|
-
|
|
411
|
-
@staticmethod
|
|
412
|
-
def _fmu_state_from_bytes(state: bytes) -> Dict[str, Any]:
|
|
413
|
-
return json.loads(state.decode("utf-8"))
|
|
414
|
-
|
|
415
|
-
def _get_log_queue(self):
|
|
416
|
-
return self.log_queue
|
|
417
|
-
|
|
418
|
-
def log(
|
|
419
|
-
self,
|
|
420
|
-
msg: str,
|
|
421
|
-
status: Fmi3Status = Fmi3Status.ok,
|
|
422
|
-
category: Optional[str] = None,
|
|
423
|
-
debug: bool = False
|
|
424
|
-
):
|
|
425
|
-
"""Log a message to the FMU logger.
|
|
426
|
-
|
|
427
|
-
Args:
|
|
428
|
-
msg (str) : Log message
|
|
429
|
-
status (Fmi3Status) : Optional, message status (default ok)
|
|
430
|
-
category (str or None) : Optional, message category (default derived from status)
|
|
431
|
-
debug (bool) : Optional, is this a debug message (default False)
|
|
432
|
-
"""
|
|
433
|
-
if category is None:
|
|
434
|
-
category = f"logStatus{status.name.capitalize()}"
|
|
435
|
-
if category not in self.log_categories:
|
|
436
|
-
category = "logAll"
|
|
437
|
-
log_msg = LogMsg(status, category, msg, debug)
|
|
438
|
-
self.log_queue.append(log_msg)
|
|
1
|
+
"""Define the abstract facade class."""
|
|
2
|
+
import json
|
|
3
|
+
import ctypes
|
|
4
|
+
import datetime
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from collections import OrderedDict, namedtuple
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, ClassVar, Dict, List, NamedTuple, Optional
|
|
9
|
+
from uuid import uuid1
|
|
10
|
+
from xml.etree.ElementTree import Element, SubElement
|
|
11
|
+
|
|
12
|
+
from .logmsg import LogMsg
|
|
13
|
+
from .default_experiment import DefaultExperiment
|
|
14
|
+
from ._version import __version__ as VERSION
|
|
15
|
+
from .enums import Fmi3Type, Fmi3Status, Fmi3Causality, Fmi3Initial, Fmi3Variability
|
|
16
|
+
from .variables import Boolean, Enumeration, Int32, Int64, UInt64, Float64, ModelVariable, String
|
|
17
|
+
from .variable_types import VariableType
|
|
18
|
+
from .unit import Unit
|
|
19
|
+
|
|
20
|
+
ModelOptions = namedtuple("ModelOptions", ["name", "value", "cli"])
|
|
21
|
+
|
|
22
|
+
FMI3_MODEL_OPTIONS: List[ModelOptions] = [
|
|
23
|
+
ModelOptions("needsExecutionTool", True, "no-external-tool"),
|
|
24
|
+
ModelOptions("canHandleVariableCommunicationStepSize", True, "no-variable-step"),
|
|
25
|
+
ModelOptions("canBeInstantiatedOnlyOncePerProcess", False, "only-one-per-process"),
|
|
26
|
+
ModelOptions("canGetAndSetFMUState", False, "handle-state"),
|
|
27
|
+
ModelOptions("canSerializeFMUState", False, "serialize-state")
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
class Fmi3StepResult(NamedTuple):
|
|
31
|
+
status: Fmi3Status = Fmi3Status.ok
|
|
32
|
+
eventHandlingNeeded: bool = False
|
|
33
|
+
terminateSimulation: bool = False
|
|
34
|
+
earlyReturn: bool = False
|
|
35
|
+
|
|
36
|
+
class Fmi3Slave(ABC):
|
|
37
|
+
"""Abstract facade class to execute Python through FMI standard."""
|
|
38
|
+
|
|
39
|
+
# Dictionary of (category, description) entries
|
|
40
|
+
log_categories: Dict[str, str] = {
|
|
41
|
+
"logStatusWarning": "Log messages with fmi3Warning status.",
|
|
42
|
+
"logStatusDiscard": "Log messages with fmi3Discard status.",
|
|
43
|
+
"logStatusError": "Log messages with fmi3Error status.",
|
|
44
|
+
"logStatusFatal": "Log messages with fmi3Fatal status.",
|
|
45
|
+
"logAll": "Log all messages."
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def __init__(self, **kwargs):
|
|
49
|
+
self.vars = OrderedDict()
|
|
50
|
+
self.instance_name = kwargs["instance_name"]
|
|
51
|
+
self.resources = kwargs.get("resources", None)
|
|
52
|
+
self.visible = kwargs.get("visible", False)
|
|
53
|
+
self.log_queue = []
|
|
54
|
+
|
|
55
|
+
self.guid = uuid1()
|
|
56
|
+
self.author: Optional[str] = None
|
|
57
|
+
self.license: Optional[str] = None
|
|
58
|
+
self.version: Optional[str] = None
|
|
59
|
+
self.copyright: Optional[str] = None
|
|
60
|
+
self.modelName: Optional[str] = self.__class__.__name__
|
|
61
|
+
self.description: Optional[str] = None
|
|
62
|
+
self.default_experiment: Optional[DefaultExperiment] = None
|
|
63
|
+
|
|
64
|
+
self.type_definitions: Dict[str, VariableType] = {}
|
|
65
|
+
self.units: Dict[str, Unit] = {}
|
|
66
|
+
|
|
67
|
+
def to_xml(self, model_options: Dict[str, str] = dict()) -> Element:
|
|
68
|
+
"""Build the XML representation of the model.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
model_options (Dict[str, str]) : FMU model options
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
(xml.etree.TreeElement.Element) XML description of the FMU
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
t = datetime.datetime.now(datetime.timezone.utc)
|
|
78
|
+
date_str = t.isoformat(timespec="seconds")
|
|
79
|
+
|
|
80
|
+
attrib = dict(
|
|
81
|
+
fmiVersion="3.0",
|
|
82
|
+
modelName=self.modelName,
|
|
83
|
+
instantiationToken=f"{self.guid!s}",
|
|
84
|
+
generationTool=f"PythonFMU3 {VERSION}",
|
|
85
|
+
generationDateAndTime=date_str,
|
|
86
|
+
variableNamingConvention="structured"
|
|
87
|
+
)
|
|
88
|
+
if self.description is not None:
|
|
89
|
+
attrib["description"] = self.description
|
|
90
|
+
if self.author is not None:
|
|
91
|
+
attrib["author"] = self.author
|
|
92
|
+
if self.license is not None:
|
|
93
|
+
attrib["license"] = self.license
|
|
94
|
+
if self.version is not None:
|
|
95
|
+
attrib["version"] = self.version
|
|
96
|
+
if self.copyright is not None:
|
|
97
|
+
attrib["copyright"] = self.copyright
|
|
98
|
+
|
|
99
|
+
root = Element("fmiModelDescription", attrib)
|
|
100
|
+
|
|
101
|
+
options = dict()
|
|
102
|
+
for option in FMI3_MODEL_OPTIONS:
|
|
103
|
+
value = model_options.get(option.name, option.value)
|
|
104
|
+
options[option.name] = str(value).lower()
|
|
105
|
+
options["modelIdentifier"] = self.modelName
|
|
106
|
+
options["canNotUseMemoryManagementFunctions"] = "true"
|
|
107
|
+
|
|
108
|
+
SubElement(root, "CoSimulation", attrib=options)
|
|
109
|
+
|
|
110
|
+
if self.units:
|
|
111
|
+
unit_defs = SubElement(root, "UnitDefinitions")
|
|
112
|
+
for _, unit in self.units.items():
|
|
113
|
+
unit_defs.append(unit.to_xml())
|
|
114
|
+
|
|
115
|
+
if self.type_definitions:
|
|
116
|
+
type_defs = SubElement(root, "TypeDefinitions")
|
|
117
|
+
for _, val in self.type_definitions.items():
|
|
118
|
+
type_defs.append(val.to_xml())
|
|
119
|
+
|
|
120
|
+
if len(self.log_categories) > 0:
|
|
121
|
+
categories = SubElement(root, "LogCategories")
|
|
122
|
+
for category, description in self.log_categories.items():
|
|
123
|
+
categories.append(
|
|
124
|
+
Element(
|
|
125
|
+
"Category",
|
|
126
|
+
attrib={"name": category, "description": description},
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
if self.default_experiment is not None:
|
|
131
|
+
attrib = dict()
|
|
132
|
+
if self.default_experiment.start_time is not None:
|
|
133
|
+
attrib["startTime"] = str(self.default_experiment.start_time)
|
|
134
|
+
if self.default_experiment.stop_time is not None:
|
|
135
|
+
attrib["stopTime"] = str(self.default_experiment.stop_time)
|
|
136
|
+
if self.default_experiment.step_size is not None:
|
|
137
|
+
attrib["stepSize"] = str(self.default_experiment.step_size)
|
|
138
|
+
if self.default_experiment.tolerance is not None:
|
|
139
|
+
attrib["tolerance"] = str(self.default_experiment.tolerance)
|
|
140
|
+
SubElement(root, "DefaultExperiment", attrib)
|
|
141
|
+
|
|
142
|
+
variables = SubElement(root, "ModelVariables")
|
|
143
|
+
for v in self.vars.values():
|
|
144
|
+
if ModelVariable.requires_start(v):
|
|
145
|
+
self.__apply_start_value(v)
|
|
146
|
+
variables.append(v.to_xml())
|
|
147
|
+
|
|
148
|
+
structure = SubElement(root, "ModelStructure")
|
|
149
|
+
outputs = list(
|
|
150
|
+
filter(lambda v: v.causality == Fmi3Causality.output, self.vars.values())
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
continuous_state_derivatives = list(
|
|
154
|
+
filter(lambda v: v.variability == Fmi3Variability.continuous and (isinstance(v, Float64) and v.derivative is not None), self.vars.values())
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
allowed_variability = [None, Fmi3Initial.approx, Fmi3Initial.calculated]
|
|
158
|
+
initial_unknown = list(
|
|
159
|
+
filter(lambda v: (v.causality == Fmi3Causality.output and (v.initial in allowed_variability))
|
|
160
|
+
or v.causality == Fmi3Causality.calculatedParameter
|
|
161
|
+
or v in continuous_state_derivatives and v.initial in allowed_variability
|
|
162
|
+
or v.variability == Fmi3Variability.continuous and v.initial in allowed_variability and v.causality != Fmi3Causality.independent, self.vars.values())
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
for v in outputs:
|
|
166
|
+
SubElement(structure, "Output", attrib=dict(valueReference=str(v.value_reference)))
|
|
167
|
+
|
|
168
|
+
for v in continuous_state_derivatives:
|
|
169
|
+
SubElement(structure, "ContinuousStateDerivative", attrib=dict(valueReference=str(v.value_reference)))
|
|
170
|
+
|
|
171
|
+
for v in initial_unknown:
|
|
172
|
+
SubElement(structure, "InitialUnknown", attrib=dict(valueReference=str(v.value_reference)))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
return root
|
|
177
|
+
|
|
178
|
+
def __apply_start_value(self, var: ModelVariable):
|
|
179
|
+
vrs = [var.value_reference]
|
|
180
|
+
if isinstance(var, Int32):
|
|
181
|
+
refs = self.get_int32(vrs)
|
|
182
|
+
elif isinstance(var, (Enumeration, Int64)):
|
|
183
|
+
refs = self.get_int64(vrs)
|
|
184
|
+
elif isinstance(var, UInt64):
|
|
185
|
+
refs = [val.value for val in self.get_uint64(vrs)]
|
|
186
|
+
elif isinstance(var, Float64):
|
|
187
|
+
refs = self.get_float64(vrs)
|
|
188
|
+
elif isinstance(var, Boolean):
|
|
189
|
+
refs = self.get_boolean(vrs)
|
|
190
|
+
elif isinstance(var, String):
|
|
191
|
+
refs = self.get_string(vrs)
|
|
192
|
+
else:
|
|
193
|
+
raise Exception(f"Unsupported type {type(var)}!")
|
|
194
|
+
var.start = refs if len(getattr(var, "dimensions", [])) > 0 else refs[0]
|
|
195
|
+
|
|
196
|
+
def register_variable(self, var: ModelVariable, nested: bool = True, var_type: Any = None):
|
|
197
|
+
"""Register a variable as FMU interface.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
var (ModelVariable): The variable to be registered
|
|
201
|
+
nested (bool): Optional, does the "." in the variable name reflect an object hierarchy to access it? Default True
|
|
202
|
+
"""
|
|
203
|
+
variable_reference = len(self.vars)
|
|
204
|
+
self.vars[variable_reference] = var
|
|
205
|
+
# Set the unique value reference
|
|
206
|
+
var.value_reference = variable_reference
|
|
207
|
+
owner = self
|
|
208
|
+
if var.getter is None and nested and "." in var.name:
|
|
209
|
+
split = var.name.split(".")
|
|
210
|
+
split.pop(-1)
|
|
211
|
+
for s in split:
|
|
212
|
+
owner = getattr(owner, s)
|
|
213
|
+
if var.getter is None:
|
|
214
|
+
if hasattr(var, "dimensions") and len(var.dimensions) > 0:
|
|
215
|
+
var.getter = lambda: getattr(owner, var.local_name).flatten().tolist()
|
|
216
|
+
else:
|
|
217
|
+
var.getter = lambda: getattr(owner, var.local_name)
|
|
218
|
+
if var.setter is None and hasattr(owner, var.local_name) and var.variability != Fmi3Variability.constant:
|
|
219
|
+
if hasattr(var, "dimensions") and len(var.dimensions) > 0:
|
|
220
|
+
import numpy as np
|
|
221
|
+
var.setter = lambda v: setattr(owner, var.local_name, np.reshape(v, newshape=getattr(owner, var.local_name).shape))
|
|
222
|
+
else:
|
|
223
|
+
var.setter = lambda v: setattr(owner, var.local_name, v)
|
|
224
|
+
|
|
225
|
+
if var_type:
|
|
226
|
+
self.type_definitions[var_type.name] = var_type
|
|
227
|
+
var.declared_type = var_type.name
|
|
228
|
+
|
|
229
|
+
def setup_experiment(self, start_time: float):
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
def register_units(self, units: List[Unit]):
|
|
233
|
+
for unit in units:
|
|
234
|
+
self.units[unit.name] = unit
|
|
235
|
+
|
|
236
|
+
def enter_initialization_mode(self):
|
|
237
|
+
pass
|
|
238
|
+
|
|
239
|
+
def exit_initialization_mode(self):
|
|
240
|
+
pass
|
|
241
|
+
|
|
242
|
+
@abstractmethod
|
|
243
|
+
def do_step(self, current_time: float, step_size: float) -> Fmi3StepResult:
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
def terminate(self):
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
def get_int32(self, vrs: List[int]) -> List[int]:
|
|
250
|
+
refs = list()
|
|
251
|
+
for vr in vrs:
|
|
252
|
+
var = self.vars[vr]
|
|
253
|
+
if isinstance(var, Int32):
|
|
254
|
+
refs.append(int(var.getter()))
|
|
255
|
+
else:
|
|
256
|
+
raise TypeError(
|
|
257
|
+
f"Variable with valueReference={vr} is not of type Integer!"
|
|
258
|
+
)
|
|
259
|
+
return refs
|
|
260
|
+
|
|
261
|
+
def get_int64(self, vrs: List[int]) -> List[int]:
|
|
262
|
+
refs = list()
|
|
263
|
+
for vr in vrs:
|
|
264
|
+
var = self.vars[vr]
|
|
265
|
+
if isinstance(var, (Enumeration, Int64)):
|
|
266
|
+
refs.append(int(var.getter()))
|
|
267
|
+
else:
|
|
268
|
+
raise TypeError(
|
|
269
|
+
f"Variable with valueReference={vr} is not of type Int64!"
|
|
270
|
+
)
|
|
271
|
+
return refs
|
|
272
|
+
|
|
273
|
+
def get_uint64(self, vrs: List[int]) -> List[ctypes.c_uint64]:
|
|
274
|
+
refs = list()
|
|
275
|
+
for vr in vrs:
|
|
276
|
+
var = self.vars[vr]
|
|
277
|
+
if isinstance(var, UInt64):
|
|
278
|
+
val = var.getter()
|
|
279
|
+
refs.append(val if isinstance(val, ctypes.c_uint64) else ctypes.c_uint64(val))
|
|
280
|
+
else:
|
|
281
|
+
raise TypeError(
|
|
282
|
+
f"Variable with valueReference={vr} is not of type UInt64!"
|
|
283
|
+
)
|
|
284
|
+
return refs
|
|
285
|
+
|
|
286
|
+
def get_float64(self, vrs: List[int]) -> List[float]:
|
|
287
|
+
refs = list()
|
|
288
|
+
for vr in vrs:
|
|
289
|
+
var = self.vars[vr]
|
|
290
|
+
if isinstance(var, Float64):
|
|
291
|
+
if len(var.dimensions) == 0:
|
|
292
|
+
refs.append(float(var.getter()))
|
|
293
|
+
else:
|
|
294
|
+
refs.extend(var.getter())
|
|
295
|
+
else:
|
|
296
|
+
raise TypeError(
|
|
297
|
+
f"Variable with valueReference={vr} is not of type Real!"
|
|
298
|
+
)
|
|
299
|
+
return refs
|
|
300
|
+
|
|
301
|
+
def get_boolean(self, vrs: List[int]) -> List[bool]:
|
|
302
|
+
refs = list()
|
|
303
|
+
for vr in vrs:
|
|
304
|
+
var = self.vars[vr]
|
|
305
|
+
if isinstance(var, Boolean):
|
|
306
|
+
refs.append(bool(var.getter()))
|
|
307
|
+
else:
|
|
308
|
+
raise TypeError(
|
|
309
|
+
f"Variable with valueReference={vr} is not of type Boolean!"
|
|
310
|
+
)
|
|
311
|
+
return refs
|
|
312
|
+
|
|
313
|
+
def get_string(self, vrs: List[int]) -> List[str]:
|
|
314
|
+
refs = list()
|
|
315
|
+
for vr in vrs:
|
|
316
|
+
var = self.vars[vr]
|
|
317
|
+
if isinstance(var, String):
|
|
318
|
+
refs.append(str(var.getter()))
|
|
319
|
+
else:
|
|
320
|
+
raise TypeError(
|
|
321
|
+
f"Variable with valueReference={vr} is not of type String!"
|
|
322
|
+
)
|
|
323
|
+
return refs
|
|
324
|
+
|
|
325
|
+
def set_int32(self, vrs: List[int], values: List[int]):
|
|
326
|
+
for vr, value in zip(vrs, values):
|
|
327
|
+
var = self.vars[vr]
|
|
328
|
+
if isinstance(var, Int32):
|
|
329
|
+
var.setter(value)
|
|
330
|
+
else:
|
|
331
|
+
raise TypeError(
|
|
332
|
+
f"Variable with valueReference={vr} is not of type Integer!"
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def set_int64(self, vrs: List[int], values: List[int]):
|
|
336
|
+
for vr, value in zip(vrs, values):
|
|
337
|
+
var = self.vars[vr]
|
|
338
|
+
if isinstance(var, (Enumeration, Int64)):
|
|
339
|
+
var.setter(value)
|
|
340
|
+
else:
|
|
341
|
+
raise TypeError(
|
|
342
|
+
f"Variable with valueReference={vr} is not of type Integer!"
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
def set_uint64(self, vrs: List[int], values: List[int]):
|
|
346
|
+
for vr, value in zip(vrs, values):
|
|
347
|
+
var = self.vars[vr]
|
|
348
|
+
if isinstance(var, UInt64):
|
|
349
|
+
var.setter(value)
|
|
350
|
+
else:
|
|
351
|
+
raise TypeError(
|
|
352
|
+
f"Variable with valueReference={vr} is not of type UInt64!"
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
def set_float64(self, vrs: List[int], values: List[float]):
|
|
356
|
+
offset = 0
|
|
357
|
+
for vr in vrs:
|
|
358
|
+
var = self.vars[vr]
|
|
359
|
+
if isinstance(var, Float64):
|
|
360
|
+
size = var.size(self.vars)
|
|
361
|
+
if size > 1:
|
|
362
|
+
var.setter(values[offset:offset+size])
|
|
363
|
+
else:
|
|
364
|
+
var.setter(values[offset])
|
|
365
|
+
offset += size
|
|
366
|
+
else:
|
|
367
|
+
raise TypeError(
|
|
368
|
+
f"Variable with valueReference={vr} is not of type Real!"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
def set_boolean(self, vrs: List[int], values: List[bool]):
|
|
372
|
+
for vr, value in zip(vrs, values):
|
|
373
|
+
var = self.vars[vr]
|
|
374
|
+
if isinstance(var, Boolean):
|
|
375
|
+
var.setter(value)
|
|
376
|
+
else:
|
|
377
|
+
raise TypeError(
|
|
378
|
+
f"Variable with valueReference={vr} is not of type Boolean!"
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
def set_string(self, vrs: List[int], values: List[str]):
|
|
382
|
+
for vr, value in zip(vrs, values):
|
|
383
|
+
var = self.vars[vr]
|
|
384
|
+
if isinstance(var, String):
|
|
385
|
+
var.setter(value)
|
|
386
|
+
else:
|
|
387
|
+
raise TypeError(
|
|
388
|
+
f"Variable with valueReference={vr} is not of type String!"
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
def _get_fmu_state(self) -> Dict[str, Any]:
|
|
392
|
+
state = dict()
|
|
393
|
+
for var in self.vars.values():
|
|
394
|
+
state[var.name] = var.getter()
|
|
395
|
+
return state
|
|
396
|
+
|
|
397
|
+
def _set_fmu_state(self, state: Dict[str, Any]):
|
|
398
|
+
vars_by_name = dict([(v.name, v) for v in self.vars.values()])
|
|
399
|
+
for name, value in state.items():
|
|
400
|
+
if name not in vars_by_name:
|
|
401
|
+
setattr(self, name, value)
|
|
402
|
+
else:
|
|
403
|
+
v = vars_by_name[name]
|
|
404
|
+
if v.setter is not None:
|
|
405
|
+
v.setter(value)
|
|
406
|
+
|
|
407
|
+
@staticmethod
|
|
408
|
+
def _fmu_state_to_bytes(state: Dict[str, Any]) -> bytes:
|
|
409
|
+
return json.dumps(state).encode("utf-8")
|
|
410
|
+
|
|
411
|
+
@staticmethod
|
|
412
|
+
def _fmu_state_from_bytes(state: bytes) -> Dict[str, Any]:
|
|
413
|
+
return json.loads(state.decode("utf-8"))
|
|
414
|
+
|
|
415
|
+
def _get_log_queue(self):
|
|
416
|
+
return self.log_queue
|
|
417
|
+
|
|
418
|
+
def log(
|
|
419
|
+
self,
|
|
420
|
+
msg: str,
|
|
421
|
+
status: Fmi3Status = Fmi3Status.ok,
|
|
422
|
+
category: Optional[str] = None,
|
|
423
|
+
debug: bool = False
|
|
424
|
+
):
|
|
425
|
+
"""Log a message to the FMU logger.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
msg (str) : Log message
|
|
429
|
+
status (Fmi3Status) : Optional, message status (default ok)
|
|
430
|
+
category (str or None) : Optional, message category (default derived from status)
|
|
431
|
+
debug (bool) : Optional, is this a debug message (default False)
|
|
432
|
+
"""
|
|
433
|
+
if category is None:
|
|
434
|
+
category = f"logStatus{status.name.capitalize()}"
|
|
435
|
+
if category not in self.log_categories:
|
|
436
|
+
category = "logAll"
|
|
437
|
+
log_msg = LogMsg(status, category, msg, debug)
|
|
438
|
+
self.log_queue.append(log_msg)
|