pythonfmu3 0.1.11__py3-none-any.whl → 0.1.16__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/_version.py +1 -1
- pythonfmu3/fmi3slave.py +2 -1
- pythonfmu3/osutil.py +2 -2
- pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp +0 -3
- pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp +4 -3
- pythonfmu3/pythonfmu-export/src/pythonfmu/IPyState.hpp +18 -0
- pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp +67 -8
- pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp +3 -1
- pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp +67 -23
- pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
- pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
- pythonfmu3/tests/slaves/PythonSlaveWithException.py +4 -1
- pythonfmu3/tests/slaves/pythonslave.py +3 -0
- pythonfmu3/tests/slaves/pythonslave_read_file.py +4 -1
- pythonfmu3/tests/slaves/slavewithdep.py +4 -1
- pythonfmu3/tests/test_integration.py +41 -47
- pythonfmu3/tests/test_multiple_fmus.py +12 -10
- pythonfmu3/tests/test_variables.py +3 -1
- pythonfmu3/variables.py +33 -5
- {pythonfmu3-0.1.11.dist-info → pythonfmu3-0.1.16.dist-info}/METADATA +9 -7
- {pythonfmu3-0.1.11.dist-info → pythonfmu3-0.1.16.dist-info}/RECORD +25 -24
- {pythonfmu3-0.1.11.dist-info → pythonfmu3-0.1.16.dist-info}/WHEEL +1 -1
- {pythonfmu3-0.1.11.dist-info → pythonfmu3-0.1.16.dist-info}/LICENSE +0 -0
- {pythonfmu3-0.1.11.dist-info → pythonfmu3-0.1.16.dist-info}/entry_points.txt +0 -0
- {pythonfmu3-0.1.11.dist-info → pythonfmu3-0.1.16.dist-info}/top_level.txt +0 -0
pythonfmu3/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.16"
|
pythonfmu3/fmi3slave.py
CHANGED
|
@@ -55,6 +55,7 @@ class Fmi3Slave(ABC):
|
|
|
55
55
|
self.modelName: Optional[str] = self.__class__.__name__
|
|
56
56
|
self.description: Optional[str] = None
|
|
57
57
|
self.default_experiment: Optional[DefaultExperiment] = None
|
|
58
|
+
|
|
58
59
|
self.type_definitions: Dict[str, VariableType] = {}
|
|
59
60
|
self.units: Dict[str, Unit] = {}
|
|
60
61
|
|
|
@@ -223,7 +224,7 @@ class Fmi3Slave(ABC):
|
|
|
223
224
|
def setup_experiment(self, start_time: float):
|
|
224
225
|
pass
|
|
225
226
|
|
|
226
|
-
def
|
|
227
|
+
def register_units(self, units: List[Unit]):
|
|
227
228
|
for unit in units:
|
|
228
229
|
self.units[unit.name] = unit
|
|
229
230
|
|
pythonfmu3/osutil.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
import platform
|
|
3
2
|
|
|
4
3
|
import platform
|
|
5
4
|
|
|
@@ -8,7 +7,8 @@ def get_fmu_arch():
|
|
|
8
7
|
system = platform.system()
|
|
9
8
|
is_x86 = platform.machine() in ["i386", "AMD64", "x86_64"]
|
|
10
9
|
platforms = {"Windows": "windows", "Linux": "linux", "Darwin": "darwin"}
|
|
11
|
-
|
|
10
|
+
arch = "x86_64" if is_x86 else "x86"
|
|
11
|
+
return arch + "-" + platforms.get(system, "unknown")
|
|
12
12
|
|
|
13
13
|
def get_platform() -> str:
|
|
14
14
|
"""Get FMU binary platform folder name."""
|
|
@@ -105,11 +105,9 @@ public:
|
|
|
105
105
|
|
|
106
106
|
Logger(
|
|
107
107
|
FMIComponentEnvironment component,
|
|
108
|
-
String instanceName,
|
|
109
108
|
fmi3LogMessageCallback logCallback,
|
|
110
109
|
std::shared_ptr<Settings> settings)
|
|
111
110
|
: m_component{component}
|
|
112
|
-
, m_instanceName(std::move(instanceName))
|
|
113
111
|
, m_fmiLogger{logCallback}
|
|
114
112
|
, m_settings{settings}
|
|
115
113
|
{
|
|
@@ -154,7 +152,6 @@ public:
|
|
|
154
152
|
|
|
155
153
|
private:
|
|
156
154
|
const FMIComponentEnvironment m_component;
|
|
157
|
-
const String m_instanceName;
|
|
158
155
|
const FMICallbackLogger m_fmiLogger;
|
|
159
156
|
std::shared_ptr<Settings> m_settings;
|
|
160
157
|
};
|
|
@@ -47,10 +47,10 @@ struct Component
|
|
|
47
47
|
modelError = 1 << 12,
|
|
48
48
|
modelFatal = 1 << 13,
|
|
49
49
|
};
|
|
50
|
-
Component(cppfmu::
|
|
50
|
+
Component(cppfmu::FMIComponentEnvironment instanceEnvironment,
|
|
51
51
|
cppfmu::FMICallbackLogger logCallback,
|
|
52
52
|
cppfmu::FMIBoolean loggingOn) : loggerSettings{std::make_shared<cppfmu::Logger::Settings>()},
|
|
53
|
-
logger{
|
|
53
|
+
logger{instanceEnvironment, logCallback, loggerSettings}, lastSuccessfulTime{std::numeric_limits<cppfmu::FMIFloat64>::quiet_NaN()}
|
|
54
54
|
{
|
|
55
55
|
loggerSettings->debugLoggingEnabled = (loggingOn == cppfmu::FMITrue);
|
|
56
56
|
}
|
|
@@ -96,7 +96,8 @@ fmi3Instance fmi3InstantiateCoSimulation(
|
|
|
96
96
|
fmi3IntermediateUpdateCallback intermediateUpdate )
|
|
97
97
|
{
|
|
98
98
|
try {
|
|
99
|
-
auto component = new Component(
|
|
99
|
+
auto component = new Component(
|
|
100
|
+
environment,
|
|
100
101
|
logMessage,
|
|
101
102
|
loggingOn);
|
|
102
103
|
component->slave = CppfmuInstantiateSlave(
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#ifndef PYTHONFMU_IPYTHONSTATE_HPP
|
|
2
|
+
#define PYTHONFMU_IPYTHONSTATE_HPP
|
|
3
|
+
|
|
4
|
+
namespace pythonfmu
|
|
5
|
+
{
|
|
6
|
+
class IPyState
|
|
7
|
+
{
|
|
8
|
+
public:
|
|
9
|
+
IPyState() = default;
|
|
10
|
+
IPyState(IPyState const& other) = delete;
|
|
11
|
+
IPyState(IPyState&& other) = delete;
|
|
12
|
+
IPyState& operator=(IPyState const& other) = delete;
|
|
13
|
+
IPyState& operator=(IPyState&& other) = delete;
|
|
14
|
+
virtual ~IPyState() = default;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#endif //PYTHONFMU_IPYTHONSTATE_HPP
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
|
|
2
|
+
#include "pythonfmu/IPyState.hpp"
|
|
2
3
|
#include "pythonfmu/PySlaveInstance.hpp"
|
|
3
4
|
|
|
4
5
|
#include "pythonfmu/PyState.hpp"
|
|
@@ -7,6 +8,7 @@
|
|
|
7
8
|
|
|
8
9
|
#include <fstream>
|
|
9
10
|
#include <functional>
|
|
11
|
+
#include <mutex>
|
|
10
12
|
#include <regex>
|
|
11
13
|
#include <sstream>
|
|
12
14
|
#include <utility>
|
|
@@ -44,8 +46,9 @@ inline void py_safe_run(const std::function<void(PyGILState_STATE gilState)>& f)
|
|
|
44
46
|
PyGILState_Release(gil_state);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
PySlaveInstance::PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, const bool visible)
|
|
48
|
-
:
|
|
49
|
+
PySlaveInstance::PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, const bool visible, std::shared_ptr<IPyState> pyState)
|
|
50
|
+
: pyState_{ std::move(pyState) }
|
|
51
|
+
, instanceName_(std::move(instanceName))
|
|
49
52
|
, resources_(std::move(resources))
|
|
50
53
|
, logger_(logger)
|
|
51
54
|
, visible_(visible)
|
|
@@ -441,9 +444,14 @@ void PySlaveInstance::GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
441
444
|
}
|
|
442
445
|
|
|
443
446
|
for (int i = 0; i < nvr; i++) {
|
|
444
|
-
PyObject*
|
|
445
|
-
|
|
447
|
+
PyObject* item = PyList_GetItem(refs, i);
|
|
448
|
+
PyObject* value = PyObject_GetAttrString(item, "value");
|
|
449
|
+
if (value && PyLong_Check(value))
|
|
450
|
+
{
|
|
451
|
+
values[i] = static_cast<cppfmu::FMIUInt64>(PyLong_AsUnsignedLongLong(value));
|
|
452
|
+
}
|
|
446
453
|
}
|
|
454
|
+
|
|
447
455
|
Py_DECREF(refs);
|
|
448
456
|
clearLogBuffer();
|
|
449
457
|
});
|
|
@@ -625,7 +633,10 @@ PySlaveInstance::~PySlaveInstance()
|
|
|
625
633
|
|
|
626
634
|
} // namespace pythonfmu
|
|
627
635
|
|
|
628
|
-
|
|
636
|
+
namespace {
|
|
637
|
+
std::mutex pyStateMutex{};
|
|
638
|
+
std::shared_ptr<pythonfmu::PyState> pyState{};
|
|
639
|
+
}
|
|
629
640
|
|
|
630
641
|
std::unique_ptr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
|
|
631
642
|
cppfmu::FMIString instanceName,
|
|
@@ -650,9 +661,57 @@ std::unique_ptr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
|
|
|
650
661
|
#endif
|
|
651
662
|
}
|
|
652
663
|
|
|
653
|
-
|
|
654
|
-
|
|
664
|
+
{
|
|
665
|
+
auto const ensurePyStateAlive = [&]() {
|
|
666
|
+
auto const lock = std::lock_guard{pyStateMutex};
|
|
667
|
+
if (nullptr == pyState) pyState = std::make_shared<pythonfmu::PyState>();
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
ensurePyStateAlive();
|
|
671
|
+
return std::make_unique<pythonfmu::PySlaveInstance>(
|
|
672
|
+
instanceName, resources, logger, visible, pyState);
|
|
655
673
|
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
extern "C" {
|
|
656
677
|
|
|
657
|
-
|
|
678
|
+
// The PyState instance owns it's own thread for constructing and destroying the Py* from the same thread.
|
|
679
|
+
// Creation of an std::thread increments ref counter of a shared library. So, when the client unloads the library
|
|
680
|
+
// the library won't be freed, as std::thread is alive, and the std::thread itself waits for de-initialization request.
|
|
681
|
+
// Thus, use DllMain on Windows and __attribute__((destructor)) on Linux for signaling to the PyState about de-initialization.
|
|
682
|
+
void finalizePythonInterpreter()
|
|
683
|
+
{
|
|
684
|
+
pyState = nullptr;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
namespace
|
|
689
|
+
{
|
|
690
|
+
#ifdef _WIN32
|
|
691
|
+
#include <windows.h>
|
|
692
|
+
|
|
693
|
+
BOOL APIENTRY DllMain(HMODULE hModule,
|
|
694
|
+
DWORD ul_reason_for_call,
|
|
695
|
+
LPVOID lpReserved)
|
|
696
|
+
{
|
|
697
|
+
switch (ul_reason_for_call) {
|
|
698
|
+
case DLL_PROCESS_ATTACH:
|
|
699
|
+
break;
|
|
700
|
+
case DLL_THREAD_ATTACH:
|
|
701
|
+
case DLL_THREAD_DETACH:
|
|
702
|
+
break;
|
|
703
|
+
case DLL_PROCESS_DETACH:
|
|
704
|
+
finalizePythonInterpreter();
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
return TRUE;
|
|
708
|
+
}
|
|
709
|
+
#elif defined(__linux__)
|
|
710
|
+
__attribute__((destructor)) void onLibraryUnload()
|
|
711
|
+
{
|
|
712
|
+
finalizePythonInterpreter();
|
|
713
|
+
}
|
|
714
|
+
#else
|
|
715
|
+
#error port the code
|
|
716
|
+
#endif
|
|
658
717
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#define PYTHONFMU_SLAVEINSTANCE_HPP
|
|
4
4
|
|
|
5
5
|
#include "cppfmu/cppfmu_cs.hpp"
|
|
6
|
+
#include "pythonfmu/IPyState.hpp"
|
|
6
7
|
|
|
7
8
|
#include <Python.h>
|
|
8
9
|
#include <string>
|
|
@@ -15,7 +16,7 @@ class PySlaveInstance : public cppfmu::SlaveInstance
|
|
|
15
16
|
{
|
|
16
17
|
|
|
17
18
|
public:
|
|
18
|
-
PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, bool visible);
|
|
19
|
+
PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, bool visible, std::shared_ptr<IPyState> pyState);
|
|
19
20
|
|
|
20
21
|
void initialize(PyGILState_STATE gilState);
|
|
21
22
|
|
|
@@ -53,6 +54,7 @@ public:
|
|
|
53
54
|
~PySlaveInstance() override;
|
|
54
55
|
|
|
55
56
|
private:
|
|
57
|
+
std::shared_ptr<IPyState> pyState_;
|
|
56
58
|
PyObject* pClass_;
|
|
57
59
|
PyObject* pInstance_{};
|
|
58
60
|
PyObject* pMessages_{};
|
|
@@ -2,40 +2,84 @@
|
|
|
2
2
|
#ifndef PYTHONFMU_PYTHONSTATE_HPP
|
|
3
3
|
#define PYTHONFMU_PYTHONSTATE_HPP
|
|
4
4
|
|
|
5
|
+
#include "IPyState.hpp"
|
|
5
6
|
#include <Python.h>
|
|
7
|
+
#include <condition_variable>
|
|
6
8
|
#include <iostream>
|
|
9
|
+
#include <mutex>
|
|
10
|
+
#include <thread>
|
|
7
11
|
|
|
8
12
|
namespace pythonfmu
|
|
9
13
|
{
|
|
10
|
-
|
|
11
|
-
class PyState
|
|
12
|
-
{
|
|
13
|
-
public:
|
|
14
|
-
PyState()
|
|
14
|
+
class PyState : public IPyState
|
|
15
15
|
{
|
|
16
|
-
|
|
16
|
+
public:
|
|
17
|
+
PyState()
|
|
18
|
+
: _initDeinitPyThread_{ &PyState::Worker, this }
|
|
19
|
+
{
|
|
20
|
+
auto lock = std::unique_lock{ mutex_ };
|
|
21
|
+
conditionalVariable_.wait(lock, [&] { return consructed_; });
|
|
22
|
+
}
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
~PyState() override
|
|
25
|
+
{
|
|
26
|
+
{
|
|
27
|
+
auto const lock = std::lock_guard{ mutex_ };
|
|
28
|
+
destroyRequested_ = true;
|
|
29
|
+
}
|
|
30
|
+
conditionalVariable_.notify_one();
|
|
31
|
+
if (_initDeinitPyThread_.joinable()) _initDeinitPyThread_.join();
|
|
23
32
|
}
|
|
24
|
-
}
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
private:
|
|
35
|
+
|
|
36
|
+
// In accordance to the documentation https://docs.python.org/3/c-api/init.html#c.Py_FinalizeEx
|
|
37
|
+
// the Py_Initialize/Py_Finalize should be called from the same
|
|
38
|
+
// thread. The FMI standard allows to call fmi functions from different threads, also different threads
|
|
39
|
+
// could be used for loading unloading the FMU libraries. There is a deadlock, when the simulation tool
|
|
40
|
+
// unloads the FMU library from the thread which is differs from the thread where the Py_Initialize was called.
|
|
41
|
+
// Create a new thread which is used for calling Py_Initialize and Py_Deinitialize. The thread waits for
|
|
42
|
+
// notification from destructor and then calls Py_Deinitialize.
|
|
43
|
+
|
|
44
|
+
void Worker()
|
|
45
|
+
{
|
|
46
|
+
// It will be nullptr in case when some other tool already called Py_IsInitialized.
|
|
47
|
+
// There is no need to call Py_Finalize thus, this thread can exit ASAP.
|
|
48
|
+
auto const mainPyThread = []() -> PyThreadState* {
|
|
49
|
+
auto const justInitialized = !Py_IsInitialized();
|
|
50
|
+
if (justInitialized) {
|
|
51
|
+
Py_SetProgramName(L"./PythonFMU");
|
|
52
|
+
Py_Initialize();
|
|
53
|
+
#if PY_VERSION_HEX < 0x03070000
|
|
54
|
+
PyEval_InitThreads();
|
|
55
|
+
#endif
|
|
56
|
+
return PyEval_SaveThread();
|
|
57
|
+
}
|
|
58
|
+
return nullptr;
|
|
59
|
+
}();
|
|
60
|
+
|
|
61
|
+
{
|
|
62
|
+
auto const lock = std::lock_guard{ mutex_ };
|
|
63
|
+
consructed_ = true;
|
|
64
|
+
}
|
|
65
|
+
conditionalVariable_.notify_one();
|
|
66
|
+
|
|
67
|
+
if (nullptr != mainPyThread) {
|
|
68
|
+
auto lock = std::unique_lock{ mutex_ };
|
|
69
|
+
conditionalVariable_.wait(lock, [&] { return destroyRequested_; });
|
|
70
|
+
|
|
71
|
+
PyEval_RestoreThread(mainPyThread);
|
|
72
|
+
Py_Finalize();
|
|
73
|
+
}
|
|
31
74
|
}
|
|
32
|
-
}
|
|
33
75
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
76
|
+
bool consructed_ = false;
|
|
77
|
+
bool destroyRequested_ = false;
|
|
78
|
+
std::condition_variable conditionalVariable_;
|
|
79
|
+
std::mutex mutex_;
|
|
80
|
+
std::thread _initDeinitPyThread_;
|
|
81
|
+
};
|
|
38
82
|
|
|
39
83
|
} // namespace pythonfmu
|
|
40
84
|
|
|
41
|
-
#endif //PYTHONFMU_PYTHONSTATE_HPP
|
|
85
|
+
#endif //PYTHONFMU_PYTHONSTATE_HPP
|
|
Binary file
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Float64
|
|
1
|
+
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Float64, Fmi3Variability
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class PythonSlaveWithException(Fmi3Slave):
|
|
@@ -8,6 +8,9 @@ class PythonSlaveWithException(Fmi3Slave):
|
|
|
8
8
|
|
|
9
9
|
self.realIn = 22.0
|
|
10
10
|
self.realOut = 0.0
|
|
11
|
+
self.time = 0
|
|
12
|
+
|
|
13
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
11
14
|
self.register_variable(Float64("realIn", causality=Fmi3Causality.input))
|
|
12
15
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
13
16
|
|
|
@@ -21,6 +21,9 @@ class PythonSlave(Fmi3Slave):
|
|
|
21
21
|
self.realIn = 2. / 3.
|
|
22
22
|
self.booleanParameter = False
|
|
23
23
|
self.stringParameter = "dog"
|
|
24
|
+
self.time = 0
|
|
25
|
+
|
|
26
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
24
27
|
self.register_variable(
|
|
25
28
|
Int32("intParam", causality=Fmi3Causality.parameter, variability=Fmi3Variability.tunable))
|
|
26
29
|
self.register_variable(Float64("realIn", causality=Fmi3Causality.input))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Fmi3Variability, String, DefaultExperiment
|
|
1
|
+
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Fmi3Variability, String, DefaultExperiment, Float64
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class PythonSlaveReadFile(Fmi3Slave):
|
|
@@ -11,6 +11,9 @@ class PythonSlaveReadFile(Fmi3Slave):
|
|
|
11
11
|
with (open(f'{self.resources}/hello.txt', 'r')) as file:
|
|
12
12
|
data = file.read()
|
|
13
13
|
|
|
14
|
+
self.time = 0
|
|
15
|
+
|
|
16
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
14
17
|
self.register_variable(
|
|
15
18
|
String("file_content", getter=lambda: data,
|
|
16
19
|
causality=Fmi3Causality.output,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import math
|
|
2
|
-
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Float64
|
|
2
|
+
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Float64, Fmi3Variability
|
|
3
3
|
from localmodule import get_amplitude, get_time_constant
|
|
4
4
|
|
|
5
5
|
|
|
@@ -10,6 +10,9 @@ class PythonSlaveWithDep(Fmi3Slave):
|
|
|
10
10
|
|
|
11
11
|
self.realIn = 22.0
|
|
12
12
|
self.realOut = 0.0
|
|
13
|
+
self.time = 0
|
|
14
|
+
|
|
15
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
13
16
|
self.register_variable(Float64("realIn", causality=Fmi3Causality.input))
|
|
14
17
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
15
18
|
|
|
@@ -38,13 +38,12 @@ def test_integration_reset(tmp_path):
|
|
|
38
38
|
|
|
39
39
|
md = fmpy.read_model_description(str(fmu))
|
|
40
40
|
unzipdir = fmpy.extract(str(fmu))
|
|
41
|
-
model = fmpy.fmi3.
|
|
41
|
+
model = fmpy.fmi3.FMU3Slave(guid=md.guid,
|
|
42
42
|
unzipDirectory=unzipdir,
|
|
43
43
|
modelIdentifier=md.coSimulation.modelIdentifier,
|
|
44
44
|
instanceName="instance"
|
|
45
45
|
)
|
|
46
46
|
model.instantiate()
|
|
47
|
-
model.setupExperiment()
|
|
48
47
|
model.enterInitializationMode()
|
|
49
48
|
model.exitInitializationMode()
|
|
50
49
|
|
|
@@ -52,13 +51,13 @@ def test_integration_reset(tmp_path):
|
|
|
52
51
|
vr = vars["realOut"].valueReference
|
|
53
52
|
dt = 0.1
|
|
54
53
|
|
|
55
|
-
initial_value = model.
|
|
54
|
+
initial_value = model.getFloat64([vr])[0]
|
|
56
55
|
assert initial_value == pytest.approx(3.0, rel=1e-7)
|
|
57
56
|
model.doStep(0.0, dt, True)
|
|
58
|
-
read = model.
|
|
57
|
+
read = model.getFloat64([vr])[0]
|
|
59
58
|
assert read == pytest.approx(dt, rel=1e-7)
|
|
60
59
|
model.reset()
|
|
61
|
-
read = model.
|
|
60
|
+
read = model.getFloat64([vr])[0]
|
|
62
61
|
assert read == pytest.approx(initial_value, rel=1e-7)
|
|
63
62
|
|
|
64
63
|
model.terminate()
|
|
@@ -77,13 +76,12 @@ def test_integration_get_state(tmp_path):
|
|
|
77
76
|
|
|
78
77
|
md = fmpy.read_model_description(str(fmu))
|
|
79
78
|
unzipdir = fmpy.extract(str(fmu))
|
|
80
|
-
model = fmpy.fmi3.
|
|
79
|
+
model = fmpy.fmi3.FMU3Slave(guid=md.guid,
|
|
81
80
|
unzipDirectory=unzipdir,
|
|
82
81
|
modelIdentifier=md.coSimulation.modelIdentifier,
|
|
83
82
|
instanceName="instance"
|
|
84
83
|
)
|
|
85
84
|
model.instantiate()
|
|
86
|
-
model.setupExperiment()
|
|
87
85
|
model.enterInitializationMode()
|
|
88
86
|
model.exitInitializationMode()
|
|
89
87
|
|
|
@@ -98,15 +96,15 @@ def test_integration_get_state(tmp_path):
|
|
|
98
96
|
t += dt
|
|
99
97
|
|
|
100
98
|
step_model()
|
|
101
|
-
state = model.
|
|
102
|
-
assert model.
|
|
99
|
+
state = model.getFMUState()
|
|
100
|
+
assert model.getFloat64([vr])[0] == pytest.approx(dt, rel=1e-7)
|
|
103
101
|
step_model()
|
|
104
|
-
assert model.
|
|
105
|
-
model.
|
|
106
|
-
assert model.
|
|
102
|
+
assert model.getFloat64([vr])[0] == pytest.approx(dt * 2, rel=1e-7)
|
|
103
|
+
model.setFMUState(state)
|
|
104
|
+
assert model.getFloat64([vr])[0] == pytest.approx(dt, rel=1e-7)
|
|
107
105
|
step_model()
|
|
108
|
-
assert model.
|
|
109
|
-
model.
|
|
106
|
+
assert model.getFloat64([vr])[0] == pytest.approx(dt * 3, rel=1e-7)
|
|
107
|
+
model.freeFMUState(state)
|
|
110
108
|
|
|
111
109
|
model.terminate()
|
|
112
110
|
model.freeInstance()
|
|
@@ -126,14 +124,13 @@ def test_integration_get_serialize_state(tmp_path):
|
|
|
126
124
|
md = fmpy.read_model_description(fmu)
|
|
127
125
|
unzip_dir = fmpy.extract(fmu)
|
|
128
126
|
|
|
129
|
-
model = fmpy.fmi3.
|
|
127
|
+
model = fmpy.fmi3.FMU3Slave(
|
|
130
128
|
guid=md.guid,
|
|
131
129
|
unzipDirectory=unzip_dir,
|
|
132
130
|
modelIdentifier=md.coSimulation.modelIdentifier,
|
|
133
131
|
instanceName='instance1')
|
|
134
132
|
|
|
135
133
|
model.instantiate()
|
|
136
|
-
model.setupExperiment()
|
|
137
134
|
model.enterInitializationMode()
|
|
138
135
|
model.exitInitializationMode()
|
|
139
136
|
|
|
@@ -148,22 +145,22 @@ def test_integration_get_serialize_state(tmp_path):
|
|
|
148
145
|
t += dt
|
|
149
146
|
|
|
150
147
|
step_model()
|
|
151
|
-
state = model.
|
|
152
|
-
assert model.
|
|
148
|
+
state = model.getFMUState()
|
|
149
|
+
assert model.getFloat64(vrs)[0] == pytest.approx(dt, rel=1e-7)
|
|
153
150
|
step_model()
|
|
154
|
-
assert model.
|
|
155
|
-
model.
|
|
156
|
-
assert model.
|
|
151
|
+
assert model.getFloat64(vrs)[0] == pytest.approx(dt * 2, rel=1e-7)
|
|
152
|
+
model.setFMUState(state)
|
|
153
|
+
assert model.getFloat64(vrs)[0] == pytest.approx(dt, rel=1e-7)
|
|
157
154
|
step_model()
|
|
158
|
-
assert model.
|
|
155
|
+
assert model.getFloat64(vrs)[0] == pytest.approx(dt * 3, rel=1e-7)
|
|
159
156
|
|
|
160
|
-
serialize_fmu_state = model.
|
|
161
|
-
model.
|
|
162
|
-
de_serialize_fmu_state = model.
|
|
163
|
-
model.
|
|
164
|
-
assert model.
|
|
157
|
+
serialize_fmu_state = model.serializeFMUState(state)
|
|
158
|
+
model.freeFMUState(state)
|
|
159
|
+
de_serialize_fmu_state = model.deserializeFMUState(serialize_fmu_state)
|
|
160
|
+
model.setFMUState(de_serialize_fmu_state)
|
|
161
|
+
assert model.getFloat64(vrs)[0] == pytest.approx(dt, rel=1e-7)
|
|
165
162
|
|
|
166
|
-
model.
|
|
163
|
+
model.freeFMUState(de_serialize_fmu_state)
|
|
167
164
|
|
|
168
165
|
model.terminate()
|
|
169
166
|
model.freeInstance()
|
|
@@ -178,14 +175,13 @@ def test_integration_get(tmp_path):
|
|
|
178
175
|
md = fmpy.read_model_description(fmu)
|
|
179
176
|
unzip_dir = fmpy.extract(fmu)
|
|
180
177
|
|
|
181
|
-
model = fmpy.fmi3.
|
|
178
|
+
model = fmpy.fmi3.FMU3Slave(
|
|
182
179
|
guid=md.guid,
|
|
183
180
|
unzipDirectory=unzip_dir,
|
|
184
181
|
modelIdentifier=md.coSimulation.modelIdentifier,
|
|
185
182
|
instanceName='instance1')
|
|
186
183
|
|
|
187
184
|
model.instantiate()
|
|
188
|
-
model.setupExperiment()
|
|
189
185
|
model.enterInitializationMode()
|
|
190
186
|
model.exitInitializationMode()
|
|
191
187
|
|
|
@@ -207,14 +203,14 @@ def test_integration_get(tmp_path):
|
|
|
207
203
|
for key, value in to_test.items():
|
|
208
204
|
var = variables[key]
|
|
209
205
|
vrs = [var.valueReference]
|
|
210
|
-
if var.type == "
|
|
211
|
-
model_value = model.
|
|
212
|
-
elif var.type == "
|
|
213
|
-
model_value = model.
|
|
206
|
+
if var.type == "Int32":
|
|
207
|
+
model_value = model.getInt32(vrs)[0]
|
|
208
|
+
elif var.type == "Float64":
|
|
209
|
+
model_value = model.getFloat64(vrs)[0]
|
|
214
210
|
elif var.type == "Boolean":
|
|
215
211
|
model_value = model.getBoolean(vrs)[0]
|
|
216
212
|
elif var.type == "String":
|
|
217
|
-
model_value = model.getString(vrs)[0]
|
|
213
|
+
model_value = model.getString(vrs)[0]
|
|
218
214
|
else:
|
|
219
215
|
pytest.xfail("Unsupported type")
|
|
220
216
|
|
|
@@ -234,20 +230,19 @@ def test_integration_read_from_file(tmp_path):
|
|
|
234
230
|
md = fmpy.read_model_description(fmu)
|
|
235
231
|
unzip_dir = fmpy.extract(fmu)
|
|
236
232
|
|
|
237
|
-
model = fmpy.fmi3.
|
|
233
|
+
model = fmpy.fmi3.FMU3Slave(
|
|
238
234
|
guid=md.guid,
|
|
239
235
|
unzipDirectory=unzip_dir,
|
|
240
236
|
modelIdentifier=md.coSimulation.modelIdentifier,
|
|
241
237
|
instanceName='instance1')
|
|
242
238
|
|
|
243
239
|
model.instantiate()
|
|
244
|
-
model.setupExperiment()
|
|
245
240
|
model.enterInitializationMode()
|
|
246
241
|
model.exitInitializationMode()
|
|
247
242
|
|
|
248
243
|
variables = mapped(md)
|
|
249
244
|
var = variables["file_content"]
|
|
250
|
-
model_value = model.getString([var.valueReference])[0]
|
|
245
|
+
model_value = model.getString([var.valueReference])[0]
|
|
251
246
|
|
|
252
247
|
with (open(project_file, 'r')) as file:
|
|
253
248
|
data = file.read()
|
|
@@ -267,14 +262,13 @@ def test_integration_set(tmp_path):
|
|
|
267
262
|
md = fmpy.read_model_description(fmu)
|
|
268
263
|
unzip_dir = fmpy.extract(fmu)
|
|
269
264
|
|
|
270
|
-
model = fmpy.fmi3.
|
|
265
|
+
model = fmpy.fmi3.FMU3Slave(
|
|
271
266
|
guid=md.guid,
|
|
272
267
|
unzipDirectory=unzip_dir,
|
|
273
268
|
modelIdentifier=md.coSimulation.modelIdentifier,
|
|
274
269
|
instanceName='instance1')
|
|
275
270
|
|
|
276
271
|
model.instantiate()
|
|
277
|
-
model.setupExperiment()
|
|
278
272
|
model.enterInitializationMode()
|
|
279
273
|
model.exitInitializationMode()
|
|
280
274
|
|
|
@@ -292,18 +286,18 @@ def test_integration_set(tmp_path):
|
|
|
292
286
|
for key, value in to_test.items():
|
|
293
287
|
var = variables[key]
|
|
294
288
|
vrs = [var.valueReference]
|
|
295
|
-
if var.type == "
|
|
296
|
-
model.
|
|
297
|
-
model_value = model.
|
|
298
|
-
elif var.type == "
|
|
299
|
-
model.
|
|
300
|
-
model_value = model.
|
|
289
|
+
if var.type == "Int32":
|
|
290
|
+
model.setInt32(vrs, [value])
|
|
291
|
+
model_value = model.getInt32(vrs)[0]
|
|
292
|
+
elif var.type == "Float64":
|
|
293
|
+
model.setFloat64(vrs, [value])
|
|
294
|
+
model_value = model.getFloat64(vrs)[0]
|
|
301
295
|
elif var.type == "Boolean":
|
|
302
296
|
model.setBoolean(vrs, [value])
|
|
303
297
|
model_value = model.getBoolean(vrs)[0]
|
|
304
298
|
elif var.type == "String":
|
|
305
299
|
model.setString(vrs, [value])
|
|
306
|
-
model_value = model.getString(vrs)[0]
|
|
300
|
+
model_value = model.getString(vrs)[0]
|
|
307
301
|
else:
|
|
308
302
|
pytest.xfail("Unsupported type")
|
|
309
303
|
|
|
@@ -22,7 +22,7 @@ def mapped(md):
|
|
|
22
22
|
@pytest.mark.integration
|
|
23
23
|
def test_integration_multiple_fmus(tmp_path):
|
|
24
24
|
slave1_code = """import math
|
|
25
|
-
from pythonfmu3
|
|
25
|
+
from pythonfmu3 import Fmi3Slave, Fmi3Causality, Fmi3Variability, Int32, Float64, Boolean, String
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class Slave1(Fmi3Slave):
|
|
@@ -32,6 +32,8 @@ class Slave1(Fmi3Slave):
|
|
|
32
32
|
|
|
33
33
|
self.realIn = 22.0
|
|
34
34
|
self.realOut = 0.0
|
|
35
|
+
self.time = 0
|
|
36
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
35
37
|
self.register_variable(Float64("realIn", causality=Fmi3Causality.input))
|
|
36
38
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
37
39
|
|
|
@@ -41,7 +43,7 @@ class Slave1(Fmi3Slave):
|
|
|
41
43
|
return True
|
|
42
44
|
"""
|
|
43
45
|
|
|
44
|
-
slave2_code = """from pythonfmu3
|
|
46
|
+
slave2_code = """from pythonfmu3 import Fmi3Slave, Fmi3Causality, Fmi3Variability, Int32, Float64, Boolean, String
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
class Slave2(Fmi3Slave):
|
|
@@ -51,6 +53,8 @@ class Slave2(Fmi3Slave):
|
|
|
51
53
|
|
|
52
54
|
self.realIn = 22.0
|
|
53
55
|
self.realOut = 0.0
|
|
56
|
+
self.time = 0
|
|
57
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
54
58
|
self.register_variable(Float64("realIn", causality=Fmi3Causality.input))
|
|
55
59
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
56
60
|
|
|
@@ -84,21 +88,20 @@ class Slave2(Fmi3Slave):
|
|
|
84
88
|
md1 = fmpy.read_model_description(fmu1)
|
|
85
89
|
unzip_dir = fmpy.extract(fmu1)
|
|
86
90
|
|
|
87
|
-
model1 = fmpy.fmi3.
|
|
91
|
+
model1 = fmpy.fmi3.FMU3Slave(
|
|
88
92
|
guid=md1.guid,
|
|
89
93
|
unzipDirectory=unzip_dir,
|
|
90
94
|
modelIdentifier=md1.coSimulation.modelIdentifier,
|
|
91
95
|
instanceName='instance1')
|
|
92
96
|
|
|
93
97
|
model1.instantiate()
|
|
94
|
-
model1.setupExperiment()
|
|
95
98
|
model1.enterInitializationMode()
|
|
96
99
|
model1.exitInitializationMode()
|
|
97
100
|
|
|
98
101
|
md2 = fmpy.read_model_description(fmu2)
|
|
99
102
|
unzip_dir = fmpy.extract(fmu2)
|
|
100
103
|
|
|
101
|
-
model2 = fmpy.fmi3.
|
|
104
|
+
model2 = fmpy.fmi3.FMU3Slave(
|
|
102
105
|
guid=md2.guid,
|
|
103
106
|
unzipDirectory=unzip_dir,
|
|
104
107
|
modelIdentifier=md2.coSimulation.modelIdentifier,
|
|
@@ -111,11 +114,10 @@ class Slave2(Fmi3Slave):
|
|
|
111
114
|
realIn = variables2["realIn"]
|
|
112
115
|
|
|
113
116
|
model2.instantiate()
|
|
114
|
-
model2.setupExperiment()
|
|
115
117
|
model2.enterInitializationMode()
|
|
116
118
|
|
|
117
|
-
value = model1.
|
|
118
|
-
model2.
|
|
119
|
+
value = model1.getFloat64([realOut.valueReference])[0]
|
|
120
|
+
model2.setFloat64([realIn.valueReference], [value])
|
|
119
121
|
|
|
120
122
|
model2.exitInitializationMode()
|
|
121
123
|
|
|
@@ -125,12 +127,12 @@ class Slave2(Fmi3Slave):
|
|
|
125
127
|
|
|
126
128
|
while time < stop_time:
|
|
127
129
|
|
|
128
|
-
model2.
|
|
130
|
+
model2.setFloat64([realIn.valueReference], [value])
|
|
129
131
|
|
|
130
132
|
model1.doStep(time, step_size)
|
|
131
133
|
model2.doStep(time, step_size)
|
|
132
134
|
|
|
133
|
-
value = model1.
|
|
135
|
+
value = model1.getFloat64([realOut.valueReference])[0]
|
|
134
136
|
|
|
135
137
|
time += step_size
|
|
136
138
|
|
|
@@ -295,7 +295,9 @@ def test_String_to_xml(name, start):
|
|
|
295
295
|
r = String(name, start)
|
|
296
296
|
xml = r.to_xml()
|
|
297
297
|
if start is not None:
|
|
298
|
-
|
|
298
|
+
start_elements = xml.findall('.//Start')
|
|
299
|
+
assert len(start_elements) == 1
|
|
300
|
+
assert start_elements[0].attrib['value'] == str(start)
|
|
299
301
|
|
|
300
302
|
|
|
301
303
|
@pytest.mark.requirements("numpy")
|
pythonfmu3/variables.py
CHANGED
|
@@ -132,6 +132,16 @@ class ModelVariable(ABC):
|
|
|
132
132
|
f"causality={self.causality}, " \
|
|
133
133
|
f"variability={self.variability})"
|
|
134
134
|
|
|
135
|
+
class Start(object):
|
|
136
|
+
def __init__(self, startValue):
|
|
137
|
+
self.value = startValue
|
|
138
|
+
|
|
139
|
+
def to_xml(self) -> Element:
|
|
140
|
+
attrib = dict()
|
|
141
|
+
attrib["value"] = self.value
|
|
142
|
+
return Element("Start", attrib)
|
|
143
|
+
|
|
144
|
+
|
|
135
145
|
class Dimension(object):
|
|
136
146
|
def __init__(self, start: str = "", valueReference: str = ""):
|
|
137
147
|
if start and valueReference and any((start, valueReference)):
|
|
@@ -144,7 +154,7 @@ class Dimension(object):
|
|
|
144
154
|
if self.start:
|
|
145
155
|
return self.start
|
|
146
156
|
else:
|
|
147
|
-
result = next((
|
|
157
|
+
result = next((value for vr, value in vars.items() if vr == int(self.value_reference)), None)
|
|
148
158
|
return result.getter()
|
|
149
159
|
|
|
150
160
|
def to_xml(self) -> Element:
|
|
@@ -158,10 +168,11 @@ class Dimension(object):
|
|
|
158
168
|
return ele
|
|
159
169
|
|
|
160
170
|
class Float64(ModelVariable):
|
|
161
|
-
def __init__(self, name: str, start: Optional[Any] = None, derivative: Optional[Any] = None, dimensions: List[Dimension] = [], **kwargs):
|
|
171
|
+
def __init__(self, name: str, start: Optional[Any] = None, derivative: Optional[Any] = None, dimensions: List[Dimension] = [], unit: Optional[str] = None, **kwargs):
|
|
162
172
|
super().__init__(name, **kwargs)
|
|
163
173
|
self.__attrs = {"start": start, "derivative": derivative}
|
|
164
174
|
self._type = "Float64"
|
|
175
|
+
self._unit = unit
|
|
165
176
|
if dimensions:
|
|
166
177
|
check_numpy()
|
|
167
178
|
self.__dimensions = dimensions
|
|
@@ -173,6 +184,14 @@ class Float64(ModelVariable):
|
|
|
173
184
|
@start.setter
|
|
174
185
|
def start(self, value: float):
|
|
175
186
|
self.__attrs["start"] = value
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def unit(self) -> Optional[Any]:
|
|
190
|
+
return self._unit
|
|
191
|
+
|
|
192
|
+
@unit.setter
|
|
193
|
+
def unit(self, value: str):
|
|
194
|
+
self._unit = value
|
|
176
195
|
|
|
177
196
|
@property
|
|
178
197
|
def derivative(self):
|
|
@@ -182,6 +201,7 @@ class Float64(ModelVariable):
|
|
|
182
201
|
def dimensions(self) -> List[Dimension]:
|
|
183
202
|
return self.__dimensions
|
|
184
203
|
|
|
204
|
+
|
|
185
205
|
def to_xml(self) -> Element:
|
|
186
206
|
attrib = dict()
|
|
187
207
|
for key, value in self.__attrs.items():
|
|
@@ -194,6 +214,10 @@ class Float64(ModelVariable):
|
|
|
194
214
|
else:
|
|
195
215
|
output = f"{value:.16g}"
|
|
196
216
|
attrib[key] = output
|
|
217
|
+
|
|
218
|
+
if self.unit:
|
|
219
|
+
attrib["unit"] = self.unit
|
|
220
|
+
|
|
197
221
|
self._extras = attrib
|
|
198
222
|
parent = super().to_xml()
|
|
199
223
|
|
|
@@ -306,16 +330,17 @@ class Boolean(ModelVariable):
|
|
|
306
330
|
class String(ModelVariable):
|
|
307
331
|
def __init__(self, name: str, start: Optional[Any] = None, **kwargs):
|
|
308
332
|
super().__init__(name, **kwargs)
|
|
309
|
-
self.__attrs =
|
|
333
|
+
self.__attrs = dict()
|
|
310
334
|
self._type = "String"
|
|
335
|
+
self._start = Start(start)
|
|
311
336
|
|
|
312
337
|
@property
|
|
313
338
|
def start(self) -> Optional[Any]:
|
|
314
|
-
return self.
|
|
339
|
+
return self._start.value
|
|
315
340
|
|
|
316
341
|
@start.setter
|
|
317
342
|
def start(self, value: float):
|
|
318
|
-
self.
|
|
343
|
+
self._start.value = value
|
|
319
344
|
|
|
320
345
|
def to_xml(self) -> Element:
|
|
321
346
|
attrib = dict()
|
|
@@ -324,6 +349,9 @@ class String(ModelVariable):
|
|
|
324
349
|
attrib[key] = str(value)
|
|
325
350
|
self._extras = attrib
|
|
326
351
|
parent = super().to_xml()
|
|
352
|
+
|
|
353
|
+
if self.start is not None:
|
|
354
|
+
parent.append(self._start.to_xml())
|
|
327
355
|
|
|
328
356
|
return parent
|
|
329
357
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: pythonfmu3
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
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
|
|
@@ -21,8 +21,8 @@ Classifier: Topic :: Scientific/Engineering
|
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Provides-Extra: tests
|
|
24
|
-
Requires-Dist: pytest
|
|
25
|
-
Requires-Dist: fmpy
|
|
24
|
+
Requires-Dist: pytest; extra == "tests"
|
|
25
|
+
Requires-Dist: fmpy; extra == "tests"
|
|
26
26
|
|
|
27
27
|
# PythonFMU3
|
|
28
28
|
|
|
@@ -131,16 +131,18 @@ from pythonfmu3 import Fmi3Causality, Fmi3Slave, Boolean, Int32, Float64, String
|
|
|
131
131
|
|
|
132
132
|
class PythonSlave(Fmi3Slave):
|
|
133
133
|
|
|
134
|
-
author = "John Doe"
|
|
135
|
-
description = "A simple description"
|
|
136
|
-
|
|
137
134
|
def __init__(self, **kwargs):
|
|
138
135
|
super().__init__(**kwargs)
|
|
139
136
|
|
|
137
|
+
self.author = "John Doe"
|
|
138
|
+
self.description = "A simple description"
|
|
139
|
+
|
|
140
|
+
self.time = 0.0
|
|
140
141
|
self.intOut = 1
|
|
141
142
|
self.realOut = 3.0
|
|
142
143
|
self.booleanVariable = True
|
|
143
144
|
self.stringVariable = "Hello World!"
|
|
145
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent))
|
|
144
146
|
self.register_variable(Int32("intOut", causality=Fmi3Causality.output))
|
|
145
147
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
146
148
|
self.register_variable(Boolean("booleanVariable", causality=Fmi3Causality.local))
|
|
@@ -1,53 +1,54 @@
|
|
|
1
1
|
pythonfmu3/__init__.py,sha256=PT1R0LpSInyVdXGJbkWMTQym6_SEU7CqUkG7OH5j1f0,396
|
|
2
2
|
pythonfmu3/__main__.py,sha256=gC1T8x2d02hhqshyNQGSS70SyCk-3I3t8zPqoRo2Xd8,1543
|
|
3
|
-
pythonfmu3/_version.py,sha256=
|
|
3
|
+
pythonfmu3/_version.py,sha256=yF88-8vL8keLe6gCTumymw0UoMkWkSrJnzLru4zBCLQ,23
|
|
4
4
|
pythonfmu3/builder.py,sha256=YLTeLLpiFOkoDYwnJ55GEhb3ham0xxROMjMN6bvaw5Y,8784
|
|
5
5
|
pythonfmu3/default_experiment.py,sha256=FJ0l0FpU7pT4Pox2RiA2y-Cb_e7DE2XlbWKySRYQaVk,293
|
|
6
6
|
pythonfmu3/deploy.py,sha256=eqFWVxZUkJCLvmq1-GuMNjn2j0VCGoaXPhRWrCk2Vxg,3741
|
|
7
7
|
pythonfmu3/enums.py,sha256=ZmC6vGeBTGt7HarPQ4uYV10ezPsvIxAHH1ypjNq7G3E,729
|
|
8
|
-
pythonfmu3/fmi3slave.py,sha256=
|
|
8
|
+
pythonfmu3/fmi3slave.py,sha256=7rDp_5TCQnkFLV11UF55mKEvUDXbpZPfn8sOjYCNCSw,16569
|
|
9
9
|
pythonfmu3/logmsg.py,sha256=0wn11GLwq0LLWqyC2ktLRjQFWlpj07wWliZ8rUHx1gg,359
|
|
10
|
-
pythonfmu3/osutil.py,sha256=
|
|
10
|
+
pythonfmu3/osutil.py,sha256=1qN6qudN4zCupZwc15pzp52WObkqi6jGy9a-MehNt9Q,656
|
|
11
11
|
pythonfmu3/unit.py,sha256=SjOaywb5lVQbAO_iLjc_7Ly3nYrmAvIW2_hAGt12UFU,1475
|
|
12
12
|
pythonfmu3/variable_types.py,sha256=Q0D4wlGi3i3zE5UsmGJEpNh7SV_W8mn_8p_WIkwrLQA,2881
|
|
13
|
-
pythonfmu3/variables.py,sha256=
|
|
13
|
+
pythonfmu3/variables.py,sha256=hwYT6AfaSQIa1egVJrOxnDi5NEj1bk_VkQd9-hsoDII,11905
|
|
14
14
|
pythonfmu3/pythonfmu-export/.clang-format,sha256=ApbsHWYwilCXJ1iBux4nqOjLkaqgbGLsVrc_U3hX7WI,944
|
|
15
15
|
pythonfmu3/pythonfmu-export/CMakeLists.txt,sha256=p0-4Xh_sAaOI9AVb8lsUsMxJPc6bg9b_6CF6NJt5FHE,1847
|
|
16
16
|
pythonfmu3/pythonfmu-export/build_unix.sh,sha256=I5RrF2O6y0hRG_SN4ExKN_CCFYaa2l-ngKzqmBXnHmA,73
|
|
17
17
|
pythonfmu3/pythonfmu-export/build_win.sh,sha256=UzmAcWqFmwBu6mvYne2Dx3UK8iPwTtP4HpjICsjUaJA,97
|
|
18
18
|
pythonfmu3/pythonfmu-export/src/CMakeLists.txt,sha256=RyjGhUUDhG_HEieCdt5WMfTR1snofOqigEKc83_zvfE,1104
|
|
19
|
-
pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp,sha256=
|
|
19
|
+
pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp,sha256=cgrlZa5L-bbjZeVi0pmy4JKE5UJ2GszzuyOqTdRcBRQ,4353
|
|
20
20
|
pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.cpp,sha256=EsUIevwmCzX9hsui3i8x1crSnSOG8dEK15Zm1ZIavYQ,3843
|
|
21
21
|
pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp,sha256=v4Fb8gOWk2tsUgnuQ2NBeqwc3EFQhWRxvDYHtL1C0zk,6144
|
|
22
|
-
pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp,sha256=
|
|
22
|
+
pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp,sha256=olF5Umz35uqXVMT_v9UMvwmvOJzbRbjuq1uOfnlvtUA,25539
|
|
23
23
|
pythonfmu3/pythonfmu-export/src/fmi/fmi3FunctionTypes.h,sha256=Tk1WY0nAYBGiOAQ8zVbmqRAfdQttITWsLba7udhBwG4,30050
|
|
24
24
|
pythonfmu3/pythonfmu-export/src/fmi/fmi3Functions.h,sha256=NsHuclp20wcuUInYf-91UXZbljTeu2cPXENqNEORdgU,16262
|
|
25
25
|
pythonfmu3/pythonfmu-export/src/fmi/fmi3PlatformTypes.h,sha256=ZCQLJgSbRGrMsBiQ4auaYCY4J3Hck6g3t1iZp5NEguw,4251
|
|
26
|
-
pythonfmu3/pythonfmu-export/src/pythonfmu/
|
|
27
|
-
pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.
|
|
28
|
-
pythonfmu3/pythonfmu-export/src/pythonfmu/
|
|
29
|
-
pythonfmu3/
|
|
30
|
-
pythonfmu3/resources/binaries/x86_64-
|
|
26
|
+
pythonfmu3/pythonfmu-export/src/pythonfmu/IPyState.hpp,sha256=GLiOjhKLDVEICersnqVwezwEshbxfdh_cyMxBJGFbu8,456
|
|
27
|
+
pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp,sha256=pzHSx3Cfq7Lt7dcvMR5na7tyOrTs0RsNglnrAcRc0cs,24333
|
|
28
|
+
pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp,sha256=Q8eulMAkv7YkfzCi2KSwxvYngv0I_nRbmfTJR9vT92M,4063
|
|
29
|
+
pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp,sha256=CjFEIrZu8b1jxQAIgfI_o36hyItW_SdQ5VSkImf7JNs,2977
|
|
30
|
+
pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so,sha256=w89mM0c5nt_5RT1uYWtFFrM-qd40y7vdW-cmLbuo9Cc,365120
|
|
31
|
+
pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll,sha256=jjd6J1oOhFfExxYkoUClk4uakAmUc00HbZParm7iFXY,430592
|
|
31
32
|
pythonfmu3/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
33
|
pythonfmu3/tests/conftest.py,sha256=B6XxTnBdeF_tEJ3w2DlfPKUfvlk-oULgIhQtXkgOJIc,171
|
|
33
34
|
pythonfmu3/tests/test_builder.py,sha256=StR9oNmF6dtuxxKtPFlncLQN-O7n97nvHm6EFH4Qn14,6252
|
|
34
35
|
pythonfmu3/tests/test_deploy.py,sha256=JCDo-O93-izxrP9Q4K0njBcfZ-gAXYx9aZ4plgctz7k,1783
|
|
35
36
|
pythonfmu3/tests/test_fmi3slave.py,sha256=1uwTdfiFZNmU0yzIdC30kAuu95xsZSTMA9CGk_Kdmm4,4304
|
|
36
|
-
pythonfmu3/tests/test_integration.py,sha256=
|
|
37
|
+
pythonfmu3/tests/test_integration.py,sha256=zA3iJ4rI2cKEE8mDsAmcVuwNWlJzhonV8Wxz92hzEac,10387
|
|
37
38
|
pythonfmu3/tests/test_logger.py,sha256=OcHQhxUtNBPH-0zdmheC4I9OWz1yu48W8MYDshNVkf8,5765
|
|
38
|
-
pythonfmu3/tests/test_multiple_fmus.py,sha256
|
|
39
|
-
pythonfmu3/tests/test_variables.py,sha256=
|
|
39
|
+
pythonfmu3/tests/test_multiple_fmus.py,sha256=-sthCm2YUZiRkdfvpLQNgquPBForUKQ7HGqfFBdrRas,3942
|
|
40
|
+
pythonfmu3/tests/test_variables.py,sha256=t4r2TBruf58uYwjkp3r-RcZS5TrsOaVT_mDuoiUTQtg,9598
|
|
40
41
|
pythonfmu3/tests/utils.py,sha256=WusWSUGoBPenZL-FAKQlzaHN2aYXiznZM2fwCjoldwY,301
|
|
41
42
|
pythonfmu3/tests/data/csvdemo.csv,sha256=TiTUyzHyjQNXM4eXI33oJipc7w9_PJ8aMejRmwc52Xw,256
|
|
42
43
|
pythonfmu3/tests/data/hello.txt,sha256=wFNeS-K3n_2TKRMFQ2v4iTFOSj-uwF7P_Lt98xrZ5Ro,12
|
|
43
|
-
pythonfmu3/tests/slaves/PythonSlaveWithException.py,sha256=
|
|
44
|
+
pythonfmu3/tests/slaves/PythonSlaveWithException.py,sha256=lTTMMnD5Cu09HzA8G2masKyf-ntEMfJ0a3M8btdYVLo,644
|
|
44
45
|
pythonfmu3/tests/slaves/localmodule.py,sha256=-q5Lmh2I88MG7nLTscms0n5xhHLkUDed1IuQ5wj49lo,79
|
|
45
|
-
pythonfmu3/tests/slaves/pythonslave.py,sha256=
|
|
46
|
-
pythonfmu3/tests/slaves/pythonslave_read_file.py,sha256=
|
|
47
|
-
pythonfmu3/tests/slaves/slavewithdep.py,sha256=
|
|
48
|
-
pythonfmu3-0.1.
|
|
49
|
-
pythonfmu3-0.1.
|
|
50
|
-
pythonfmu3-0.1.
|
|
51
|
-
pythonfmu3-0.1.
|
|
52
|
-
pythonfmu3-0.1.
|
|
53
|
-
pythonfmu3-0.1.
|
|
46
|
+
pythonfmu3/tests/slaves/pythonslave.py,sha256=uzrTQmfk0QBalxNTviGMZNph_YgVmcbxPqKgX5rSvXg,2172
|
|
47
|
+
pythonfmu3/tests/slaves/pythonslave_read_file.py,sha256=t2sDaZSr2veD91gdqCO9W9CN-AN3TyXABXXfV9Rt0Xs,791
|
|
48
|
+
pythonfmu3/tests/slaves/slavewithdep.py,sha256=hHDFB0dw8wkHiGzxeAVbz9O5LR6ghsFeONgRFdiK_t0,812
|
|
49
|
+
pythonfmu3-0.1.16.dist-info/LICENSE,sha256=24Cw_MPouLK-G_t8Hgjm59IS9BnPoL6SKkyuZ0xesXY,1155
|
|
50
|
+
pythonfmu3-0.1.16.dist-info/METADATA,sha256=fNhz4isDTQ1wlef8U-x5AYctr3bPGKRSqtSC8IunMhU,7442
|
|
51
|
+
pythonfmu3-0.1.16.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
52
|
+
pythonfmu3-0.1.16.dist-info/entry_points.txt,sha256=GgkWYs0DKGHLcijMEYZGkhkHOK2RVUpjGwvl1fwyDwQ,60
|
|
53
|
+
pythonfmu3-0.1.16.dist-info/top_level.txt,sha256=6ne7bh_cME1hepxCx1BG8N5QVhVAufrp7LLok6vq7nE,11
|
|
54
|
+
pythonfmu3-0.1.16.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|