Skip to content

Commit 1bc4610

Browse files
feat: added functionality to use pathlib.Path object in result_file_name option and when loading results
1 parent 09ce2f6 commit 1bc4610

4 files changed

Lines changed: 42 additions & 8 deletions

File tree

‎CHANGELOG‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* Paths when loading FMUs
99
* `log_file_name` when loading FMUs
1010
* `pyfmi.common.log.parse_xml_log` & `extract_xml_log`, including `fmu.extract_xml_log()`.
11+
* `result_file_name` as simulation option and manual use of `ResultReader` classes
1112

1213
--- PyFMI-2.20.1 ---
1314
* Resolved issue where caching in result handling was too persistent and could prevent automatic garbage collection.

‎src/common/io.py‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from shutil import disk_usage
2929
import abc
3030
import warnings
31+
from pathlib import Path
3132

3233
import numpy as np
3334
import scipy
@@ -388,7 +389,7 @@ def __init__(self, filename, delimiter=";"):
388389
Default: ";""
389390
"""
390391

391-
if isinstance(filename, str):
392+
if isinstance(filename, (str, Path)):
392393
try:
393394
fid = codecs.open(filename,'r','utf-8')
394395
except FileNotFoundError as e:
@@ -997,7 +998,8 @@ def __init__(self,fname):
997998
Name of file or stream object which the result is written to.
998999
If fname is a stream, it needs to support 'readline' and 'seek'.
9991000
"""
1000-
if isinstance(fname, str):
1001+
if isinstance(fname, (str, Path)):
1002+
fname = os.path.abspath(fname)
10011003
try:
10021004
fid = codecs.open(fname,'r','utf-8')
10031005
except FileNotFoundError as e:
@@ -1302,8 +1304,8 @@ def __init__(self, fname, delayed_trajectory_loading = True, allow_file_updates=
13021304
Default: False
13031305
"""
13041306

1305-
if isinstance(fname, str):
1306-
self._fname = fname
1307+
if isinstance(fname, (str, Path)):
1308+
self._fname = os.path.abspath(fname)
13071309
self._is_stream = False
13081310
elif hasattr(fname, "name") and os.path.isfile(fname.name):
13091311
self._fname = fname.name
@@ -2134,7 +2136,7 @@ def simulation_start(self):
21342136
cont_alias_bool.append(-1 if var.alias == fmi.FMI_NEGATED_ALIAS else 1)
21352137

21362138
# Open file
2137-
if isinstance(self.file_name, str):
2139+
if isinstance(self.file_name, (str, Path)):
21382140
f = codecs.open(self.file_name,'w','utf-8')
21392141
self.file_open = True
21402142
else:
@@ -2254,7 +2256,7 @@ def file_name(self):
22542256
@cached_property
22552257
def _is_stream(self):
22562258
file = self.file_name
2257-
if isinstance(file, str):
2259+
if isinstance(file, (str, Path)):
22582260
return False
22592261
else:
22602262
if not (hasattr(file, 'write') and hasattr(file, 'seek')):
@@ -2899,7 +2901,7 @@ def simulation_start(self, diagnostics_params={}, diagnostics_vars={}):
28992901

29002902
# Open file
29012903
file_name = self.file_name
2902-
if isinstance(self.file_name, str):
2904+
if isinstance(self.file_name, (str, Path)):
29032905
self._file = open(file_name,'wb')
29042906
else:
29052907
if not (hasattr(self.file_name, 'write') and hasattr(self.file_name, 'seek') and (hasattr(self.file_name, 'tell'))):
@@ -3177,7 +3179,7 @@ def __init__(self, fname):
31773179
which the result is written to.
31783180
"""
31793181

3180-
if isinstance(fname, str):
3182+
if isinstance(fname, (str, Path)):
31813183
self._fname = fname
31823184
elif hasattr(fname, "name") and os.path.isfile(fname.name):
31833185
self._fname = fname.name

‎tests/test_fmi.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ def test_extract_xml_log_as_path(self, load_with_path_object):
147147
fmu = load_with_path_object.loader(str(load_with_path_object.path))
148148
fmu.extract_xml_log(Path("xml_log.xml"))
149149

150+
def test_result_file_name_as_path(self, load_with_path_object):
151+
fmu = load_with_path_object.loader(str(load_with_path_object.path))
152+
opts = fmu.simulate_options()
153+
opts["ncp"] = 1 # speed
154+
if opts.get("solver"): # work-around for an FMI1 bug with absolute tolerance adjustments
155+
opts["solver"] = "ExplicitEuler"
156+
opts["result_file_name"] = Path("res.mat")
157+
fmu.simulate(options = opts)
158+
150159
@pytest.mark.parametrize("fmu_loader, fmu_path",
151160
[
152161
(FMUModelME1, PATH_TO_FMU_EXAMPLES/ 'ME1.0' / 'bouncingBall.fmu'),

‎tests/test_io.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2739,3 +2739,25 @@ def test_given_no_start_simulation_when_get_result_then_no_result_error(result_h
27392739

27402740
with pytest.raises(NoResultError):
27412741
result_handler.get_result()
2742+
2743+
@pytest.mark.parametrize("result_reader_class, result_handling, suffix", [
2744+
(ResultDymolaBinary, "binary", ".mat"),
2745+
(ResultReaderBinaryMat, "binary", ".mat"),
2746+
(lambda x: ResultCSVTextual(x, delimiter=","), "csv", ".csv"),
2747+
(ResultDymolaTextual, "file", ".txt")
2748+
])
2749+
def test_load_results_files_via_path_object(
2750+
tmp_path,
2751+
result_reader_class,
2752+
result_handling,
2753+
suffix
2754+
):
2755+
# Functionality itself is FMI agnostic; FMI2 supports most types
2756+
fmu = load_fmu(FMI2_REF_FMU_PATH / "VanDerPol.fmu")
2757+
opts = fmu.simulate_options()
2758+
opts["ncp"] = 0 # speed
2759+
res_file = Path(tmp_path) / f"res_file{suffix}"
2760+
opts["result_file_name"] = res_file
2761+
opts["result_handling"] = result_handling
2762+
fmu.simulate(options = opts)
2763+
result_reader_class(res_file)

0 commit comments

Comments
 (0)