pythonfmu3 0.3.2__py3-none-any.whl → 0.3.4__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/builder.py +29 -7
- pythonfmu3/fmi3slave.py +77 -30
- pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.cpp +20 -10
- pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp +20 -10
- pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp +10 -10
- pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp +40 -29
- pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp +10 -10
- 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/pythonslave_arraytypes.py +80 -0
- pythonfmu3/tests/test_docs.py +98 -0
- pythonfmu3/tests/test_integration.py +106 -0
- pythonfmu3/variables.py +68 -44
- {pythonfmu3-0.3.2.dist-info → pythonfmu3-0.3.4.dist-info}/METADATA +2 -1
- {pythonfmu3-0.3.2.dist-info → pythonfmu3-0.3.4.dist-info}/RECORD +20 -18
- {pythonfmu3-0.3.2.dist-info → pythonfmu3-0.3.4.dist-info}/WHEEL +0 -0
- {pythonfmu3-0.3.2.dist-info → pythonfmu3-0.3.4.dist-info}/entry_points.txt +0 -0
- {pythonfmu3-0.3.2.dist-info → pythonfmu3-0.3.4.dist-info}/licenses/LICENSE +0 -0
- {pythonfmu3-0.3.2.dist-info → pythonfmu3-0.3.4.dist-info}/top_level.txt +0 -0
|
@@ -285,13 +285,16 @@ void PySlaveInstance::SetFloat64(const cppfmu::FMIValueReference* vr, std::size_
|
|
|
285
285
|
});
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
void PySlaveInstance::SetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt32* values)
|
|
288
|
+
void PySlaveInstance::SetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt32* values, std::size_t nValues)
|
|
289
289
|
{
|
|
290
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
290
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
291
291
|
PyObject* vrs = PyList_New(nvr);
|
|
292
|
-
PyObject* refs = PyList_New(
|
|
292
|
+
PyObject* refs = PyList_New(nValues);
|
|
293
293
|
for (int i = 0; i < nvr; i++) {
|
|
294
294
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
for (int i = 0; i < nValues; i++) {
|
|
295
298
|
PyList_SetItem(refs, i, Py_BuildValue("i", values[i]));
|
|
296
299
|
}
|
|
297
300
|
|
|
@@ -306,13 +309,15 @@ void PySlaveInstance::SetInt32(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
306
309
|
});
|
|
307
310
|
}
|
|
308
311
|
|
|
309
|
-
void PySlaveInstance::SetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt64* values)
|
|
312
|
+
void PySlaveInstance::SetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt64* values, std::size_t nValues)
|
|
310
313
|
{
|
|
311
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
314
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
312
315
|
PyObject* vrs = PyList_New(nvr);
|
|
313
|
-
PyObject* refs = PyList_New(
|
|
316
|
+
PyObject* refs = PyList_New(nValues);
|
|
314
317
|
for (int i = 0; i < nvr; i++) {
|
|
315
318
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
319
|
+
}
|
|
320
|
+
for (int i = 0; i < nValues; i++) {
|
|
316
321
|
PyList_SetItem(refs, i, Py_BuildValue("L", values[i]));
|
|
317
322
|
}
|
|
318
323
|
|
|
@@ -327,13 +332,15 @@ void PySlaveInstance::SetInt64(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
327
332
|
});
|
|
328
333
|
}
|
|
329
334
|
|
|
330
|
-
void PySlaveInstance::SetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIUInt64* values)
|
|
335
|
+
void PySlaveInstance::SetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIUInt64* values, std::size_t nValues)
|
|
331
336
|
{
|
|
332
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
337
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
333
338
|
PyObject* vrs = PyList_New(nvr);
|
|
334
|
-
PyObject* refs = PyList_New(
|
|
339
|
+
PyObject* refs = PyList_New(nValues);
|
|
335
340
|
for (int i = 0; i < nvr; i++) {
|
|
336
341
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
342
|
+
}
|
|
343
|
+
for (int i = 0; i < nValues; i++) {
|
|
337
344
|
PyList_SetItem(refs, i, Py_BuildValue("K", values[i]));
|
|
338
345
|
}
|
|
339
346
|
|
|
@@ -348,13 +355,15 @@ void PySlaveInstance::SetUInt64(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
348
355
|
});
|
|
349
356
|
}
|
|
350
357
|
|
|
351
|
-
void PySlaveInstance::SetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIBoolean* values)
|
|
358
|
+
void PySlaveInstance::SetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIBoolean* values, std::size_t nValues)
|
|
352
359
|
{
|
|
353
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
360
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
354
361
|
PyObject* vrs = PyList_New(nvr);
|
|
355
|
-
PyObject* refs = PyList_New(
|
|
362
|
+
PyObject* refs = PyList_New(nValues);
|
|
356
363
|
for (int i = 0; i < nvr; i++) {
|
|
357
364
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
365
|
+
}
|
|
366
|
+
for (int i = 0; i < nValues; i++) {
|
|
358
367
|
PyList_SetItem(refs, i, PyBool_FromLong(values[i]));
|
|
359
368
|
}
|
|
360
369
|
|
|
@@ -369,13 +378,15 @@ void PySlaveInstance::SetBoolean(const cppfmu::FMIValueReference* vr, std::size_
|
|
|
369
378
|
});
|
|
370
379
|
}
|
|
371
380
|
|
|
372
|
-
void PySlaveInstance::SetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString const* values)
|
|
381
|
+
void PySlaveInstance::SetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString const* values, std::size_t nValues)
|
|
373
382
|
{
|
|
374
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
383
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
375
384
|
PyObject* vrs = PyList_New(nvr);
|
|
376
385
|
PyObject* refs = PyList_New(nvr);
|
|
377
386
|
for (int i = 0; i < nvr; i++) {
|
|
378
387
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
388
|
+
}
|
|
389
|
+
for (int i = 0; i < nValues; i++) {
|
|
379
390
|
PyList_SetItem(refs, i, Py_BuildValue("s", values[i]));
|
|
380
391
|
}
|
|
381
392
|
|
|
@@ -413,9 +424,9 @@ void PySlaveInstance::GetFloat64(const cppfmu::FMIValueReference* vr, std::size_
|
|
|
413
424
|
});
|
|
414
425
|
}
|
|
415
426
|
|
|
416
|
-
void PySlaveInstance::GetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt32* values) const
|
|
427
|
+
void PySlaveInstance::GetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt32* values, std::size_t nValues) const
|
|
417
428
|
{
|
|
418
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
429
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
419
430
|
PyObject* vrs = PyList_New(nvr);
|
|
420
431
|
for (int i = 0; i < nvr; i++) {
|
|
421
432
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
@@ -426,7 +437,7 @@ void PySlaveInstance::GetInt32(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
426
437
|
handle_py_exception("[getInt32] PyObject_CallMethod", gilState);
|
|
427
438
|
}
|
|
428
439
|
|
|
429
|
-
for (int i = 0; i <
|
|
440
|
+
for (int i = 0; i < nValues; i++) {
|
|
430
441
|
PyObject* value = PyList_GetItem(refs, i);
|
|
431
442
|
values[i] = static_cast<cppfmu::FMIInt32>(PyLong_AsLong(value));
|
|
432
443
|
}
|
|
@@ -435,9 +446,9 @@ void PySlaveInstance::GetInt32(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
435
446
|
});
|
|
436
447
|
}
|
|
437
448
|
|
|
438
|
-
void PySlaveInstance::GetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt64* values) const
|
|
449
|
+
void PySlaveInstance::GetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt64* values, std::size_t nValues) const
|
|
439
450
|
{
|
|
440
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
451
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
441
452
|
PyObject* vrs = PyList_New(nvr);
|
|
442
453
|
for (int i = 0; i < nvr; i++) {
|
|
443
454
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
@@ -448,7 +459,7 @@ void PySlaveInstance::GetInt64(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
448
459
|
handle_py_exception("[getInt64] PyObject_CallMethod", gilState);
|
|
449
460
|
}
|
|
450
461
|
|
|
451
|
-
for (int i = 0; i <
|
|
462
|
+
for (int i = 0; i < nValues; i++) {
|
|
452
463
|
PyObject* value = PyList_GetItem(refs, i);
|
|
453
464
|
values[i] = static_cast<cppfmu::FMIInt64>(PyLong_AsLongLong(value));
|
|
454
465
|
}
|
|
@@ -457,9 +468,9 @@ void PySlaveInstance::GetInt64(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
457
468
|
});
|
|
458
469
|
}
|
|
459
470
|
|
|
460
|
-
void PySlaveInstance::GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIUInt64* values) const
|
|
471
|
+
void PySlaveInstance::GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIUInt64* values, std::size_t nValues) const
|
|
461
472
|
{
|
|
462
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
473
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
463
474
|
PyObject* vrs = PyList_New(nvr);
|
|
464
475
|
for (int i = 0; i < nvr; i++) {
|
|
465
476
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
@@ -470,7 +481,7 @@ void PySlaveInstance::GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
470
481
|
handle_py_exception("[getUInt64] PyObject_CallMethod", gilState);
|
|
471
482
|
}
|
|
472
483
|
|
|
473
|
-
for (int i = 0; i <
|
|
484
|
+
for (int i = 0; i < nValues; i++) {
|
|
474
485
|
PyObject* item = PyList_GetItem(refs, i);
|
|
475
486
|
PyObject* value = PyObject_GetAttrString(item, "value");
|
|
476
487
|
if (value && PyLong_Check(value))
|
|
@@ -484,9 +495,9 @@ void PySlaveInstance::GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
484
495
|
});
|
|
485
496
|
}
|
|
486
497
|
|
|
487
|
-
void PySlaveInstance::GetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIBoolean* values) const
|
|
498
|
+
void PySlaveInstance::GetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIBoolean* values, std::size_t nValues) const
|
|
488
499
|
{
|
|
489
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
500
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
490
501
|
PyObject* vrs = PyList_New(nvr);
|
|
491
502
|
for (int i = 0; i < nvr; i++) {
|
|
492
503
|
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
|
|
@@ -497,7 +508,7 @@ void PySlaveInstance::GetBoolean(const cppfmu::FMIValueReference* vr, std::size_
|
|
|
497
508
|
handle_py_exception("[getBoolean] PyObject_CallMethod", gilState);
|
|
498
509
|
}
|
|
499
510
|
|
|
500
|
-
for (int i = 0; i <
|
|
511
|
+
for (int i = 0; i < nValues; i++) {
|
|
501
512
|
PyObject* value = PyList_GetItem(refs, i);
|
|
502
513
|
values[i] = PyObject_IsTrue(value);
|
|
503
514
|
}
|
|
@@ -506,9 +517,9 @@ void PySlaveInstance::GetBoolean(const cppfmu::FMIValueReference* vr, std::size_
|
|
|
506
517
|
});
|
|
507
518
|
}
|
|
508
519
|
|
|
509
|
-
void PySlaveInstance::GetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString* values) const
|
|
520
|
+
void PySlaveInstance::GetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString* values, std::size_t nValues) const
|
|
510
521
|
{
|
|
511
|
-
py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
|
|
522
|
+
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
|
|
512
523
|
clearStrBuffer();
|
|
513
524
|
PyObject* vrs = PyList_New(nvr);
|
|
514
525
|
for (int i = 0; i < nvr; i++) {
|
|
@@ -520,7 +531,7 @@ void PySlaveInstance::GetString(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
520
531
|
handle_py_exception("[getString] PyObject_CallMethod", gilState);
|
|
521
532
|
}
|
|
522
533
|
|
|
523
|
-
for (int i = 0; i <
|
|
534
|
+
for (int i = 0; i < nValues; i++) {
|
|
524
535
|
PyObject* value = PyUnicode_AsEncodedString(PyList_GetItem(refs, i), "utf-8", nullptr);
|
|
525
536
|
values[i] = PyBytes_AsString(value);
|
|
526
537
|
strBuffer.emplace_back(value);
|
|
@@ -34,18 +34,18 @@ public:
|
|
|
34
34
|
cppfmu::FMIFloat64& endOfStep) override;
|
|
35
35
|
|
|
36
36
|
void SetFloat64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIFloat64* value, std::size_t nValues) override;
|
|
37
|
-
void SetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt32* value) override;
|
|
38
|
-
void SetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt64* value) override;
|
|
39
|
-
void SetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIUInt64* value) override;
|
|
40
|
-
void SetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIBoolean* value) override;
|
|
41
|
-
void SetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString const* value) override;
|
|
37
|
+
void SetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt32* value, std::size_t nValues) override;
|
|
38
|
+
void SetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt64* value, std::size_t nValues) override;
|
|
39
|
+
void SetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIUInt64* value, std::size_t nValues) override;
|
|
40
|
+
void SetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIBoolean* value, std::size_t nValues) override;
|
|
41
|
+
void SetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString const* value, std::size_t nValues) override;
|
|
42
42
|
|
|
43
43
|
void GetFloat64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIFloat64* value, std::size_t nValues) const override;
|
|
44
|
-
void GetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt32* value) const override;
|
|
45
|
-
void GetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt64* value) const override;
|
|
46
|
-
void GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIUInt64* value) const override;
|
|
47
|
-
void GetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIBoolean* value) const override;
|
|
48
|
-
void GetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString* value) const override;
|
|
44
|
+
void GetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt32* value, std::size_t nValues) const override;
|
|
45
|
+
void GetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt64* value, std::size_t nValues) const override;
|
|
46
|
+
void GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIUInt64* value, std::size_t nValues) const override;
|
|
47
|
+
void GetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIBoolean* value, std::size_t nValues) const override;
|
|
48
|
+
void GetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString* value, std::size_t nValues) const override;
|
|
49
49
|
|
|
50
50
|
void GetFMUstate(fmi3FMUState& State) override;
|
|
51
51
|
void SetFMUstate(const fmi3FMUState& State) override;
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
from pythonfmu3 import Fmi3Causality, Fmi3Variability, Dimension, Fmi3Slave, Fmi3Status, Float64, Int32, Int64, UInt64, String, Boolean
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
SIZE = 10
|
|
7
|
+
|
|
8
|
+
TYPES = [UInt64, Float64, Boolean, Int32, Int64]
|
|
9
|
+
CAUSALITY = [Fmi3Causality.output, Fmi3Causality.input]
|
|
10
|
+
|
|
11
|
+
TYPE_MAP = {
|
|
12
|
+
UInt64: np.uint64,
|
|
13
|
+
Float64: np.float64,
|
|
14
|
+
Int32: np.int32,
|
|
15
|
+
Int64: np.int64,
|
|
16
|
+
Boolean: bool
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def var_names(var_type, causality):
|
|
20
|
+
return f"{var_type.__name__.lower()}_{causality.name.lower()}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def init_var(var_type, causality, array=True):
|
|
24
|
+
if array:
|
|
25
|
+
return np.zeros(SIZE, dtype=TYPE_MAP[var_type])
|
|
26
|
+
else:
|
|
27
|
+
return TYPE_MAP[var_type]()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_vars(self):
|
|
31
|
+
dimensions = [Dimension(start=str(SIZE))]
|
|
32
|
+
for var_type in TYPES:
|
|
33
|
+
for causality in CAUSALITY:
|
|
34
|
+
name = var_names(var_type, causality)
|
|
35
|
+
if var_type == Float64:
|
|
36
|
+
var = var_type(name, causality=causality, variability=Fmi3Variability.continuous, dimensions=dimensions)
|
|
37
|
+
else:
|
|
38
|
+
var = var_type(name, causality=causality, variability=Fmi3Variability.discrete, dimensions=dimensions)
|
|
39
|
+
setattr(self, name, init_var(var_type, causality))
|
|
40
|
+
self.register_variable(var)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def generate_data(self):
|
|
44
|
+
for var_type in TYPES:
|
|
45
|
+
causality = Fmi3Causality.output
|
|
46
|
+
name = var_names(var_type, causality)
|
|
47
|
+
var = getattr(self, name)
|
|
48
|
+
if var_type == Boolean:
|
|
49
|
+
data = [False, True, False, True, True, False, False, True, False, True]
|
|
50
|
+
setattr(self, name, np.array(data).astype(bool))
|
|
51
|
+
elif var_type == Int32 or var_type == Int64 or var_type == UInt64:
|
|
52
|
+
data = [1,2,3,4,5,6,7,8,9,10]
|
|
53
|
+
setattr(self, name, np.array(data).astype(TYPE_MAP[var_type]))
|
|
54
|
+
else:
|
|
55
|
+
data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
|
|
56
|
+
setattr(self, name, np.array(data).astype(TYPE_MAP[var_type]))
|
|
57
|
+
|
|
58
|
+
class ArrayTypes(Fmi3Slave):
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def __init__(self, **kwargs):
|
|
62
|
+
super().__init__(**kwargs)
|
|
63
|
+
|
|
64
|
+
self.author = "Tester"
|
|
65
|
+
self.description = "FMU with all basic types as arrays"
|
|
66
|
+
|
|
67
|
+
self.time = 0.0
|
|
68
|
+
|
|
69
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
70
|
+
|
|
71
|
+
create_vars(self)
|
|
72
|
+
|
|
73
|
+
generate_data(self)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def do_step(self, current_time: float, step_size: float) -> Fmi3Status:
|
|
77
|
+
generate_data(self)
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import pathlib
|
|
3
|
+
import re
|
|
4
|
+
import tempfile
|
|
5
|
+
|
|
6
|
+
def extract_python_code_from_md(md_file):
|
|
7
|
+
"""Extract Python code blocks from markdown, excluding skip blocks"""
|
|
8
|
+
content = pathlib.Path(md_file).read_text()
|
|
9
|
+
|
|
10
|
+
# Find all python code blocks that aren't marked as skip
|
|
11
|
+
python_blocks = []
|
|
12
|
+
|
|
13
|
+
# Pattern to match python blocks but not python skip
|
|
14
|
+
pattern = r'```python(?!\s+skip)(?:[^\n]*)\n(.*?)\n```'
|
|
15
|
+
matches = re.findall(pattern, content, re.DOTALL)
|
|
16
|
+
|
|
17
|
+
for match in matches:
|
|
18
|
+
# Skip blocks that are wrapped in skip-test comments
|
|
19
|
+
if '<!-- skip-test -->' not in content.split(match)[0][-100:]:
|
|
20
|
+
python_blocks.append(match)
|
|
21
|
+
|
|
22
|
+
return python_blocks
|
|
23
|
+
|
|
24
|
+
@pytest.mark.doc
|
|
25
|
+
def test_readme():
|
|
26
|
+
test_dir = pathlib.Path(__file__).parent
|
|
27
|
+
fpath = test_dir.parent.parent / "README.md"
|
|
28
|
+
|
|
29
|
+
# extract Python code
|
|
30
|
+
code_blocks = extract_python_code_from_md(fpath)
|
|
31
|
+
|
|
32
|
+
namespace = {}
|
|
33
|
+
exec("from pythonfmu3 import *", namespace)
|
|
34
|
+
|
|
35
|
+
for i, code in enumerate(code_blocks):
|
|
36
|
+
try:
|
|
37
|
+
exec(code, namespace)
|
|
38
|
+
print(f"✅ Code block {i+1} executed successfully")
|
|
39
|
+
except Exception as e:
|
|
40
|
+
pytest.fail(f"Code block {i+1} failed: {e}\nCode:\n{code}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.mark.doc
|
|
46
|
+
def test_cosimulation_examples():
|
|
47
|
+
"""Test that all Python examples in usage.md actually work"""
|
|
48
|
+
test_dir = pathlib.Path(__file__).parent
|
|
49
|
+
usage_file = test_dir.parent.parent / "docs" / "usage.md"
|
|
50
|
+
|
|
51
|
+
# Extract Python code
|
|
52
|
+
code_blocks = extract_python_code_from_md(usage_file)
|
|
53
|
+
|
|
54
|
+
# Create a test namespace
|
|
55
|
+
namespace = {}
|
|
56
|
+
exec("from pythonfmu3 import *", namespace)
|
|
57
|
+
|
|
58
|
+
# Execute each code block
|
|
59
|
+
for i, code in enumerate(code_blocks):
|
|
60
|
+
try:
|
|
61
|
+
exec(code, namespace)
|
|
62
|
+
print(f"✅ Code block {i+1} executed successfully")
|
|
63
|
+
except Exception as e:
|
|
64
|
+
pytest.fail(f"Code block {i+1} failed: {e}\nCode:\n{code}")
|
|
65
|
+
|
|
66
|
+
assert 'LinearTransform' in namespace, "LinearTransform class not found"
|
|
67
|
+
assert 'PIDController' in namespace, "PIDController class not found"
|
|
68
|
+
|
|
69
|
+
# Test instantiation
|
|
70
|
+
lt = namespace['LinearTransform'](instance_name="test")
|
|
71
|
+
pc = namespace['PIDController'](instance_name="test")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@pytest.mark.doc
|
|
75
|
+
def test_model_exchange_examples():
|
|
76
|
+
"""Test Model Exchange examples"""
|
|
77
|
+
test_dir = pathlib.Path(__file__).parent
|
|
78
|
+
mx_file = test_dir.parent.parent / "docs" / "usageMX.md"
|
|
79
|
+
|
|
80
|
+
code_blocks = extract_python_code_from_md(mx_file)
|
|
81
|
+
|
|
82
|
+
namespace = {}
|
|
83
|
+
exec("from pythonfmu3 import *", namespace)
|
|
84
|
+
|
|
85
|
+
for i, code in enumerate(code_blocks):
|
|
86
|
+
try:
|
|
87
|
+
exec(code, namespace)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
pytest.fail(f"MX Code block {i+1} failed: {e}")
|
|
90
|
+
|
|
91
|
+
# Test that classes were actually created
|
|
92
|
+
assert 'VanDerPol' in namespace, "VanDerPol class not found"
|
|
93
|
+
assert 'BouncingBall' in namespace, "BouncingBall class not found"
|
|
94
|
+
|
|
95
|
+
# Test instantiation
|
|
96
|
+
vdp = namespace['VanDerPol'](instance_name="test")
|
|
97
|
+
|
|
98
|
+
bb = namespace['BouncingBall'](instance_name="test")
|
|
@@ -280,6 +280,57 @@ def test_integration_get(tmp_path):
|
|
|
280
280
|
model.terminate()
|
|
281
281
|
model.freeInstance()
|
|
282
282
|
|
|
283
|
+
@pytest.mark.integration
|
|
284
|
+
def test_integration_get_array(tmp_path):
|
|
285
|
+
script_file = Path(__file__).parent / "slaves/pythonslave_arraytypes.py"
|
|
286
|
+
fmu = FmuBuilder.build_FMU(script_file, dest=tmp_path, needsExecutionTool="false")
|
|
287
|
+
assert fmu.exists()
|
|
288
|
+
|
|
289
|
+
md = fmpy.read_model_description(fmu)
|
|
290
|
+
unzip_dir = fmpy.extract(fmu)
|
|
291
|
+
|
|
292
|
+
model = fmpy.fmi3.FMU3Slave(
|
|
293
|
+
guid=md.guid,
|
|
294
|
+
unzipDirectory=unzip_dir,
|
|
295
|
+
modelIdentifier=md.coSimulation.modelIdentifier,
|
|
296
|
+
instanceName='instance1')
|
|
297
|
+
|
|
298
|
+
model.instantiate()
|
|
299
|
+
model.enterInitializationMode()
|
|
300
|
+
model.exitInitializationMode()
|
|
301
|
+
|
|
302
|
+
to_test = {
|
|
303
|
+
"int32_output": [1,2,3,4,5,6,7,8,9,10],
|
|
304
|
+
"int64_output": [1,2,3,4,5,6,7,8,9,10],
|
|
305
|
+
"uint64_output": [1,2,3,4,5,6,7,8,9,10],
|
|
306
|
+
"float64_output": [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],
|
|
307
|
+
"boolean_output": [False, True, False, True, True, False, False, True, False, True]
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
model_value = None
|
|
311
|
+
variables = mapped(md)
|
|
312
|
+
for key, value in to_test.items():
|
|
313
|
+
var = variables[key]
|
|
314
|
+
vrs = [var.valueReference]
|
|
315
|
+
if var.type == "Int32":
|
|
316
|
+
model_values = model.getInt32(vrs, nValues=10)
|
|
317
|
+
elif var.type == "Int64":
|
|
318
|
+
model_values = model.getInt64(vrs, nValues=10)
|
|
319
|
+
elif var.type == "UInt64":
|
|
320
|
+
model_values = model.getUInt64(vrs, nValues=10)
|
|
321
|
+
elif var.type == "Float64":
|
|
322
|
+
model_values = model.getFloat64(vrs, nValues=10)
|
|
323
|
+
elif var.type == "Boolean":
|
|
324
|
+
model_values = model.getBoolean(vrs, nValues=10)
|
|
325
|
+
else:
|
|
326
|
+
pytest.xfail("Unsupported type")
|
|
327
|
+
|
|
328
|
+
assert len(model_values) == len(value)
|
|
329
|
+
assert model_values == value
|
|
330
|
+
|
|
331
|
+
model.terminate()
|
|
332
|
+
model.freeInstance()
|
|
333
|
+
|
|
283
334
|
|
|
284
335
|
@pytest.mark.integration
|
|
285
336
|
def test_integration_read_from_file(tmp_path):
|
|
@@ -367,6 +418,61 @@ def test_integration_set(tmp_path):
|
|
|
367
418
|
model.terminate()
|
|
368
419
|
model.freeInstance()
|
|
369
420
|
|
|
421
|
+
@pytest.mark.integration
|
|
422
|
+
def test_integration_set_array(tmp_path):
|
|
423
|
+
script_file = Path(__file__).parent / "slaves/pythonslave_arraytypes.py"
|
|
424
|
+
fmu = FmuBuilder.build_FMU(script_file, dest=tmp_path, needsExecutionTool="false")
|
|
425
|
+
assert fmu.exists()
|
|
426
|
+
|
|
427
|
+
md = fmpy.read_model_description(fmu)
|
|
428
|
+
unzip_dir = fmpy.extract(fmu)
|
|
429
|
+
|
|
430
|
+
model = fmpy.fmi3.FMU3Slave(
|
|
431
|
+
guid=md.guid,
|
|
432
|
+
unzipDirectory=unzip_dir,
|
|
433
|
+
modelIdentifier=md.coSimulation.modelIdentifier,
|
|
434
|
+
instanceName='instance1')
|
|
435
|
+
|
|
436
|
+
model.instantiate()
|
|
437
|
+
model.enterInitializationMode()
|
|
438
|
+
model.exitInitializationMode()
|
|
439
|
+
|
|
440
|
+
to_test = {
|
|
441
|
+
"int32_input": [1,2,3,4,5,6,7,8,9,10],
|
|
442
|
+
"int64_input": [1,2,3,4,5,6,7,8,9,10],
|
|
443
|
+
"uint64_input": [1,2,3,4,5,6,7,8,9,10],
|
|
444
|
+
"float64_input": [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],
|
|
445
|
+
"boolean_input": [False, True, False, True, True, False, False, True, False, True]
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
model_value = None
|
|
449
|
+
variables = mapped(md)
|
|
450
|
+
for key, value in to_test.items():
|
|
451
|
+
var = variables[key]
|
|
452
|
+
vrs = [var.valueReference]
|
|
453
|
+
if var.type == "Int32":
|
|
454
|
+
model.setInt32(vrs, value)
|
|
455
|
+
model_value = model.getInt32(vrs, nValues=10)
|
|
456
|
+
elif var.type == "Int64":
|
|
457
|
+
model.setInt64(vrs, value)
|
|
458
|
+
model_value = model.getInt64(vrs, nValues=10)
|
|
459
|
+
elif var.type == "UInt64":
|
|
460
|
+
model.setUInt64(vrs, value)
|
|
461
|
+
model_value = model.getUInt64(vrs, nValues=10)
|
|
462
|
+
elif var.type == "Float64":
|
|
463
|
+
model.setFloat64(vrs, value)
|
|
464
|
+
model_value = model.getFloat64(vrs,nValues=10)
|
|
465
|
+
elif var.type == "Boolean":
|
|
466
|
+
model.setBoolean(vrs, value)
|
|
467
|
+
model_value = model.getBoolean(vrs, nValues=10)
|
|
468
|
+
else:
|
|
469
|
+
pytest.xfail("Unsupported type")
|
|
470
|
+
|
|
471
|
+
assert list(model_value) == value
|
|
472
|
+
|
|
473
|
+
model.terminate()
|
|
474
|
+
model.freeInstance()
|
|
475
|
+
|
|
370
476
|
|
|
371
477
|
@pytest.mark.integration
|
|
372
478
|
def test_simple_integration_fmpy(tmp_path):
|