Skip to content

Commit 684d608

Browse files
fix array usage
1 parent d4d6888 commit 684d608

File tree

5 files changed

+223
-14
lines changed

5 files changed

+223
-14
lines changed
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ def generate_random_data(self):
5252
else:
5353
setattr(self, name, np.random.rand(SIZE).astype(TYPE_MAP[var_type]))
5454

55-
class AllTypes(Fmi3Slave):
56-
55+
class ArrayTypes(Fmi3Slave):
5756

5857
def __init__(self, **kwargs):
5958
super().__init__(**kwargs)

pythonfmu3/fmi3slave.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -366,30 +366,48 @@ def get_string(self, vrs: List[int]) -> List[str]:
366366
return refs
367367

368368
def set_int32(self, vrs: List[int], values: List[int]):
369-
for vr, value in zip(vrs, values):
369+
offset = 0
370+
for vr in vrs:
370371
var = self.vars[vr]
371372
if isinstance(var, Int32):
372-
var.setter(value)
373+
size = var.size(self.vars)
374+
if size > 1:
375+
var.setter(values[offset:offset+size])
376+
else:
377+
var.setter(values[offset])
378+
offset += size
373379
else:
374380
raise TypeError(
375381
f"Variable with valueReference={vr} is not of type Integer!"
376382
)
377383

378384
def set_int64(self, vrs: List[int], values: List[int]):
379-
for vr, value in zip(vrs, values):
385+
offset = 0
386+
for vr in vrs:
380387
var = self.vars[vr]
381388
if isinstance(var, (Enumeration, Int64)):
382-
var.setter(value)
389+
size = var.size(self.vars)
390+
if size > 1:
391+
var.setter(values[offset:offset+size])
392+
else:
393+
var.setter(values[offset])
394+
offset += size
383395
else:
384396
raise TypeError(
385397
f"Variable with valueReference={vr} is not of type Integer!"
386398
)
387399

388400
def set_uint64(self, vrs: List[int], values: List[int]):
389-
for vr, value in zip(vrs, values):
401+
offset = 0
402+
for vr in vrs:
390403
var = self.vars[vr]
391404
if isinstance(var, UInt64):
392-
var.setter(value)
405+
size = var.size(self.vars)
406+
if size > 1:
407+
var.setter(values[offset:offset+size])
408+
else:
409+
var.setter(values[offset])
410+
offset += size
393411
else:
394412
raise TypeError(
395413
f"Variable with valueReference={vr} is not of type UInt64!"
@@ -412,10 +430,16 @@ def set_float64(self, vrs: List[int], values: List[float]):
412430
)
413431

414432
def set_boolean(self, vrs: List[int], values: List[bool]):
415-
for vr, value in zip(vrs, values):
433+
offset = 0
434+
for vr in vrs:
416435
var = self.vars[vr]
417436
if isinstance(var, Boolean):
418-
var.setter(value)
437+
size = var.size(self.vars)
438+
if size > 1:
439+
var.setter(values[offset:offset+size])
440+
else:
441+
var.setter(values[offset])
442+
offset += size
419443
else:
420444
raise TypeError(
421445
f"Variable with valueReference={vr} is not of type Boolean!"

pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ void PySlaveInstance::SetInt32(const cppfmu::FMIValueReference* vr, std::size_t
289289
{
290290
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
291291
PyObject* vrs = PyList_New(nvr);
292-
PyObject* refs = PyList_New(nvr);
292+
PyObject* refs = PyList_New(nValues);
293293
for (int i = 0; i < nvr; i++) {
294294
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
295295
}
@@ -313,7 +313,7 @@ void PySlaveInstance::SetInt64(const cppfmu::FMIValueReference* vr, std::size_t
313313
{
314314
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
315315
PyObject* vrs = PyList_New(nvr);
316-
PyObject* refs = PyList_New(nvr);
316+
PyObject* refs = PyList_New(nValues);
317317
for (int i = 0; i < nvr; i++) {
318318
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
319319
}
@@ -336,7 +336,7 @@ void PySlaveInstance::SetUInt64(const cppfmu::FMIValueReference* vr, std::size_t
336336
{
337337
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
338338
PyObject* vrs = PyList_New(nvr);
339-
PyObject* refs = PyList_New(nvr);
339+
PyObject* refs = PyList_New(nValues);
340340
for (int i = 0; i < nvr; i++) {
341341
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
342342
}
@@ -359,7 +359,7 @@ void PySlaveInstance::SetBoolean(const cppfmu::FMIValueReference* vr, std::size_
359359
{
360360
py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
361361
PyObject* vrs = PyList_New(nvr);
362-
PyObject* refs = PyList_New(nvr);
362+
PyObject* refs = PyList_New(nValues);
363363
for (int i = 0; i < nvr; i++) {
364364
PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
365365
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+

pythonfmu3/tests/test_integration.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,57 @@ def test_integration_get(tmp_path):
280280
model.terminate()
281281
model.freeInstance()
282282

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+
283334

284335
@pytest.mark.integration
285336
def test_integration_read_from_file(tmp_path):
@@ -367,6 +418,61 @@ def test_integration_set(tmp_path):
367418
model.terminate()
368419
model.freeInstance()
369420

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+
370476

371477
@pytest.mark.integration
372478
def test_simple_integration_fmpy(tmp_path):

0 commit comments

Comments
 (0)